Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions src/__tests__/__snapshots__/server.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,24 @@ exports[`runServer should allow server to be stopped, http stop server: diagnost
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -77,15 +86,24 @@ exports[`runServer should allow server to be stopped, stdio stop server: diagnos
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -133,15 +151,24 @@ exports[`runServer should attempt to run server, create transport, connect, and
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -197,15 +224,24 @@ exports[`runServer should attempt to run server, disable SIGINT handler: diagnos
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -256,15 +292,24 @@ exports[`runServer should attempt to run server, enable SIGINT handler explicitl
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -320,15 +365,24 @@ exports[`runServer should attempt to run server, register a tool: diagnostics 1`
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -395,15 +449,24 @@ exports[`runServer should attempt to run server, register multiple tools: diagno
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -480,15 +543,24 @@ exports[`runServer should attempt to run server, use custom options: diagnostics
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -544,15 +616,24 @@ exports[`runServer should attempt to run server, use default tools, http: diagno
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down Expand Up @@ -621,15 +702,24 @@ exports[`runServer should attempt to run server, use default tools, stdio: diagn
[
"Registered resource: patternfly-components-index",
],
[
"Registered resource: patternfly-components-index-meta",
],
[
"Registered resource: patternfly-docs-index",
],
[
"Registered resource: patternfly-docs-index-meta",
],
[
"Registered resource: patternfly-docs-template",
],
[
"Registered resource: patternfly-schemas-index",
],
[
"Registered resource: patternfly-schemas-index-meta",
],
[
"Registered resource: patternfly-schemas-template",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ exports[`searchPatternFlyDocsTool, callback should have a specific markdown form

exports[`searchPatternFlyDocsTool, callback should parse parameters, default: search 1`] = `"# Search results for PatternFly version "v6" and "Button". Showing 2 exact matches."`;

exports[`searchPatternFlyDocsTool, callback should parse parameters, with "*" searchQuery all: search 1`] = `"# Search results for PatternFly version "v6" and "all" resources. Only showing the first 10 results. There are 806 potential match variations. Try searching with a more specific query."`;
exports[`searchPatternFlyDocsTool, callback should parse parameters, with "*" searchQuery all: search 1`] = `"# Search results for PatternFly version "v6" and "all" resources. Only showing the first 10 results. There are 805 potential match variations. Try searching with a more specific query."`;

exports[`searchPatternFlyDocsTool, callback should parse parameters, with "all" searchQuery all: search 1`] = `"# Search results for PatternFly version "v6" and "all" resources. Only showing the first 10 results. There are 806 potential match variations. Try searching with a more specific query."`;
exports[`searchPatternFlyDocsTool, callback should parse parameters, with "all" searchQuery all: search 1`] = `"# Search results for PatternFly version "v6" and "all" resources. Only showing the first 10 results. There are 805 potential match variations. Try searching with a more specific query."`;

exports[`searchPatternFlyDocsTool, callback should parse parameters, with explicit valid version: search 1`] = `"# Search results for PatternFly version "v6" and "Button". Showing 2 exact matches."`;

Expand Down
43 changes: 43 additions & 0 deletions src/__tests__/docsDataIntegrity.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { distance } from 'fastest-levenshtein';
import docsJson from '../docs.json';

describe('Documentation Data Integrity', () => {
const allEntries = Object.values(docsJson.docs).flat();
const uniqueCategories = [...new Set(allEntries.map(entry => entry.category).filter(Boolean))];
const uniqueSections = [...new Set(allEntries.map(entry => (entry as any).section).filter(Boolean))];

const checkSimilarity = (list: string[], type: string) => {
for (let i = 0; i < list.length; i++) {
for (let j = i + 1; j < list.length; j++) {
const str1 = list[i]!.toLowerCase();
const str2 = list[j]!.toLowerCase();

// Check for near-duplicates using Levenshtein distance
const dist = distance(str1, str2);

if (dist <= 2) {
throw new Error(`Potential duplicate ${type} found: "${list[i]}" and "${list[j]}" (distance: ${dist})`);
}

// Check if one is a substring of another (e.g., "component" and "components")
if (str1.includes(str2) || str2.includes(str1)) {
throw new Error(`Potential overlapping ${type} found: "${list[i]}" and "${list[j]}"`);
}
}
}
};

test('categories should be unique and distinct', () => {
expect(() => checkSimilarity(uniqueCategories as string[], 'category')).not.toThrow();
});

test('sections should be unique and distinct', () => {
expect(() => checkSimilarity(uniqueSections as string[], 'section')).not.toThrow();
});

test('no section should be named "getting-started"', () => {
const hasGettingStarted = allEntries.some(entry => (entry as any).section === 'getting-started');

expect(hasGettingStarted).toBe(false);
});
});
72 changes: 72 additions & 0 deletions src/__tests__/server.resourceMeta.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerResourceMeta } from '../server.resourceMeta';
import { type McpResource } from '../server';
import { getOptions, initializeSession } from '../options.context';

describe('registerResourceMeta', () => {
let server: McpServer;
const options = getOptions();
const session = initializeSession();

beforeEach(() => {
server = new McpServer({ name: 'test', version: '1.0.0' });
jest.spyOn(server, 'registerResource').mockImplementation(() => ({} as any));
});

afterEach(() => {
jest.restoreAllMocks();
});

test('should return original resource if enableMeta is false', () => {
const callback = jest.fn();
const resource: McpResource = [
'test-resource',
'test://uri',
{ title: 'Test', description: 'Test' },
callback,
{ enableMeta: false }
];

const result = registerResourceMeta(server, ...resource, options, session);

expect(result).toEqual(resource);
expect(server.registerResource).not.toHaveBeenCalled();
});

test('should register meta resource and enhance callback if enableMeta is true', async () => {
const callback = jest.fn().mockResolvedValue({ contents: [{ uri: 'test://uri', text: 'original' }] });
const metaHandler = jest.fn().mockResolvedValue({
title: 'Meta Title',
description: 'Meta Desc',
params: [],
exampleUris: []
});

const resource: McpResource = [
'test-resource',
new ResourceTemplate('test://uri{?a}', { list: undefined }),
{ title: 'Test', description: 'Test' },
callback,
{ enableMeta: true, metaHandler }
];

const result = registerResourceMeta(server, ...resource, options, session);

// Should have registered the meta resource
expect(server.registerResource).toHaveBeenCalledWith(
'test-resource-meta',
expect.any(ResourceTemplate),
expect.objectContaining({ title: 'Test Metadata' }),
expect.any(Function)
);

// Enhanced callback should return 2 contents
const enhancedCallback = result[3];
const callResult = await enhancedCallback(new URL('test://uri'), { a: 'val' });

expect(callResult.contents).toHaveLength(2);
expect(callResult.contents[0].text).toBe('original');
expect(callResult.contents[1].uri).toBe('test://uri/meta');
expect(callResult.contents[1].text).toContain('# Resource Metadata: Meta Title');
});
});
9 changes: 8 additions & 1 deletion src/__tests__/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import { startHttpTransport, type HttpServerHandle } from '../server.http';
import { DEFAULT_OPTIONS } from '../options.defaults';

// Mock dependencies
jest.mock('@modelcontextprotocol/sdk/server/mcp.js');
jest.mock('@modelcontextprotocol/sdk/server/mcp.js', () => {
const actual = jest.requireActual('@modelcontextprotocol/sdk/server/mcp.js');

return {
...actual,
McpServer: jest.fn()
};
});
jest.mock('@modelcontextprotocol/sdk/server/stdio.js');
jest.mock('../logger');
jest.mock('../server.logger', () => ({
Expand Down
Loading
Loading