216 lines
7.5 KiB
TypeScript
216 lines
7.5 KiB
TypeScript
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();
|
||
});
|
||
});
|