import { describe, expect, it, vi } from 'vitest' import type { AgentConfig } from '../src/config/agentConfig.js' import type { Keystore } from '../src/keys/keystore.js' import type { AgentIdentity } from '../src/keys/identity.js' import type { EnrollResult } from 'relay-contracts' import { CliUsageError, parseArgs, runCli, type CliDeps } from '../src/cli.js' const CFG: AgentConfig = { relayUrl: 'wss://relay/agent', enrollUrl: 'https://x/enroll', stateDir: '/tmp/x', localTargetUrl: 'ws://127.0.0.1:3000', subdomain: 'host-42', hostId: 'h-1', } function fakeIdentity(): AgentIdentity { return { publicKey: new Uint8Array(32), enrollFpr: 'fpr', sign: () => new Uint8Array(64), exportPrivatePkcs8Pem: () => 'PEM', privateKeyObject: () => ({}) as never, } } function fakeKeystore(enrolled: boolean): Keystore { return { saveIdentity: vi.fn(), loadIdentity: () => (enrolled ? fakeIdentity() : null), saveCert: vi.fn(), loadCert: () => (enrolled ? { certPem: 'C', caChainPem: 'CA' } : null), saveContentSecret: vi.fn(), loadContentSecret: () => null, } } function deps(overrides: Partial = {}, enrolled = false): { d: CliDeps; out: string[] } { const out: string[] = [] const d: CliDeps = { loadConfig: () => CFG, openKeystore: () => fakeKeystore(enrolled), generateIdentity: fakeIdentity, redeem: async (): Promise => ({ hostId: 'h-1', subdomain: 'host-42', cert: 'C', caChain: 'CA', hostContentSecret: new Uint8Array([1]), }), runTunnel: async () => 0, installService: vi.fn(async () => {}), uninstallService: vi.fn(async () => {}), print: (l) => out.push(l), ...overrides, } return { d, out } } describe('parseArgs (T5)', () => { it('parses `pair ABCD-1234`', () => { expect(parseArgs(['pair', 'ABCD-1234'])).toEqual({ command: 'pair', code: 'ABCD-1234', flags: {} }) }) it('rejects an unknown command', () => { expect(() => parseArgs(['frobnicate'])).toThrow(CliUsageError) }) it('requires a code on pair', () => { expect(() => parseArgs(['pair'])).toThrow(CliUsageError) }) it('collects flags', () => { expect(parseArgs(['pair', 'X', '--install'])).toEqual({ command: 'pair', code: 'X', flags: { install: true }, }) }) }) describe('runCli (T5)', () => { it('pair happy path calls redeem and prints no secrets', async () => { const { d, out } = deps() const code = await runCli(parseArgs(['pair', 'ABCD']), d) expect(code).toBe(0) expect(out.join('\n')).toContain('host-42') expect(out.join('\n')).not.toContain('PEM') }) it('pair --install installs the service', async () => { const install = vi.fn(async () => {}) const { d } = deps({ installService: install }) await runCli(parseArgs(['pair', 'ABCD', '--install']), d) expect(install).toHaveBeenCalledOnce() }) it('run before pairing fails fast', async () => { const { d } = deps({}, false) await expect(runCli({ command: 'run', flags: {} }, d)).rejects.toBeInstanceOf(CliUsageError) }) it('status prints no key/cert material (INV9)', async () => { const { d, out } = deps({}, true) await runCli({ command: 'status', flags: {} }, d) const joined = out.join('\n') expect(joined).toContain('subdomain: host-42') expect(joined).not.toContain('PEM') expect(joined).not.toContain('CA') }) })