Files
aetheel-2/tests/property/agent-runtime.property.test.ts
tanmay11k 453389f55c feat: add pluggable multi-CLI backend system
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.
2026-02-22 23:41:30 -05:00

120 lines
4.1 KiB
TypeScript

import { describe, it, expect } from "vitest";
import fc from "fast-check";
import { mapBackendEventResult } from "../../src/agent-runtime.js";
import { SessionManager } from "../../src/session-manager.js";
import type { BackendEventResult } from "../../src/backends/types.js";
// Feature: multi-cli-backend, Property 9: EventResult mapping preserves semantics
// **Validates: Requirements 10.3**
/** Arbitrary that produces a BackendEventResult */
const backendEventResult: fc.Arbitrary<BackendEventResult> = fc.record({
responseText: fc.option(fc.string({ minLength: 0, maxLength: 500 }), { nil: undefined }),
sessionId: fc.option(fc.string({ minLength: 1, maxLength: 100 }), { nil: undefined }),
isError: fc.boolean(),
});
/** Arbitrary for channel IDs */
const channelId = fc.option(fc.string({ minLength: 1, maxLength: 50 }), { nil: undefined });
describe("Property 9: EventResult mapping preserves semantics", () => {
it("sets error to responseText when isError is true, with no responseText on gateway result", () => {
fc.assert(
fc.property(
backendEventResult.filter((r) => r.isError),
channelId,
(result, chId) => {
const mapped = mapBackendEventResult(result, chId);
expect(mapped.error).toBe(result.responseText);
expect(mapped.responseText).toBeUndefined();
expect(mapped.sessionId).toBeUndefined();
expect(mapped.targetChannelId).toBe(chId);
},
),
{ numRuns: 100 },
);
});
it("sets responseText and sessionId when isError is false, with no error on gateway result", () => {
fc.assert(
fc.property(
backendEventResult.filter((r) => !r.isError),
channelId,
(result, chId) => {
const mapped = mapBackendEventResult(result, chId);
expect(mapped.responseText).toBe(result.responseText);
expect(mapped.sessionId).toBe(result.sessionId);
expect(mapped.error).toBeUndefined();
expect(mapped.targetChannelId).toBe(chId);
},
),
{ numRuns: 100 },
);
});
it("always sets targetChannelId regardless of isError", () => {
fc.assert(
fc.property(backendEventResult, channelId, (result, chId) => {
const mapped = mapBackendEventResult(result, chId);
expect(mapped.targetChannelId).toBe(chId);
}),
{ numRuns: 100 },
);
});
});
// Feature: multi-cli-backend, Property 10: Session ID storage after backend execution
// **Validates: Requirements 10.4**
describe("Property 10: Session ID storage after backend execution", () => {
it("stores sessionId in SessionManager when BackendEventResult has a sessionId", () => {
fc.assert(
fc.property(
fc.string({ minLength: 1, maxLength: 50 }),
fc.string({ minLength: 1, maxLength: 100 }),
(chId, sessionId) => {
const sessionManager = new SessionManager();
const backendResult: BackendEventResult = {
responseText: "some response",
sessionId,
isError: false,
};
// Simulate what AgentRuntime.processMessage does after backend execution
if (backendResult.sessionId && chId) {
sessionManager.setSessionId(chId, backendResult.sessionId);
}
expect(sessionManager.getSessionId(chId)).toBe(sessionId);
},
),
{ numRuns: 100 },
);
});
it("does not update SessionManager when sessionId is undefined", () => {
fc.assert(
fc.property(
fc.string({ minLength: 1, maxLength: 50 }),
(chId) => {
const sessionManager = new SessionManager();
const backendResult: BackendEventResult = {
responseText: "some response",
sessionId: undefined,
isError: false,
};
// Simulate what AgentRuntime.processMessage does after backend execution
if (backendResult.sessionId && chId) {
sessionManager.setSessionId(chId, backendResult.sessionId);
}
expect(sessionManager.getSessionId(chId)).toBeUndefined();
},
),
{ numRuns: 100 },
);
});
});