Skip to content

Commit ce074a9

Browse files
committed
chore: refactor tests
1 parent d7a4936 commit ce074a9

File tree

75 files changed

+1056
-515
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1056
-515
lines changed

packages/mcp-server/src/tools/register.test.ts

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import { describe, it, expect, vi, beforeEach } from 'vitest';
22
import { registerAllTools } from './register.js';
33
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4-
import {
5-
aiActionTools,
6-
assetTools,
7-
contentTypeTools,
8-
contextTools,
9-
editorInterfaceTools,
10-
entryTools,
11-
environmentTools,
12-
jobTools,
13-
localeTools,
14-
orgTools,
15-
spaceTools,
16-
tagTools,
17-
taxonomyTools,
18-
} from '@contentful/mcp-tools';
4+
import { ContentfulMcpTools } from '@contentful/mcp-tools';
5+
import { env } from '../config/env.js';
6+
7+
// Mock the env module
8+
vi.mock('../config/env.js', () => ({
9+
env: {
10+
success: true,
11+
data: {
12+
CONTENTFUL_MANAGEMENT_ACCESS_TOKEN: 'test-token',
13+
CONTENTFUL_HOST: 'api.contentful.com',
14+
SPACE_ID: 'test-space-id',
15+
ENVIRONMENT_ID: 'master',
16+
ORGANIZATION_ID: 'test-org-id',
17+
APP_ID: 'test-app-id',
18+
},
19+
},
20+
}));
1921

