import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createSetupCommand } from '../../src/commands/setup.js'; import type { ApiClient } from '../../src/api-client.js'; import type { SetupPromptDeps } from '../../src/commands/setup.js'; function mockClient(): ApiClient { return { get: vi.fn(async () => []), post: vi.fn(async () => ({ id: 'new-id', name: 'test' })), put: vi.fn(async () => ({})), delete: vi.fn(async () => {}), } as unknown as ApiClient; } function mockPrompt(answers: Record): SetupPromptDeps { const answersQueue = { ...answers }; return { input: vi.fn(async (message: string) => { for (const [key, val] of Object.entries(answersQueue)) { if (message.toLowerCase().includes(key.toLowerCase()) && typeof val === 'string') { delete answersQueue[key]; return val; } } return ''; }), password: vi.fn(async () => 'secret-value'), select: vi.fn(async () => 'STDIO') as SetupPromptDeps['select'], confirm: vi.fn(async (message: string) => { if (message.includes('profile')) return true; if (message.includes('secret')) return false; if (message.includes('another')) return false; return false; }), }; } describe('setup command', () => { let client: ReturnType; let output: string[]; const log = (...args: unknown[]) => output.push(args.map(String).join(' ')); beforeEach(() => { client = mockClient(); output = []; }); it('creates server with prompted values', async () => { const prompt = mockPrompt({ 'transport': 'STDIO', 'npm package': '@anthropic/slack-mcp', 'docker image': '', 'description': 'Slack server', 'profile name': 'default', 'environment variable name': '', }); const cmd = createSetupCommand({ client, prompt, log }); await cmd.parseAsync(['slack'], { from: 'user' }); expect(client.post).toHaveBeenCalledWith('/api/v1/servers', expect.objectContaining({ name: 'slack', transport: 'STDIO', })); expect(output.join('\n')).toContain("Server 'test' created"); }); it('creates profile with env vars', async () => { vi.mocked(client.post) .mockResolvedValueOnce({ id: 'srv-1', name: 'slack' }) // server create .mockResolvedValueOnce({ id: 'prof-1', name: 'default' }); // profile create const prompt = mockPrompt({ 'transport': 'STDIO', 'npm package': '', 'docker image': '', 'description': '', 'profile name': 'default', }); // Override confirm to create profile and add one env var let confirmCallCount = 0; vi.mocked(prompt.confirm).mockImplementation(async (msg: string) => { confirmCallCount++; if (msg.includes('profile')) return true; if (msg.includes('secret')) return true; if (msg.includes('another')) return false; return false; }); // Override input to provide env var name then empty to stop let inputCallCount = 0; vi.mocked(prompt.input).mockImplementation(async (msg: string) => { inputCallCount++; if (msg.includes('Profile name')) return 'default'; if (msg.includes('variable name') && inputCallCount <= 8) return 'API_KEY'; if (msg.includes('variable name')) return ''; return ''; }); const cmd = createSetupCommand({ client, prompt, log }); await cmd.parseAsync(['slack'], { from: 'user' }); expect(client.post).toHaveBeenCalledTimes(2); const profileCall = vi.mocked(client.post).mock.calls[1]; expect(profileCall?.[0]).toBe('/api/v1/profiles'); expect(profileCall?.[1]).toEqual(expect.objectContaining({ name: 'default', serverId: 'srv-1', })); }); it('exits if server creation fails', async () => { vi.mocked(client.post).mockRejectedValue(new Error('conflict')); const prompt = mockPrompt({ 'npm package': '', 'docker image': '', 'description': '', }); const cmd = createSetupCommand({ client, prompt, log }); await cmd.parseAsync(['slack'], { from: 'user' }); expect(output.join('\n')).toContain('Failed to create server'); expect(client.post).toHaveBeenCalledTimes(1); // Only server create, no profile }); it('skips profile creation when declined', async () => { const prompt = mockPrompt({ 'npm package': '', 'docker image': '', 'description': '', }); vi.mocked(prompt.confirm).mockResolvedValue(false); const cmd = createSetupCommand({ client, prompt, log }); await cmd.parseAsync(['test-server'], { from: 'user' }); expect(client.post).toHaveBeenCalledTimes(1); // Only server create expect(output.join('\n')).toContain('Setup complete'); }); });