import { describe, it, expect, vi } from 'vitest'; import { createDescribeCommand } from '../../src/commands/describe.js'; import type { DescribeCommandDeps } from '../../src/commands/describe.js'; import type { ApiClient } from '../../src/api-client.js'; function mockClient(): ApiClient { return { get: vi.fn(async () => []), post: vi.fn(async () => ({})), put: vi.fn(async () => ({})), delete: vi.fn(async () => {}), } as unknown as ApiClient; } function makeDeps(item: unknown = {}): DescribeCommandDeps & { output: string[] } { const output: string[] = []; return { output, client: mockClient(), fetchResource: vi.fn(async () => item), log: (...args: string[]) => output.push(args.join(' ')), }; } describe('describe command', () => { it('shows detailed server info with sections', async () => { const deps = makeDeps({ id: 'srv-1', name: 'slack', transport: 'STDIO', packageName: '@slack/mcp', dockerImage: null, env: [], createdAt: '2025-01-01', }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'server', 'srv-1']); expect(deps.fetchResource).toHaveBeenCalledWith('servers', 'srv-1'); const text = deps.output.join('\n'); expect(text).toContain('=== Server: slack ==='); expect(text).toContain('Name:'); expect(text).toContain('slack'); expect(text).toContain('Transport:'); expect(text).toContain('STDIO'); expect(text).toContain('Package:'); expect(text).toContain('@slack/mcp'); expect(text).toContain('Metadata:'); expect(text).toContain('ID:'); }); it('resolves resource aliases', async () => { const deps = makeDeps({ id: 's1' }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'sec', 's1']); expect(deps.fetchResource).toHaveBeenCalledWith('secrets', 's1'); }); it('outputs JSON format', async () => { const deps = makeDeps({ id: 'srv-1', name: 'slack' }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'server', 'srv-1', '-o', 'json']); const parsed = JSON.parse(deps.output[0] ?? ''); expect(parsed.name).toBe('slack'); }); it('outputs YAML format', async () => { const deps = makeDeps({ id: 'srv-1', name: 'slack' }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'server', 'srv-1', '-o', 'yaml']); expect(deps.output[0]).toContain('name: slack'); }); it('shows project detail', async () => { const deps = makeDeps({ id: 'proj-1', name: 'my-project', description: 'A test project', ownerId: 'user-1', createdAt: '2025-01-01', }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'project', 'proj-1']); const text = deps.output.join('\n'); expect(text).toContain('=== Project: my-project ==='); expect(text).toContain('A test project'); expect(text).toContain('user-1'); }); it('shows secret detail with masked values', async () => { const deps = makeDeps({ id: 'sec-1', name: 'ha-creds', data: { TOKEN: 'abc123', URL: 'https://ha.local' }, createdAt: '2025-01-01', }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'secret', 'sec-1']); const text = deps.output.join('\n'); expect(text).toContain('=== Secret: ha-creds ==='); expect(text).toContain('TOKEN'); expect(text).toContain('***'); expect(text).not.toContain('abc123'); expect(text).toContain('use --show-values to reveal'); }); it('shows secret detail with revealed values when --show-values', async () => { const deps = makeDeps({ id: 'sec-1', name: 'ha-creds', data: { TOKEN: 'abc123' }, createdAt: '2025-01-01', }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'secret', 'sec-1', '--show-values']); const text = deps.output.join('\n'); expect(text).toContain('abc123'); expect(text).not.toContain('***'); }); it('shows instance detail with container info', async () => { const deps = makeDeps({ id: 'inst-1', serverId: 'srv-1', status: 'RUNNING', containerId: 'abc123', port: 3000, createdAt: '2025-01-01', }); const cmd = createDescribeCommand(deps); await cmd.parseAsync(['node', 'test', 'instance', 'inst-1']); const text = deps.output.join('\n'); expect(text).toContain('=== Instance: inst-1 ==='); expect(text).toContain('RUNNING'); expect(text).toContain('abc123'); }); });