-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathresource.patternFlyDocsTemplate.test.ts
More file actions
115 lines (101 loc) · 3.73 KB
/
resource.patternFlyDocsTemplate.test.ts
File metadata and controls
115 lines (101 loc) · 3.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { McpError } from '@modelcontextprotocol/sdk/types.js';
import { patternFlyDocsTemplateResource } from '../resource.patternFlyDocsTemplate';
import { fetchComponentData } from '../api.fetcher';
import { searchComponents } from '../tool.searchPatternFlyDocs';
import { isPlainObject } from '../server.helpers';
jest.mock('../api.fetcher');
jest.mock('../tool.searchPatternFlyDocs');
jest.mock('../server.caching', () => ({
memo: jest.fn(fn => fn)
}));
jest.mock('../options.context', () => ({
getOptions: jest.fn(() => ({}))
}));
const mockFetchComponentData = fetchComponentData as jest.MockedFunction<typeof fetchComponentData>;
const mockSearchComponents = searchComponents as jest.MockedFunction<typeof searchComponents>;
describe('patternFlyDocsTemplateResource', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should have a consistent return structure', () => {
const resource = patternFlyDocsTemplateResource();
expect({
name: resource[0],
uri: resource[1],
config: isPlainObject(resource[2]),
handler: resource[3]
}).toMatchSnapshot('structure');
});
});
describe('patternFlyDocsTemplateResource, callback', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it.each([
{
description: 'default',
name: 'Button',
docs: '# Button documentation content'
},
{
description: 'with trimmed name',
name: ' Table ',
docs: '# Table documentation content'
},
{
description: 'with lower case name',
name: 'button',
docs: '# Button documentation content'
}
])('should parse parameters and return documentation, $description', async ({ name, docs }) => {
mockFetchComponentData.mockResolvedValue({ name: name.trim(), info: {} as any, docs });
const [_name, _uri, _config, callback] = patternFlyDocsTemplateResource();
const uri = new URL('patternfly://docs/Button');
const variables = { name };
const result = await callback(uri, variables);
expect(result.contents).toBeDefined();
expect(Object.keys(result.contents[0])).toEqual(['uri', 'mimeType', 'text']);
expect(result.contents[0].text).toContain(docs);
});
it.each([
{
description: 'with missing or undefined name',
error: 'Missing required parameter: name must be a string',
variables: {}
},
{
description: 'with null name',
error: 'Missing required parameter: name must be a string',
variables: { name: null }
},
{
description: 'with empty name',
error: 'Missing required parameter: name must be a string',
variables: { name: '' }
},
{
description: 'with non-string name',
error: 'Missing required parameter: name must be a string',
variables: { name: 123 }
}
])('should handle variable errors, $description', async ({ error, variables }) => {
const [_name, _uri, _config, callback] = patternFlyDocsTemplateResource();
const uri = new URL('patternfly://docs/test');
await expect(callback(uri, variables)).rejects.toThrow(McpError);
await expect(callback(uri, variables)).rejects.toThrow(error);
});
it('should handle documentation not found errors', async () => {
mockFetchComponentData.mockResolvedValue(undefined);
mockSearchComponents.mockResolvedValue({
isSearchWildCardAll: false,
firstExactMatch: undefined,
exactMatches: [],
searchResults: []
});
const [_name, _uri, _config, handler] = patternFlyDocsTemplateResource();
const uri = new URL('patternfly://docs/Button');
const variables = { name: 'Button' };
await expect(handler(uri, variables)).rejects.toThrow(McpError);
await expect(handler(uri, variables)).rejects.toThrow('No documentation found');
});
});