Skip to content

Commit ac1ffc3

Browse files
emilioaccclaude
andcommitted
Add and update tests for new tool commands
- Update index.test.ts with tests for dev subcommand and tool argument parsing - Add login.test.ts for config file format and login options - Add call-tool.test.ts for server mapping and result parsing - Add commands/commands.test.ts for all tool commands (search, image, music, video, x) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent fac2a18 commit ac1ffc3

File tree

4 files changed

+383
-8
lines changed

4 files changed

+383
-8
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { describe, it, expect } from 'vitest';
2+
3+
describe('Call Tool', () => {
4+
describe('server configuration', () => {
5+
it('should map commands to correct servers', () => {
6+
const serverMap: Record<string, string> = {
7+
search: 'search.mcp.atxp.ai',
8+
image: 'image.mcp.atxp.ai',
9+
music: 'music.mcp.atxp.ai',
10+
video: 'video.mcp.atxp.ai',
11+
x: 'x-live-search.mcp.atxp.ai',
12+
};
13+
14+
expect(serverMap.search).toBe('search.mcp.atxp.ai');
15+
expect(serverMap.image).toBe('image.mcp.atxp.ai');
16+
expect(serverMap.music).toBe('music.mcp.atxp.ai');
17+
expect(serverMap.video).toBe('video.mcp.atxp.ai');
18+
expect(serverMap.x).toBe('x-live-search.mcp.atxp.ai');
19+
});
20+
21+
it('should map commands to correct tool names', () => {
22+
const toolMap: Record<string, string> = {
23+
search: 'search',
24+
image: 'generate_image',
25+
music: 'generate_music',
26+
video: 'generate_video',
27+
x: 'x_live_search',
28+
};
29+
30+
expect(toolMap.search).toBe('search');
31+
expect(toolMap.image).toBe('generate_image');
32+
expect(toolMap.music).toBe('generate_music');
33+
expect(toolMap.video).toBe('generate_video');
34+
expect(toolMap.x).toBe('x_live_search');
35+
});
36+
});
37+
38+
describe('connection string validation', () => {
39+
it('should detect missing connection string', () => {
40+
const checkConnection = (connection?: string) => {
41+
return !!connection;
42+
};
43+
44+
expect(checkConnection(undefined)).toBe(false);
45+
expect(checkConnection('')).toBe(false);
46+
expect(checkConnection('valid-connection')).toBe(true);
47+
});
48+
});
49+
50+
describe('tool result parsing', () => {
51+
it('should extract text content from result', () => {
52+
const extractText = (
53+
result: { content: Array<{ type: string; text?: string }> } | null
54+
) => {
55+
if (result?.content?.[0]?.text) {
56+
return result.content[0].text;
57+
}
58+
return null;
59+
};
60+
61+
const textResult = { content: [{ type: 'text', text: 'Hello world' }] };
62+
expect(extractText(textResult)).toBe('Hello world');
63+
64+
const emptyResult = { content: [] };
65+
expect(extractText(emptyResult)).toBe(null);
66+
67+
expect(extractText(null)).toBe(null);
68+
});
69+
70+
it('should detect binary content', () => {
71+
const isBinaryContent = (content: { data?: string; mimeType?: string }) => {
72+
return !!(content.data && content.mimeType);
73+
};
74+
75+
expect(isBinaryContent({ data: 'base64data', mimeType: 'image/png' })).toBe(true);
76+
expect(isBinaryContent({ data: 'base64data' })).toBe(false);
77+
expect(isBinaryContent({ mimeType: 'image/png' })).toBe(false);
78+
expect(isBinaryContent({})).toBe(false);
79+
});
80+
});
81+
82+
describe('server URL construction', () => {
83+
it('should construct correct server URL', () => {
84+
const constructUrl = (server: string) => `https://${server}`;
85+
86+
expect(constructUrl('search.mcp.atxp.ai')).toBe('https://search.mcp.atxp.ai');
87+
expect(constructUrl('image.mcp.atxp.ai')).toBe('https://image.mcp.atxp.ai');
88+
});
89+
});
90+
});
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { describe, it, expect } from 'vitest';
2+
3+
describe('Tool Commands', () => {
4+
describe('search command', () => {
5+
const SERVER = 'search.mcp.atxp.ai';
6+
const TOOL = 'search';
7+
8+
it('should have correct server', () => {
9+
expect(SERVER).toBe('search.mcp.atxp.ai');
10+
});
11+
12+
it('should have correct tool name', () => {
13+
expect(TOOL).toBe('search');
14+
});
15+
16+
it('should validate query is required', () => {
17+
const validateQuery = (query: string) => {
18+
return query && query.trim().length > 0;
19+
};
20+
21+
expect(validateQuery('test query')).toBeTruthy();
22+
expect(validateQuery('')).toBeFalsy();
23+
expect(validateQuery(' ')).toBeFalsy();
24+
});
25+
26+
it('should trim query whitespace', () => {
27+
const prepareQuery = (query: string) => query.trim();
28+
29+
expect(prepareQuery(' hello world ')).toBe('hello world');
30+
expect(prepareQuery('test')).toBe('test');
31+
});
32+
});
33+
34+
describe('image command', () => {
35+
const SERVER = 'image.mcp.atxp.ai';
36+
const TOOL = 'generate_image';
37+
38+
it('should have correct server', () => {
39+
expect(SERVER).toBe('image.mcp.atxp.ai');
40+
});
41+
42+
it('should have correct tool name', () => {
43+
expect(TOOL).toBe('generate_image');
44+
});
45+
46+
it('should validate prompt is required', () => {
47+
const validatePrompt = (prompt: string) => {
48+
return prompt && prompt.trim().length > 0;
49+
};
50+
51+
expect(validatePrompt('sunset over mountains')).toBeTruthy();
52+
expect(validatePrompt('')).toBeFalsy();
53+
});
54+
});
55+
56+
describe('music command', () => {
57+
const SERVER = 'music.mcp.atxp.ai';
58+
const TOOL = 'generate_music';
59+
60+
it('should have correct server', () => {
61+
expect(SERVER).toBe('music.mcp.atxp.ai');
62+
});
63+
64+
it('should have correct tool name', () => {
65+
expect(TOOL).toBe('generate_music');
66+
});
67+
68+
it('should validate prompt is required', () => {
69+
const validatePrompt = (prompt: string) => {
70+
return prompt && prompt.trim().length > 0;
71+
};
72+
73+
expect(validatePrompt('relaxing piano')).toBeTruthy();
74+
expect(validatePrompt('')).toBeFalsy();
75+
});
76+
});
77+
78+
describe('video command', () => {
79+
const SERVER = 'video.mcp.atxp.ai';
80+
const TOOL = 'generate_video';
81+
82+
it('should have correct server', () => {
83+
expect(SERVER).toBe('video.mcp.atxp.ai');
84+
});
85+
86+
it('should have correct tool name', () => {
87+
expect(TOOL).toBe('generate_video');
88+
});
89+
90+
it('should validate prompt is required', () => {
91+
const validatePrompt = (prompt: string) => {
92+
return prompt && prompt.trim().length > 0;
93+
};
94+
95+
expect(validatePrompt('ocean waves')).toBeTruthy();
96+
expect(validatePrompt('')).toBeFalsy();
97+
});
98+
});
99+
100+
describe('x command', () => {
101+
const SERVER = 'x-live-search.mcp.atxp.ai';
102+
const TOOL = 'x_live_search';
103+
104+
it('should have correct server', () => {
105+
expect(SERVER).toBe('x-live-search.mcp.atxp.ai');
106+
});
107+
108+
it('should have correct tool name', () => {
109+
expect(TOOL).toBe('x_live_search');
110+
});
111+
112+
it('should validate query is required', () => {
113+
const validateQuery = (query: string) => {
114+
return query && query.trim().length > 0;
115+
};
116+
117+
expect(validateQuery('trending topics')).toBeTruthy();
118+
expect(validateQuery('')).toBeFalsy();
119+
});
120+
});
121+
122+
describe('common command behavior', () => {
123+
it('should construct tool arguments correctly', () => {
124+
const buildArgs = (key: string, value: string) => {
125+
return { [key]: value.trim() };
126+
};
127+
128+
expect(buildArgs('query', 'test query')).toEqual({ query: 'test query' });
129+
expect(buildArgs('prompt', ' image prompt ')).toEqual({ prompt: 'image prompt' });
130+
});
131+
132+
it('should handle multi-word inputs', () => {
133+
const parseInput = (args: string[]) => args.join(' ');
134+
135+
expect(parseInput(['hello', 'world'])).toBe('hello world');
136+
expect(parseInput(['a', 'beautiful', 'sunset'])).toBe('a beautiful sunset');
137+
expect(parseInput([])).toBe('');
138+
});
139+
});
140+
});

