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 = 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 }, ); }); });