2022
describe('registerAllTools', () => {
2123
let mockServer: McpServer;
@@ -38,20 +40,30 @@ describe('registerAllTools', () => {
3840
it('should register all standard tool collections', () => {
3941
registerAllTools(mockServer);
4042

43+
// Create a ContentfulMcpTools instance to count tools
44+
const mcpTools = new ContentfulMcpTools({
45+
accessToken: env.data!.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
46+
host: env.data!.CONTENTFUL_HOST,
47+
spaceId: env.data!.SPACE_ID,
48+
environmentId: env.data!.ENVIRONMENT_ID,
49+
organizationId: env.data!.ORGANIZATION_ID,
50+
appId: env.data!.APP_ID,
51+
});
52+
4153
// Count expected tool registrations from standard collections
4254
const standardToolCollections = [
43-
aiActionTools,
44-
assetTools,
45-
contentTypeTools,
46-
contextTools,
47-
editorInterfaceTools,
48-
entryTools,
49-
environmentTools,
50-
localeTools,
51-
orgTools,
52-
spaceTools,
53-
tagTools,
54-
taxonomyTools,
55+
mcpTools.getAiActionTools(),
56+
mcpTools.getAssetTools(),
57+
mcpTools.getContentTypeTools(),
58+
mcpTools.getContextTools(),
59+
mcpTools.getEditorInterfaceTools(),
60+
mcpTools.getEntryTools(),
61+
mcpTools.getEnvironmentTools(),
62+
mcpTools.getLocaleTools(),
63+
mcpTools.getOrgTools(),
64+
mcpTools.getSpaceTools(),
65+
mcpTools.getTagTools(),
66+
mcpTools.getTaxonomyTools(),
5567
];
5668

5769
const expectedStandardToolCount = standardToolCollections.reduce(
@@ -90,7 +102,15 @@ describe('registerAllTools', () => {
90102
it('should register spaceToSpaceMigrationHandler with workflow tools', () => {
91103
registerAllTools(mockServer);
92104

93-
const handlerConfig = jobTools.spaceToSpaceMigrationHandler;
105+
const mcpTools = new ContentfulMcpTools({
106+
accessToken: env.data!.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
107+
host: env.data!.CONTENTFUL_HOST,
108+
spaceId: env.data!.SPACE_ID,
109+
environmentId: env.data!.ENVIRONMENT_ID,
110+
organizationId: env.data!.ORGANIZATION_ID,
111+
appId: env.data!.APP_ID,
112+
});
113+
const handlerConfig = mcpTools.getJobTools().spaceToSpaceMigrationHandler;
94114

95115
// Find the call for the migration handler
96116
const handlerCall = registerToolSpy.mock.calls.find(
Lines changed: 83 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,104 @@
11
import { describe, it, expect } from 'vitest';
2+
import { ContentfulMcpTools, type ContentfulConfig } from './index.js';
3+
import { createMockConfig } from './test-helpers/mockConfig.js';
24

35
describe('Package Exports', () => {
4-
it('should export all tool collections', async () => {
6+
it('should export ContentfulMcpTools class', async () => {
57
const moduleExports = await import('./index.js');
68

7-
const {
8-
aiActionTools,
9-
assetTools,
10-
contentTypeTools,
11-
contextTools,
12-
editorInterfaceTools,
13-
entryTools,
14-
environmentTools,
15-
jobTools,
16-
localeTools,
17-
orgTools,
18-
spaceTools,
19-
tagTools,
20-
taxonomyTools,
21-
} = moduleExports;
22-
23-
expect(aiActionTools).toBeDefined();
24-
expect(typeof aiActionTools).toBe('object');
25-
26-
expect(assetTools).toBeDefined();
27-
expect(typeof assetTools).toBe('object');
9+
expect(moduleExports.ContentfulMcpTools).toBeDefined();
10+
expect(typeof moduleExports.ContentfulMcpTools).toBe('function');
11+
});
2812

29-
expect(contentTypeTools).toBeDefined();
30-
expect(typeof contentTypeTools).toBe('object');
13+
it('should export ContentfulConfig type', async () => {
14+
// TypeScript types are erased at runtime, so we can't test for them directly
15+
// Instead, we verify that the type can be imported and used
16+
const moduleExports = await import('./index.js');
17+
const { ContentfulMcpTools } = moduleExports;
3118

32-
expect(contextTools).toBeDefined();
33-
expect(typeof contextTools).toBe('object');
19+
// Verify we can use the type by checking that ContentfulMcpTools accepts ContentfulConfig
20+
const mockConfig = createMockConfig();
21+
const tools = new ContentfulMcpTools(mockConfig);
22+
expect(tools).toBeDefined();
23+
});
3424

35-
expect(editorInterfaceTools).toBeDefined();
36-
expect(typeof editorInterfaceTools).toBe('object');
25+
it('should export exactly 1 runtime item (ContentfulMcpTools class)', async () => {
26+
// TypeScript types are erased at runtime, so ContentfulConfig won't be in exports
27+
const moduleExports = await import('./index.js');
28+
const exportedKeys = Object.keys(moduleExports);
3729

38-
expect(entryTools).toBeDefined();
39-
expect(typeof entryTools).toBe('object');
30+
expect(exportedKeys).toHaveLength(1);
31+
expect(exportedKeys).toContain('ContentfulMcpTools');
32+
});
33+
});
4034

41-
expect(environmentTools).toBeDefined();
42-
expect(typeof environmentTools).toBe('object');
35+
describe('ContentfulMcpTools', () => {
36+
const mockConfig = createMockConfig();
4337

44-
expect(jobTools).toBeDefined();
45-
expect(typeof jobTools).toBe('object');
38+
it('should instantiate with config', () => {
39+
const tools = new ContentfulMcpTools(mockConfig);
40+
expect(tools).toBeDefined();
41+
expect(tools).toBeInstanceOf(ContentfulMcpTools);
42+
});
4643

47-
expect(localeTools).toBeDefined();
48-
expect(typeof localeTools).toBe('object');
44+
it('should provide all tool collection getters', () => {
45+
const tools = new ContentfulMcpTools(mockConfig);
46+
47+
expect(tools.getAiActionTools).toBeDefined();
48+
expect(typeof tools.getAiActionTools).toBe('function');
49+
expect(tools.getAssetTools).toBeDefined();
50+
expect(typeof tools.getAssetTools).toBe('function');
51+
expect(tools.getContentTypeTools).toBeDefined();
52+
expect(typeof tools.getContentTypeTools).toBe('function');
53+
expect(tools.getContextTools).toBeDefined();
54+
expect(typeof tools.getContextTools).toBe('function');
55+
expect(tools.getEditorInterfaceTools).toBeDefined();
56+
expect(typeof tools.getEditorInterfaceTools).toBe('function');
57+
expect(tools.getEntryTools).toBeDefined();
58+
expect(typeof tools.getEntryTools).toBe('function');
59+
expect(tools.getEnvironmentTools).toBeDefined();
60+
expect(typeof tools.getEnvironmentTools).toBe('function');
61+
expect(tools.getLocaleTools).toBeDefined();
62+
expect(typeof tools.getLocaleTools).toBe('function');
63+
expect(tools.getOrgTools).toBeDefined();
64+
expect(typeof tools.getOrgTools).toBe('function');
65+
expect(tools.getSpaceTools).toBeDefined();
66+
expect(typeof tools.getSpaceTools).toBe('function');
67+
expect(tools.getTagTools).toBeDefined();
68+
expect(typeof tools.getTagTools).toBe('function');
69+
expect(tools.getTaxonomyTools).toBeDefined();
70+
expect(typeof tools.getTaxonomyTools).toBe('function');
71+
expect(tools.getJobTools).toBeDefined();
72+
expect(typeof tools.getJobTools).toBe('function');
73+
});
4974

50-
expect(orgTools).toBeDefined();
51-
expect(typeof orgTools).toBe('object');
75+
it('should return tool collections from getters', () => {
76+
const tools = new ContentfulMcpTools(mockConfig);
5277

53-
expect(spaceTools).toBeDefined();
54-
expect(typeof spaceTools).toBe('object');
78+
const aiActionTools = tools.getAiActionTools();
79+
expect(aiActionTools).toBeDefined();
80+
expect(typeof aiActionTools).toBe('object');
5581

56-
expect(tagTools).toBeDefined();
57-
expect(typeof tagTools).toBe('object');
82+
const assetTools = tools.getAssetTools();
83+
expect(assetTools).toBeDefined();
84+
expect(typeof assetTools).toBe('object');
5885

59-
expect(taxonomyTools).toBeDefined();
60-
expect(typeof taxonomyTools).toBe('object');
86+
const contentTypeTools = tools.getContentTypeTools();
87+
expect(contentTypeTools).toBeDefined();
88+
expect(typeof contentTypeTools).toBe('object');
6189
});
6290

63-
it('should export exactly 13 tool collections', async () => {
64-
const moduleExports = await import('./index.js');
65-
const exportedKeys = Object.keys(moduleExports);
91+
it('should allow updating config', () => {
92+
const tools = new ContentfulMcpTools(mockConfig);
93+
const newConfig: Partial<ContentfulConfig> = {
94+
accessToken: 'new-token',
95+
};
96+
97+
tools.updateConfig(newConfig);
6698

67-
expect(exportedKeys).toHaveLength(13);
99+
// Verify the update worked by checking that new tools use the new config
100+
// This is an indirect test - the actual config is private
101+
expect(tools.updateConfig).toBeDefined();
102+
expect(typeof tools.updateConfig).toBe('function');
68103
});
69104
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { ContentfulConfig } from '../config/types.js';
2+
3+
/**
4+
* Creates a mock ContentfulConfig for testing
5+
*/
6+
export function createMockConfig(
7+
overrides?: Partial<ContentfulConfig>,
8+
): ContentfulConfig {
9+
return {
10+
accessToken: 'test-access-token',
11+
host: 'api.contentful.com',
12+
spaceId: 'test-space-id',
13+
environmentId: 'test-environment',
14+
organizationId: 'test-org-id',
15+
appId: 'test-app-id',
16+
...overrides,
17+
};
18+
}

packages/mcp-tools/src/tools/ai-actions/createAiAction.test.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import {
99
mockArgs,
1010
mockComplexAiAction,
1111
} from './mockClient.js';
12+
import { createMockConfig } from '../../test-helpers/mockConfig.js';
1213

13-
vi.mock('../../../src/utils/tools.js');
14-
vi.mock('../../../src/config/contentful.js');
14+
vi.mock('../../utils/tools.js');
1515

1616
describe('createAiAction', () => {
17+
const mockConfig = createMockConfig();
18+
1719
beforeEach(() => {
1820
setupMockClient();
1921
});
@@ -51,7 +53,8 @@ describe('createAiAction', () => {
5153

5254
mockAiActionCreate.mockResolvedValue(mockAiAction);
5355

54-
const result = await createAiActionTool(testArgs);
56+
const tool = createAiActionTool(mockConfig);
57+
const result = await tool(testArgs);
5558

5659
const expectedResponse = formatResponse('AI action created successfully', {
5760
aiAction: mockAiAction,
@@ -101,15 +104,16 @@ describe('createAiAction', () => {
101104
},
102105
testCases: [
103106
{
104-
name: 'Test case 1',
105-
variables: {},
107+
type: 'Text' as const,
108+
value: 'Test input',
106109
},
107110
],
108111
};
109112

110113
mockAiActionCreate.mockResolvedValue(mockComplexAiAction);
111114

112-
const result = await createAiActionTool(testArgs);
115+
const tool = createAiActionTool(mockConfig);
116+
const result = await tool(testArgs);
113117

114118
const expectedResponse = formatResponse('AI action created successfully', {
115119
aiAction: mockComplexAiAction,
@@ -147,7 +151,8 @@ describe('createAiAction', () => {
147151
const error = new Error('Invalid model configuration');
148152
mockAiActionCreate.mockRejectedValue(error);
149153

150-
const result = await createAiActionTool(testArgs);
154+
const tool = createAiActionTool(mockConfig);
155+
const result = await tool(testArgs);
151156

152157
expect(result).toEqual({
153158
isError: true,

packages/mcp-tools/src/tools/ai-actions/deleteAiAction.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import {
88
mockAiAction,
99
mockAiActionGet,
1010
} from './mockClient.js';
11+
import { createMockConfig } from '../../test-helpers/mockConfig.js';
1112

1213
vi.mock('../../../src/utils/tools.js');
13-
vi.mock('../../../src/config/contentful.js');
1414

1515
describe('deleteAiAction', () => {
16+
const mockConfig = createMockConfig();
17+
1618
beforeEach(() => {
1719
setupMockClient();
1820
});
@@ -21,7 +23,8 @@ describe('deleteAiAction', () => {
2123
mockAiActionGet.mockResolvedValue(mockAiAction);
2224
mockAiActionDelete.mockResolvedValue(undefined);
2325

24-
const result = await deleteAiActionTool(mockArgs);
26+
const tool = deleteAiActionTool(mockConfig);
27+
const result = await tool(mockArgs);
2528

2629
const expectedResponse = formatResponse('AI action deleted successfully', {
2730
aiAction: mockAiAction,
@@ -40,7 +43,8 @@ describe('deleteAiAction', () => {
4043
const error = new Error('AI action not found');
4144
mockAiActionDelete.mockRejectedValue(error);
4245

43-
const result = await deleteAiActionTool(mockArgs);
46+
const tool = deleteAiActionTool(mockConfig);
47+
const result = await tool(mockArgs);
4448

4549
expect(result).toEqual({
4650
isError: true,

packages/mcp-tools/src/tools/ai-actions/getAiAction.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,22 @@ import {
77
mockAiAction,
88
mockArgs,
99
} from './mockClient.js';
10+
import { createMockConfig } from '../../test-helpers/mockConfig.js';
1011

11-
vi.mock('../../../src/utils/tools.js');
12-
vi.mock('../../../src/config/contentful.js');
12+
vi.mock('../../utils/tools.js');
1313

1414
describe('getAiAction', () => {
15+
const mockConfig = createMockConfig();
16+
1517
beforeEach(() => {
1618
setupMockClient();
1719
});
1820

1921
it('should retrieve an AI action successfully', async () => {
2022
mockAiActionGet.mockResolvedValue(mockAiAction);
2123

22-
const result = await getAiActionTool(mockArgs);
24+
const tool = getAiActionTool(mockConfig);
25+
const result = await tool(mockArgs);
2326

2427
const expectedResponse = formatResponse(
2528
'AI action retrieved successfully',
@@ -41,7 +44,8 @@ describe('getAiAction', () => {
4144
const error = new Error('AI action not found');
4245
mockAiActionGet.mockRejectedValue(error);
4346

44-
const result = await getAiActionTool(mockArgs);
47+
const tool = getAiActionTool(mockConfig);
48+
const result = await tool(mockArgs);
4549

4650
expect(result).toEqual({
4751
isError: true,

0 commit comments

Comments
 (0)