246 lines
11 KiB
TypeScript
246 lines
11 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import {
|
||
createEmptyAddressNavigationState,
|
||
evolveAddressNavigationStateWithAssistantItem,
|
||
normalizeAddressNavigationState
|
||
} from "../src/services/addressNavigationState";
|
||
|
||
describe("address navigation state", () => {
|
||
it("creates default empty state", () => {
|
||
const state = createEmptyAddressNavigationState("asst-1", "2026-04-12T10:00:00.000Z");
|
||
expect(state.schema_version).toBe("address_navigation_state_v1");
|
||
expect(state.session_id).toBe("asst-1");
|
||
expect(state.result_sets).toEqual([]);
|
||
expect(state.navigation_history).toEqual([]);
|
||
expect(state.session_context.active_result_set_id).toBeNull();
|
||
});
|
||
|
||
it("captures result_set and focus from address assistant turn", () => {
|
||
const base = createEmptyAddressNavigationState("asst-2", "2026-04-12T10:00:00.000Z");
|
||
const assistantItem = {
|
||
message_id: "msg-a1",
|
||
session_id: "asst-2",
|
||
role: "assistant",
|
||
text: [
|
||
"Топ-6 заказчиков по сумме поступлений:",
|
||
"1. Группа | сумма: 12093465 | операций: 13",
|
||
"4. Гамма-мебель, ООО | сумма: 471000 | операций: 2"
|
||
].join("\n"),
|
||
reply_type: "factual",
|
||
created_at: "2026-04-12T10:00:10.000Z",
|
||
trace_id: "address-123",
|
||
debug: {
|
||
detected_mode: "address_query",
|
||
detected_intent: "customer_revenue_and_payments",
|
||
selected_recipe: "address_customer_revenue_and_payments_v1",
|
||
extracted_filters: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
anchor_type: "counterparty",
|
||
anchor_value_resolved: "Гамма-мебель, ООО",
|
||
dialog_continuation_contract_v2: {
|
||
decision: "new_topic"
|
||
}
|
||
}
|
||
} as any;
|
||
|
||
const evolved = evolveAddressNavigationStateWithAssistantItem(base, assistantItem, 2);
|
||
expect(evolved.result_sets.length).toBe(1);
|
||
expect(evolved.result_sets[0]?.result_set_id).toBe("rs-msg-a1");
|
||
expect(evolved.result_sets[0]?.intent).toBe("customer_revenue_and_payments");
|
||
expect(evolved.result_sets[0]?.entity_refs.length).toBeGreaterThan(0);
|
||
expect(evolved.session_context.active_result_set_id).toBe("rs-msg-a1");
|
||
expect(evolved.session_context.active_focus_object?.label).toBe("Гамма-мебель, ООО");
|
||
expect(evolved.navigation_history[0]?.action).toBe("open");
|
||
});
|
||
|
||
it("tracks drilldown event for follow-up continuation turn", () => {
|
||
const initial = normalizeAddressNavigationState(
|
||
{
|
||
schema_version: "address_navigation_state_v1",
|
||
session_id: "asst-3",
|
||
updated_at: "2026-04-12T10:00:00.000Z",
|
||
session_context: {
|
||
active_result_set_id: "rs-prev",
|
||
active_focus_object: null,
|
||
last_confirmed_route: "address_customer_revenue_and_payments_v1",
|
||
date_scope: {
|
||
as_of_date: null,
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
organization_scope: null
|
||
},
|
||
result_sets: [],
|
||
navigation_history: []
|
||
} as any,
|
||
"asst-3"
|
||
);
|
||
const assistantItem = {
|
||
message_id: "msg-a2",
|
||
session_id: "asst-3",
|
||
role: "assistant",
|
||
text: "Собран список договоров по контрагенту Гамма-мебель, ООО.",
|
||
reply_type: "factual",
|
||
created_at: "2026-04-12T10:02:00.000Z",
|
||
trace_id: "address-456",
|
||
debug: {
|
||
detected_mode: "address_query",
|
||
detected_intent: "list_contracts_by_counterparty",
|
||
selected_recipe: "address_contracts_by_counterparty_v1",
|
||
extracted_filters: {
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31",
|
||
counterparty: "Гамма-мебель, ООО"
|
||
},
|
||
anchor_type: "counterparty",
|
||
anchor_value_resolved: "Гамма-мебель, ООО",
|
||
dialog_continuation_contract_v2: {
|
||
decision: "continue_previous"
|
||
}
|
||
}
|
||
} as any;
|
||
|
||
const evolved = evolveAddressNavigationStateWithAssistantItem(initial, assistantItem, 4);
|
||
expect(evolved.navigation_history.length).toBe(1);
|
||
expect(evolved.navigation_history[0]?.action).toBe("drilldown");
|
||
expect(evolved.navigation_history[0]?.source_result_set_id).toBe("rs-prev");
|
||
expect(evolved.session_context.active_result_set_id).toBe("rs-msg-a2");
|
||
expect(evolved.session_context.active_focus_object?.object_type).toBe("counterparty");
|
||
expect(evolved.session_context.date_scope.period_from).toBe("2020-01-01");
|
||
expect(evolved.session_context.date_scope.period_to).toBe("2020-12-31");
|
||
});
|
||
|
||
it("captures item focus from inventory answers when no anchor is materialized", () => {
|
||
const base = createEmptyAddressNavigationState("asst-4", "2026-04-12T10:00:00.000Z");
|
||
const assistantItem = {
|
||
message_id: "msg-a3",
|
||
session_id: "asst-4",
|
||
role: "assistant",
|
||
text: "Собран подтвержденный закупочный след по товару Диван трехместный до 14.04.2026.\n\n1. Авансовый отчет 00000000004 от 24.08.2018 12:00:04 | дата: 24.08.2018 | сумма: 34.490,00 ₽ | склад: Основной склад",
|
||
reply_type: "factual",
|
||
created_at: "2026-04-12T10:03:00.000Z",
|
||
trace_id: "address-789",
|
||
debug: {
|
||
detected_mode: "address_query",
|
||
detected_intent: "inventory_purchase_provenance_for_item",
|
||
selected_recipe: "address_inventory_purchase_provenance_for_item_v1",
|
||
extracted_filters: {
|
||
item: "Диван трехместный",
|
||
warehouse: "Основной склад",
|
||
as_of_date: "2026-04-14"
|
||
},
|
||
anchor_type: "unknown",
|
||
anchor_value_resolved: null,
|
||
anchor_value_raw: null,
|
||
dialog_continuation_contract_v2: {
|
||
decision: "new_topic"
|
||
}
|
||
}
|
||
} as any;
|
||
|
||
const evolved = evolveAddressNavigationStateWithAssistantItem(base, assistantItem, 3);
|
||
expect(evolved.session_context.active_focus_object?.label).toBe("Диван трехместный");
|
||
expect(evolved.session_context.active_focus_object?.provenance_result_set_id).toBe("rs-msg-a3");
|
||
});
|
||
it("derives single organization scope from inventory answer text when filters omit organization", () => {
|
||
const base = createEmptyAddressNavigationState("asst-5", "2026-04-12T10:00:00.000Z");
|
||
const assistantItem = {
|
||
message_id: "msg-a4",
|
||
session_id: "asst-5",
|
||
role: "assistant",
|
||
text: [
|
||
"На 31.05.2020 на складе подтверждено 1 позиция.",
|
||
"1. Пуф арий | склад: Основной склад | количество: 1,000 | стоимость: 6.490,00 ₽ | организация: ООО Альтернатива Плюс | дата строки: 2020-05-31T23:59:59Z"
|
||
].join("\n"),
|
||
reply_type: "factual",
|
||
created_at: "2026-04-12T10:04:00.000Z",
|
||
trace_id: "address-790",
|
||
debug: {
|
||
detected_mode: "address_query",
|
||
detected_intent: "inventory_on_hand_as_of_date",
|
||
selected_recipe: "address_inventory_on_hand_as_of_date_v1",
|
||
extracted_filters: {
|
||
as_of_date: "2020-05-31"
|
||
},
|
||
anchor_type: "unknown",
|
||
anchor_value_resolved: null,
|
||
anchor_value_raw: null,
|
||
dialog_continuation_contract_v2: {
|
||
decision: "new_topic"
|
||
}
|
||
}
|
||
} as any;
|
||
|
||
const evolved = evolveAddressNavigationStateWithAssistantItem(base, assistantItem, 4);
|
||
expect(evolved.result_sets[0]?.type).toBe("inventory_snapshot");
|
||
expect(evolved.result_sets[0]?.filters.organization).toBe("ООО Альтернатива Плюс");
|
||
expect(evolved.result_sets[0]?.entity_refs[0]?.entity_type).toBe("item");
|
||
expect(evolved.result_sets[0]?.entity_refs[0]?.value).toBe("Пуф арий");
|
||
expect(evolved.session_context.organization_scope).toBe("ООО Альтернатива Плюс");
|
||
expect(evolved.session_context.active_focus_object).toBeNull();
|
||
});
|
||
it("resets stale focus and date scope on explicit new topic turn", () => {
|
||
const initial = normalizeAddressNavigationState(
|
||
{
|
||
schema_version: "address_navigation_state_v1",
|
||
session_id: "asst-6",
|
||
updated_at: "2026-04-12T10:00:00.000Z",
|
||
session_context: {
|
||
active_result_set_id: "rs-prev",
|
||
active_focus_object: {
|
||
object_type: "counterparty",
|
||
object_id: "counterparty:chapurnov",
|
||
label: "Чепурнов",
|
||
provenance_result_set_id: "rs-prev",
|
||
selected_at: "2026-04-12T09:59:00.000Z"
|
||
},
|
||
last_confirmed_route: "address_documents_by_counterparty_v1",
|
||
date_scope: {
|
||
as_of_date: null,
|
||
period_from: "2017-01-01",
|
||
period_to: "2017-12-31"
|
||
},
|
||
organization_scope: "ООО Альтернатива Плюс"
|
||
},
|
||
result_sets: [],
|
||
navigation_history: []
|
||
} as any,
|
||
"asst-6"
|
||
);
|
||
const assistantItem = {
|
||
message_id: "msg-a5",
|
||
session_id: "asst-6",
|
||
role: "assistant",
|
||
text: "На 16.04.2026 на складе подтверждено 11 позиций.",
|
||
reply_type: "factual",
|
||
created_at: "2026-04-12T10:05:00.000Z",
|
||
trace_id: "address-791",
|
||
debug: {
|
||
detected_mode: "address_query",
|
||
detected_intent: "inventory_on_hand_as_of_date",
|
||
selected_recipe: "address_inventory_on_hand_as_of_date_v1",
|
||
extracted_filters: {
|
||
as_of_date: "2026-04-16",
|
||
organization: "ООО Альтернатива Плюс"
|
||
},
|
||
anchor_type: "organization",
|
||
anchor_value_resolved: "ООО Альтернатива Плюс",
|
||
dialog_continuation_contract_v2: {
|
||
decision: "new_topic"
|
||
}
|
||
}
|
||
} as any;
|
||
|
||
const evolved = evolveAddressNavigationStateWithAssistantItem(initial, assistantItem, 5);
|
||
expect(evolved.session_context.active_result_set_id).toBe("rs-msg-a5");
|
||
expect(evolved.session_context.active_focus_object?.label).toBe("ООО Альтернатива Плюс");
|
||
expect(evolved.session_context.date_scope.as_of_date).toBe("2026-04-16");
|
||
expect(evolved.session_context.date_scope.period_from).toBeNull();
|
||
expect(evolved.session_context.date_scope.period_to).toBeNull();
|
||
expect(evolved.session_context.last_confirmed_route).toBe("address_inventory_on_hand_as_of_date_v1");
|
||
expect(evolved.navigation_history[0]?.action).toBe("open");
|
||
});
|
||
});
|