NODEDC_1C/llm_normalizer/backend/tests/assistantOrganizationScopeR...

216 lines
7.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
});
});