import { describe, expect, it, vi } from "vitest"; import { mergeFollowupContextWithOrganizationScopeRuntime, resolveSessionOrganizationScopeContextRuntime } from "../src/services/assistantOrganizationScopeRuntimeAdapter"; describe("assistant organization scope runtime adapter", () => { it("resolves selected organization from user message and promotes it to active scope", () => { const extractKnownOrganizationsFromHistory = vi.fn(() => ["Org A", "Org B"]); const resolveOrganizationSelectionFromMessage = vi.fn(() => "Org B"); const normalizeOrganizationScopeValue = vi.fn((value: unknown) => typeof value === "string" && value.trim() ? value.trim() : null ); const context = resolveSessionOrganizationScopeContextRuntime({ userMessage: "по Org B покажи", items: [] as any[], extractKnownOrganizationsFromHistory, resolveOrganizationSelectionFromMessage, normalizeOrganizationScopeValue }); expect(context).toEqual({ knownOrganizations: ["Org A", "Org B"], selectedOrganization: "Org B", activeOrganization: "Org B" }); }); it("falls back active organization to last assistant scope when selection is absent", () => { const normalizeOrganizationScopeValue = vi.fn((value: unknown) => typeof value === "string" ? value.trim() : null ); const context = resolveSessionOrganizationScopeContextRuntime({ userMessage: "просто обсуждаем", items: [] as any[], extractKnownOrganizationsFromHistory: () => ["Org A"], resolveOrganizationSelectionFromMessage: () => null, normalizeOrganizationScopeValue }); expect(context).toEqual({ knownOrganizations: ["Org A"], selectedOrganization: null, activeOrganization: "Org A" }); }); it("prefers organization scope from address navigation state when present", () => { const normalizeOrganizationScopeValue = vi.fn((value: unknown) => typeof value === "string" && value.trim() ? value.trim() : null ); const context = resolveSessionOrganizationScopeContextRuntime({ userMessage: "просто продолжай", items: [] as any[], addressNavigationState: { schema_version: "address_navigation_state_v1", session_id: "asst-nav-org", updated_at: "2026-04-15T10:00:00.000Z", session_context: { active_result_set_id: "rs-1", active_focus_object: null, last_confirmed_route: "address_inventory_on_hand_as_of_date_v1", date_scope: { as_of_date: "2020-05-31", period_from: null, period_to: null }, organization_scope: "Org B" }, result_sets: [], navigation_history: [] } as any, extractKnownOrganizationsFromHistory: () => ["Org A"], resolveOrganizationSelectionFromMessage: () => null, normalizeOrganizationScopeValue }); expect(context).toEqual({ knownOrganizations: ["Org B", "Org A"], selectedOrganization: null, activeOrganization: "Org B" }); }); it("prefers continuity-selected organization over stale navigation scope after late switch", () => { const normalizeOrganizationScopeValue = vi.fn((value: unknown) => typeof value === "string" && value.trim() ? value.trim() : null ); const context = resolveSessionOrganizationScopeContextRuntime({ userMessage: "а по этой компании что сейчас на складе?", items: [ { role: "assistant", debug: { assistant_known_organizations: ["ООО Альтернатива Плюс", "РАЙМ"], assistant_active_organization: "РАЙМ", living_chat_selected_organization: "РАЙМ" } } ] as any[], addressNavigationState: { schema_version: "address_navigation_state_v1", session_id: "asst-nav-stale-org", updated_at: "2026-04-19T12:04:44.000Z", session_context: { active_result_set_id: "rs-1", active_focus_object: null, last_confirmed_route: "address_inventory_on_hand_as_of_date_v1", date_scope: { as_of_date: "2026-04-19", period_from: null, period_to: null }, organization_scope: "ООО Альтернатива Плюс" }, result_sets: [], navigation_history: [] } as any, extractKnownOrganizationsFromHistory: () => ["ООО Альтернатива Плюс"], resolveOrganizationSelectionFromMessage: () => null, normalizeOrganizationScopeValue }); expect(context).toEqual({ knownOrganizations: ["ООО Альтернатива Плюс", "РАЙМ"], selectedOrganization: null, activeOrganization: "РАЙМ" }); }); it("reuses assistant continuity authority from prior assistant debug when legacy helpers are empty", () => { const normalizeOrganizationScopeValue = vi.fn((value: unknown) => typeof value === "string" && value.trim() ? value.trim() : null ); const context = resolveSessionOrganizationScopeContextRuntime({ userMessage: "просто продолжай", items: [ { role: "assistant", debug: { assistant_known_organizations: ["Org C", "Org D"], assistant_active_organization: "Org C" } } ] as any[], extractKnownOrganizationsFromHistory: () => [], resolveOrganizationSelectionFromMessage: () => null, normalizeOrganizationScopeValue }); expect(context).toEqual({ knownOrganizations: ["Org C", "Org D"], selectedOrganization: null, activeOrganization: "Org C" }); }); it("merges organization into followup previous filters when organization is missing", () => { const merged = mergeFollowupContextWithOrganizationScopeRuntime({ followupContext: { previous_filters: { period: "2020-07" } }, organization: " Org A ", normalizeOrganizationScopeValue: (value: unknown) => typeof value === "string" && value.trim() ? value.trim() : null, toNonEmptyString: (value: unknown) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null }); expect(merged).toEqual({ previous_filters: { period: "2020-07", organization: "Org A" }, root_filters: { organization: "Org A" } }); }); it("overrides stale organization in followup filters and returns null for empty context without org", () => { const overridden = mergeFollowupContextWithOrganizationScopeRuntime({ followupContext: { previous_filters: { organization: "Org Existing" }, root_filters: { organization: "Org Existing" } }, organization: "Org A", normalizeOrganizationScopeValue: (value: unknown) => typeof value === "string" && value.trim() ? value.trim() : null, toNonEmptyString: (value: unknown) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null }); const empty = mergeFollowupContextWithOrganizationScopeRuntime({ followupContext: null, organization: "", normalizeOrganizationScopeValue: () => null, toNonEmptyString: () => null }); expect((overridden as any).previous_filters.organization).toBe("Org A"); expect((overridden as any).root_filters.organization).toBe("Org A"); expect(empty).toBeNull(); }); });