import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createInstanceCommands } from '../../src/commands/instances.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; } describe('instance commands', () => { let client: ReturnType; let output: string[]; const log = (...args: unknown[]) => output.push(args.map(String).join(' ')); beforeEach(() => { client = mockClient(); output = []; }); describe('list', () => { it('shows no instances message when empty', async () => { const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['list'], { from: 'user' }); expect(output.join('\n')).toContain('No instances found'); }); it('shows instance table', async () => { vi.mocked(client.get).mockResolvedValue([ { id: 'inst-1', serverId: 'srv-1', status: 'RUNNING', containerId: 'ctr-abc123def', port: 3000, createdAt: '2025-01-01' }, ]); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['list'], { from: 'user' }); expect(output.join('\n')).toContain('inst-1'); expect(output.join('\n')).toContain('RUNNING'); }); it('filters by server', async () => { const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['list', '-s', 'srv-1'], { from: 'user' }); expect(client.get).toHaveBeenCalledWith(expect.stringContaining('serverId=srv-1')); }); it('outputs json', async () => { vi.mocked(client.get).mockResolvedValue([{ id: 'inst-1' }]); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['list', '-o', 'json'], { from: 'user' }); expect(output[0]).toContain('"id"'); }); }); describe('start', () => { it('starts an instance', async () => { vi.mocked(client.post).mockResolvedValue({ id: 'inst-new', status: 'RUNNING' }); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['start', 'srv-1'], { from: 'user' }); expect(client.post).toHaveBeenCalledWith('/api/v1/instances', { serverId: 'srv-1' }); expect(output.join('\n')).toContain('started'); }); it('passes host port', async () => { vi.mocked(client.post).mockResolvedValue({ id: 'inst-new', status: 'RUNNING' }); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['start', 'srv-1', '-p', '8080'], { from: 'user' }); expect(client.post).toHaveBeenCalledWith('/api/v1/instances', { serverId: 'srv-1', hostPort: 8080 }); }); }); describe('stop', () => { it('stops an instance', async () => { vi.mocked(client.post).mockResolvedValue({ id: 'inst-1', status: 'STOPPED' }); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['stop', 'inst-1'], { from: 'user' }); expect(client.post).toHaveBeenCalledWith('/api/v1/instances/inst-1/stop'); expect(output.join('\n')).toContain('stopped'); }); }); describe('restart', () => { it('restarts an instance', async () => { vi.mocked(client.post).mockResolvedValue({ id: 'inst-2', status: 'RUNNING' }); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['restart', 'inst-1'], { from: 'user' }); expect(client.post).toHaveBeenCalledWith('/api/v1/instances/inst-1/restart'); expect(output.join('\n')).toContain('restarted'); }); }); describe('remove', () => { it('removes an instance', async () => { const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['remove', 'inst-1'], { from: 'user' }); expect(client.delete).toHaveBeenCalledWith('/api/v1/instances/inst-1'); expect(output.join('\n')).toContain('removed'); }); }); describe('logs', () => { it('shows logs', async () => { vi.mocked(client.get).mockResolvedValue({ stdout: 'hello world\n', stderr: '' }); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['logs', 'inst-1'], { from: 'user' }); expect(client.get).toHaveBeenCalledWith('/api/v1/instances/inst-1/logs'); expect(output.join('\n')).toContain('hello world'); }); it('passes tail option', async () => { vi.mocked(client.get).mockResolvedValue({ stdout: '', stderr: '' }); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['logs', 'inst-1', '-t', '50'], { from: 'user' }); expect(client.get).toHaveBeenCalledWith('/api/v1/instances/inst-1/logs?tail=50'); }); }); describe('inspect', () => { it('shows container info as json', async () => { vi.mocked(client.get).mockResolvedValue({ containerId: 'ctr-abc', state: 'running' }); const cmd = createInstanceCommands({ client, log }); await cmd.parseAsync(['inspect', 'inst-1'], { from: 'user' }); expect(client.get).toHaveBeenCalledWith('/api/v1/instances/inst-1/inspect'); expect(output[0]).toContain('ctr-abc'); }); }); });