Implement BackendAdapter interface with four CLI backends: - ClaudeCodeBackend (extracted from AgentRuntime) - CodexBackend (OpenAI Codex CLI) - GeminiBackend (Google Gemini CLI) - OpenCodeBackend (OpenCode CLI) Add BackendRegistry for resolution/creation via AGENT_BACKEND env var. Refactor AgentRuntime to delegate to BackendAdapter instead of hardcoding Claude CLI. Update GatewayConfig with new env vars (AGENT_BACKEND, BACKEND_CLI_PATH, BACKEND_MODEL, BACKEND_MAX_TURNS). Includes 10 property-based test files and unit tests for edge cases.
75 lines
2.1 KiB
TypeScript
75 lines
2.1 KiB
TypeScript
import { describe, it } from "vitest";
|
|
import fc from "fast-check";
|
|
import { GeminiBackend } from "../../src/backends/gemini-backend.js";
|
|
import type { BackendAdapterConfig } from "../../src/backends/types.js";
|
|
|
|
// Feature: multi-cli-backend, Property 3: Gemini backend required flags
|
|
// **Validates: Requirements 4.2, 4.3, 4.4**
|
|
|
|
/**
|
|
* Arbitrary for non-empty strings that won't break CLI arg parsing.
|
|
*/
|
|
const nonEmptyString = fc.string({ minLength: 1, maxLength: 200 });
|
|
|
|
function createBackend(): GeminiBackend {
|
|
const config: BackendAdapterConfig = {
|
|
cliPath: "gemini",
|
|
workingDir: "/workspace",
|
|
queryTimeoutMs: 60000,
|
|
allowedTools: [],
|
|
maxTurns: 25,
|
|
};
|
|
return new GeminiBackend(config);
|
|
}
|
|
|
|
describe("Property 3: Gemini backend required flags", () => {
|
|
it("generated args always contain the prompt as a positional argument", () => {
|
|
fc.assert(
|
|
fc.property(
|
|
nonEmptyString,
|
|
(prompt) => {
|
|
const backend = createBackend();
|
|
const args = backend.buildArgs(prompt);
|
|
|
|
// The prompt (or a string containing the prompt) must appear
|
|
// as a positional arg (not preceded by a flag)
|
|
return args.some((arg) => arg.includes(prompt));
|
|
},
|
|
),
|
|
{ numRuns: 100 },
|
|
);
|
|
});
|
|
|
|
it("generated args always contain --output-format json", () => {
|
|
fc.assert(
|
|
fc.property(
|
|
nonEmptyString,
|
|
(prompt) => {
|
|
const backend = createBackend();
|
|
const args = backend.buildArgs(prompt);
|
|
|
|
const idx = args.indexOf("--output-format");
|
|
return idx !== -1 && args[idx + 1] === "json";
|
|
},
|
|
),
|
|
{ numRuns: 100 },
|
|
);
|
|
});
|
|
|
|
it("generated args always contain --approval-mode yolo", () => {
|
|
fc.assert(
|
|
fc.property(
|
|
nonEmptyString,
|
|
(prompt) => {
|
|
const backend = createBackend();
|
|
const args = backend.buildArgs(prompt);
|
|
|
|
const idx = args.indexOf("--approval-mode");
|
|
return idx !== -1 && args[idx + 1] === "yolo";
|
|
},
|
|
),
|
|
{ numRuns: 100 },
|
|
);
|
|
});
|
|
});
|