import { describe, expect, it, vi } from "vitest"; import { buildAssistantTurnRuntimeDeps } from "../src/services/assistantTurnRuntimeDepsAdapter"; describe("assistant turn runtime deps adapter", () => { it("builds runtime deps with service wrappers and static flags/defaults", async () => { const sessions = { ensureSession: vi.fn(() => ({ session_id: "asst-1" })), appendItem: vi.fn(), getSession: vi.fn(() => ({ session_id: "asst-1" })), setInvestigationState: vi.fn() }; const sessionLogger = { persistSession: vi.fn() }; const normalizerService = { normalize: vi.fn(async () => ({ trace_id: "trace-1" })) }; const dataLayer = { executeRouteRuntime: vi.fn(async () => ({ status: "ok" })) }; const addressQueryService = { tryHandle: vi.fn(async () => ({ response_type: "READY" })) }; const logEvent = vi.fn(); const helperFn = vi.fn(() => "ok"); const deps = buildAssistantTurnRuntimeDeps({ sessions, sessionLogger, normalizerService, dataLayer, addressQueryService, chatClient: { kind: "chat" }, messageIdFactory: () => "msg-1", nowIso: () => "2026-04-11T00:00:00.000Z", defaultApiKey: "api-key", logEvent, flags: { featureAssistantAddressQueryV1: true, featureAddressLlmPredecomposeV1: true, featureInvestigationStateV1: true, featureStateFollowupBindingV1: false, featureContractsV11: true, featureAnswerPolicyV11: true, featureProblemCentricAnswerV1: true, featureLifecycleAnswerV1: false }, defaults: { defaultModel: "gpt-5", defaultBaseUrl: "http://localhost" }, helpers: { compactWhitespace: helperFn } as any }); deps.ensureSession("asst-1"); deps.appendItem("asst-1", { role: "assistant" } as any); deps.getSession("asst-1"); deps.persistSession({ session_id: "asst-1" } as any); deps.setInvestigationState("asst-1", { scope: "x" }); await deps.normalize({ user_message: "q" }); await deps.executeRouteRuntime("store_canonical", "fragment"); await deps.tryAddressQueryHandle("message", { analysisDateHint: "2020-07-31" }); deps.logEvent({ event: "ok" }); expect(sessions.ensureSession).toHaveBeenCalledWith("asst-1"); expect(sessions.appendItem).toHaveBeenCalledWith("asst-1", { role: "assistant" }); expect(sessions.getSession).toHaveBeenCalledWith("asst-1"); expect(sessionLogger.persistSession).toHaveBeenCalledWith({ session_id: "asst-1" }); expect(sessions.setInvestigationState).toHaveBeenCalledWith("asst-1", { scope: "x" }); expect(normalizerService.normalize).toHaveBeenCalledWith({ user_message: "q" }); expect(dataLayer.executeRouteRuntime).toHaveBeenCalledWith("store_canonical", "fragment", undefined); expect(addressQueryService.tryHandle).toHaveBeenCalledWith("message", { analysisDateHint: "2020-07-31" }); expect(logEvent).toHaveBeenCalledWith({ event: "ok" }); expect(deps.featureContractsV11).toBe(true); expect(deps.featureLifecycleAnswerV1).toBe(false); expect(deps.defaultModel).toBe("gpt-5"); expect(deps.defaultBaseUrl).toBe("http://localhost"); expect(deps.defaultApiKey).toBe("api-key"); }); it("preserves helper functions in merged deps payload", () => { const helperCompactWhitespace = vi.fn((value: unknown) => String(value ?? "").trim()); const deps = buildAssistantTurnRuntimeDeps({ sessions: { ensureSession: () => null, appendItem: () => {}, getSession: () => null, setInvestigationState: () => {} }, sessionLogger: { persistSession: () => {} }, normalizerService: { normalize: async () => ({}) }, dataLayer: { executeRouteRuntime: async () => ({}) }, addressQueryService: { tryHandle: async () => null }, chatClient: {}, messageIdFactory: () => "msg-2", nowIso: () => "2026-04-11T00:00:00.000Z", defaultApiKey: "", logEvent: () => {}, flags: { featureAssistantAddressQueryV1: false, featureAddressLlmPredecomposeV1: false, featureInvestigationStateV1: false, featureStateFollowupBindingV1: false, featureContractsV11: false, featureAnswerPolicyV11: false, featureProblemCentricAnswerV1: false, featureLifecycleAnswerV1: false }, defaults: { defaultModel: "model", defaultBaseUrl: "base" }, helpers: { compactWhitespace: helperCompactWhitespace } as any }); expect(deps.compactWhitespace(" value ")).toBe("value"); expect(helperCompactWhitespace).toHaveBeenCalledWith(" value "); }); it("preserves method context for stateful service instances", async () => { class SessionStoreLike { public calls: string[] = []; public ensureSession(sessionId: string) { this.calls.push(`ensure:${sessionId}`); return { session_id: sessionId } as any; } public appendItem(sessionId: string) { this.calls.push(`append:${sessionId}`); } public getSession(sessionId: string) { this.calls.push(`get:${sessionId}`); return null; } public setInvestigationState(sessionId: string) { this.calls.push(`state:${sessionId}`); return null; } } class SessionLoggerLike { public persisted = 0; public persistSession() { this.persisted += 1; } } class NormalizerLike { public normalized = 0; public async normalize() { this.normalized += 1; return {}; } } const sessions = new SessionStoreLike(); const sessionLogger = new SessionLoggerLike(); const normalizerService = new NormalizerLike(); const deps = buildAssistantTurnRuntimeDeps({ sessions, sessionLogger, normalizerService, dataLayer: { executeRouteRuntime: async () => ({}) }, addressQueryService: { tryHandle: async () => null }, chatClient: {}, messageIdFactory: () => "msg-ctx", nowIso: () => "2026-04-11T00:00:00.000Z", defaultApiKey: "", logEvent: () => {}, flags: { featureAssistantAddressQueryV1: false, featureAddressLlmPredecomposeV1: false, featureInvestigationStateV1: false, featureStateFollowupBindingV1: false, featureContractsV11: false, featureAnswerPolicyV11: false, featureProblemCentricAnswerV1: false, featureLifecycleAnswerV1: false }, defaults: { defaultModel: "model", defaultBaseUrl: "base" }, helpers: { compactWhitespace: (value: unknown) => String(value ?? "") } as any }); deps.ensureSession("asst-ctx"); deps.appendItem("asst-ctx", { role: "user" } as any); deps.getSession("asst-ctx"); deps.setInvestigationState("asst-ctx", null); deps.persistSession({ session_id: "asst-ctx" } as any); await deps.normalize({ user_message: "ctx" } as any); expect(sessions.calls).toEqual(["ensure:asst-ctx", "append:asst-ctx", "get:asst-ctx", "state:asst-ctx"]); expect(sessionLogger.persisted).toBe(1); expect(normalizerService.normalized).toBe(1); }); });