import { describe, it, expect } from "vitest"; import fc from "fast-check"; import { resolveBackendName, createBackend } from "../../src/backends/registry.js"; import { ClaudeCodeBackend } from "../../src/backends/claude-backend.js"; import { CodexBackend } from "../../src/backends/codex-backend.js"; import { GeminiBackend } from "../../src/backends/gemini-backend.js"; import { OpenCodeBackend } from "../../src/backends/opencode-backend.js"; import type { BackendAdapterConfig, BackendName } from "../../src/backends/types.js"; // Feature: multi-cli-backend, Property 7: Backend name resolution // **Validates: Requirements 6.1, 6.2, 6.3, 6.5** const VALID_NAMES: BackendName[] = ["claude", "codex", "gemini", "opencode"]; /** Arbitrary that produces one of the four valid backend names */ const validBackendName = fc.constantFrom(...VALID_NAMES); /** Arbitrary that produces strings which are NOT valid backend names and NOT undefined */ const invalidBackendName = fc .string({ minLength: 1, maxLength: 100 }) .filter((s) => !VALID_NAMES.includes(s as BackendName)); describe("Property 7: Backend name resolution", () => { it("returns the corresponding BackendName for any valid backend name string", () => { fc.assert( fc.property(validBackendName, (name) => { const result = resolveBackendName(name); return result === name; }), { numRuns: 100 }, ); }); it("returns 'claude' when input is undefined", () => { expect(resolveBackendName(undefined)).toBe("claude"); }); it("throws a descriptive error for any invalid string value", () => { fc.assert( fc.property(invalidBackendName, (name) => { try { resolveBackendName(name); return false; // Should have thrown } catch (err) { const message = (err as Error).message; // Error must mention the invalid value and list valid options return ( message.includes(name) && VALID_NAMES.every((valid) => message.includes(valid)) ); } }), { numRuns: 100 }, ); }); it("createBackend returns the correct implementation for each valid name", () => { const config: BackendAdapterConfig = { cliPath: "/usr/bin/test", workingDir: "/tmp", queryTimeoutMs: 30000, allowedTools: [], maxTurns: 25, }; const expectedTypes: Record unknown> = { claude: ClaudeCodeBackend, codex: CodexBackend, gemini: GeminiBackend, opencode: OpenCodeBackend, }; fc.assert( fc.property(validBackendName, (name) => { const backend = createBackend(name, config); return ( backend instanceof expectedTypes[name] && backend.name() === name ); }), { numRuns: 100 }, ); }); });