packages/atxp/src/index.test.ts

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { describe, it, expect } from 'vitest';
33
describe('ATXP CLI', () => {
44
describe('create mode detection', () => {
55
it('should detect create mode from command', () => {
6-
// Test the logic without importing the actual module
76
const testCreateMode = (npmConfigArgv?: string, argv: string[] = []) => {
8-
return npmConfigArgv?.includes('create') ||
9-
argv.includes('--create') ||
10-
argv[2] === 'create';
7+
return (
8+
npmConfigArgv?.includes('create') ||
9+
argv.includes('--create') ||
10+
argv[2] === 'create'
11+
);
1112
};
1213

1314
expect(testCreateMode('create')).toBe(true);
@@ -32,14 +33,35 @@ describe('ATXP CLI', () => {
3233
expect(parseAppName(['node', 'script', 'demo'])).toBe(undefined);
3334
});
3435

36+
it('should parse app name from dev create command', () => {
37+
const parseAppName = (argv: string[]) => {
38+
const command = argv[2];
39+
const subCommand = argv[3];
40+
if (command === 'dev' && subCommand === 'create') {
41+
return argv[4]; // npx atxp dev create <app-name>
42+
} else if (command === 'create') {
43+
return argv[3]; // npx atxp create <app-name> (legacy)
44+
}
45+
return undefined;
46+
};
47+
48+
expect(parseAppName(['node', 'script', 'dev', 'create', 'my-app'])).toBe('my-app');
49+
expect(parseAppName(['node', 'script', 'dev', 'create'])).toBe(undefined);
50+
expect(parseAppName(['node', 'script', 'create', 'my-app'])).toBe('my-app');
51+
});
52+
3553
it('should parse framework flag', () => {
3654
const parseFramework = (argv: string[]) => {
37-
const frameworkIndex = argv.findIndex(arg => arg === '--framework' || arg === '-f');
55+
const frameworkIndex = argv.findIndex((arg) => arg === '--framework' || arg === '-f');
3856
return frameworkIndex !== -1 ? argv[frameworkIndex + 1] : undefined;
3957
};
4058

41-
expect(parseFramework(['node', 'script', 'create', 'my-app', '--framework', 'express'])).toBe('express');
42-
expect(parseFramework(['node', 'script', 'create', 'my-app', '-f', 'express'])).toBe('express');
59+
expect(
60+
parseFramework(['node', 'script', 'create', 'my-app', '--framework', 'express'])
61+
).toBe('express');
62+
expect(parseFramework(['node', 'script', 'create', 'my-app', '-f', 'express'])).toBe(
63+
'express'
64+
);
4365
expect(parseFramework(['node', 'script', 'create', 'my-app'])).toBe(undefined);
4466
});
4567

@@ -65,5 +87,59 @@ describe('ATXP CLI', () => {
6587
expect(hasHelpFlag(['node', 'script', '--help'])).toBe(true);
6688
expect(hasHelpFlag(['node', 'script', 'create', 'my-app'])).toBe(false);
6789
});
90+
91+
it('should parse force flag for login', () => {
92+
const hasForceFlag = (argv: string[]) => {
93+
return argv.includes('--force');
94+
};
95+
96+
expect(hasForceFlag(['node', 'script', 'login', '--force'])).toBe(true);
97+
expect(hasForceFlag(['node', 'script', 'login'])).toBe(false);
98+
});
99+
});
100+
101+
describe('command routing', () => {
102+
it('should identify tool commands', () => {
103+
const toolCommands = ['search', 'image', 'music', 'video', 'x'];
104+
105+
const isToolCommand = (command: string) => {
106+
return toolCommands.includes(command);
107+
};
108+
109+
expect(isToolCommand('search')).toBe(true);
110+
expect(isToolCommand('image')).toBe(true);
111+
expect(isToolCommand('music')).toBe(true);
112+
expect(isToolCommand('video')).toBe(true);
113+
expect(isToolCommand('x')).toBe(true);
114+
expect(isToolCommand('demo')).toBe(false);
115+
expect(isToolCommand('login')).toBe(false);
116+
});
117+
118+
it('should identify dev subcommands', () => {
119+
const devSubcommands = ['demo', 'create'];
120+
121+
const isDevSubcommand = (subCommand: string) => {
122+
return devSubcommands.includes(subCommand);
123+
};
124+
125+
expect(isDevSubcommand('demo')).toBe(true);
126+
expect(isDevSubcommand('create')).toBe(true);
127+
expect(isDevSubcommand('search')).toBe(false);
128+
});
129+
130+
it('should parse tool arguments', () => {
131+
const parseToolArgs = (argv: string[]) => {
132+
// Get everything after the command, filtering out flags
133+
return argv
134+
.slice(3)
135+
.filter((arg) => !arg.startsWith('-'))
136+
.join(' ');
137+
};
138+
139+
expect(parseToolArgs(['node', 'script', 'search', 'hello', 'world'])).toBe('hello world');
140+
expect(parseToolArgs(['node', 'script', 'image', 'a', 'sunset'])).toBe('a sunset');
141+
expect(parseToolArgs(['node', 'script', 'x', 'trending'])).toBe('trending');
142+
expect(parseToolArgs(['node', 'script', 'search'])).toBe('');
143+
});
68144
});
69-
});
145+
});

0 commit comments

Comments
 (0)