import { describe, expect, it } from "vitest"; import { buildAssistantMcpDiscoveryTurnInput } from "../src/services/assistantMcpDiscoveryTurnInputAdapter"; describe("assistant MCP discovery turn input adapter", () => { it("maps unsupported assistant turn meaning into a discovery-ready value-flow input", () => { const result = buildAssistantMcpDiscoveryTurnInput({ assistantTurnMeaning: { schema_version: "assistant_turn_meaning_v1", asked_domain_family: "counterparty", asked_action_family: "counterparty_value_or_turnover", unsupported_but_understood_family: "counterparty_value_or_turnover", stale_replay_forbidden: true, explicit_entity_candidates: [{ type: "counterparty", value: "SVK", source: "current_turn_loose_entity_tail" }] }, predecomposeContract: { entities: { counterparty: "Группа СВК", organization: "Альтернатива" }, period: { period_from: "2020-01-01", period_to: "2020-12-31" } } }); expect(result.adapter_status).toBe("ready"); expect(result.should_run_discovery).toBe(true); expect(result.semantic_data_need).toBe("counterparty value-flow evidence"); expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK", "Группа СВК"]); expect(result.turn_meaning_ref?.explicit_organization_scope).toBe("Альтернатива"); expect(result.turn_meaning_ref?.explicit_date_scope).toBe("2020"); expect(result.turn_meaning_ref?.stale_replay_forbidden).toBe(true); }); it("bootstraps lifecycle discovery from raw user wording and predecompose scope", () => { const result = buildAssistantMcpDiscoveryTurnInput({ userMessage: "Сколько лет мы работаем с Группа СВК?", predecomposeContract: { entities: { counterparty: "Группа СВК" }, period: { period_from: null, period_to: null, as_of_date: null } } }); expect(result.adapter_status).toBe("ready"); expect(result.source_signal).toBe("predecompose_contract"); expect(result.semantic_data_need).toBe("counterparty lifecycle evidence"); expect(result.turn_meaning_ref).toMatchObject({ asked_domain_family: "counterparty_lifecycle", asked_action_family: "activity_duration", explicit_entity_candidates: ["Группа СВК"], unsupported_but_understood_family: "counterparty_lifecycle", stale_replay_forbidden: true }); expect(result.reason_codes).toContain("mcp_discovery_lifecycle_signal_detected"); }); it("bootstraps value-flow discovery from raw turnover wording when no exact route owns it", () => { const result = buildAssistantMcpDiscoveryTurnInput({ userMessage: "какой денежный поток был у Группа СВК за 2020 год?", predecomposeContract: { entities: { counterparty: "Группа СВК" }, period: { period_from: "2020-01-01", period_to: "2020-12-31" } } }); expect(result.adapter_status).toBe("ready"); expect(result.should_run_discovery).toBe(true); expect(result.semantic_data_need).toBe("counterparty value-flow evidence"); expect(result.turn_meaning_ref).toMatchObject({ asked_domain_family: "counterparty_value", asked_action_family: "turnover", explicit_entity_candidates: ["Группа СВК"], explicit_date_scope: "2020", unsupported_but_understood_family: "counterparty_value_or_turnover", stale_replay_forbidden: true }); expect(result.reason_codes).toContain("mcp_discovery_value_flow_signal_detected"); }); it("treats value-flow organization-shaped target as entity candidate when counterparty is absent", () => { const result = buildAssistantMcpDiscoveryTurnInput({ userMessage: "какой денежный поток был у Группа СВК за 2020 год?", predecomposeContract: { entities: { organization: "Группа СВК" }, period: { period_from: "2020-01-01", period_to: "2020-12-31" } } }); expect(result.adapter_status).toBe("ready"); expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["Группа СВК"]); expect(result.turn_meaning_ref?.explicit_organization_scope).toBeUndefined(); }); it("does not activate discovery for supported exact current-turn intent", () => { const result = buildAssistantMcpDiscoveryTurnInput({ assistantTurnMeaning: { asked_domain_family: "counterparty", asked_action_family: "list_documents", explicit_intent_candidate: "list_documents_by_counterparty", explicit_entity_candidates: [{ value: "SVK" }], stale_replay_forbidden: false } }); expect(result.adapter_status).toBe("not_applicable"); expect(result.should_run_discovery).toBe(false); expect(result.turn_meaning_ref).toBeNull(); expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn"); }); it("never serializes object candidates as [object Object]", () => { const result = buildAssistantMcpDiscoveryTurnInput({ assistantTurnMeaning: { asked_domain_family: "counterparty", asked_action_family: "counterparty_value_or_turnover", unsupported_but_understood_family: "counterparty_value_or_turnover", explicit_entity_candidates: [{ type: "counterparty", value: "SVK" }] } }); expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK"]); expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain("[object Object]"); }); });