229 lines
9.6 KiB
TypeScript
229 lines
9.6 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
||
|
||
const { executeAddressMcpQueryMock } = vi.hoisted(() => ({
|
||
executeAddressMcpQueryMock: vi.fn()
|
||
}));
|
||
|
||
vi.mock("../src/services/addressMcpClient", async () => {
|
||
const actual = await vi.importActual<typeof import("../src/services/addressMcpClient")>(
|
||
"../src/services/addressMcpClient"
|
||
);
|
||
return {
|
||
...actual,
|
||
executeAddressMcpQuery: executeAddressMcpQueryMock
|
||
};
|
||
it("clears company-name counterparty noise when a bare organization selection resumes inventory", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 1,
|
||
matched_rows: 1,
|
||
raw_rows: [
|
||
{
|
||
Period: "2026-04-19T23:59:59Z",
|
||
Registrator: "Остатки товаров на складах",
|
||
AccountDt: "41.01",
|
||
AccountKt: "00.00",
|
||
Amount: 148261.67,
|
||
Quantity: 22,
|
||
SubcontoDt1: "Модуль прямоугольый 1400*110*750",
|
||
Warehouse: "Основной склад",
|
||
Organization: 'ООО "Альтернатива Плюс"'
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("АЛЬТЕРНАТИВА", {
|
||
activeOrganization: 'ООО "Альтернатива Плюс"',
|
||
knownOrganizations: ['ООО "Альтернатива Плюс"', "ООО Лайсвуд"],
|
||
followupContext: {
|
||
previous_intent: "inventory_on_hand_as_of_date",
|
||
target_intent: "inventory_on_hand_as_of_date",
|
||
previous_filters: {
|
||
organization: 'ООО "Альтернатива Плюс"',
|
||
as_of_date: "2026-04-19"
|
||
},
|
||
previous_anchor_type: "organization",
|
||
previous_anchor_value: 'ООО "Альтернатива Плюс"'
|
||
}
|
||
});
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.reply_type).toBe("factual");
|
||
expect(result?.debug.detected_intent).toBe("inventory_on_hand_as_of_date");
|
||
expect(result?.debug.extracted_filters?.organization).toBe('ООО "Альтернатива Плюс"');
|
||
expect(result?.debug.extracted_filters?.counterparty).toBeUndefined();
|
||
expect(result?.debug.reasons).toContain("counterparty_cleared_from_referential_organization_scope");
|
||
expect(String(result?.reply_text ?? "")).toContain("Модуль прямоугольый 1400*110*750");
|
||
});
|
||
});
|
||
|
||
import { AddressQueryService } from "../src/services/addressQueryService";
|
||
|
||
afterEach(() => {
|
||
executeAddressMcpQueryMock.mockReset();
|
||
vi.restoreAllMocks();
|
||
});
|
||
|
||
describe("implicit organization stock scope", () => {
|
||
it("uses llm semantic hints to ground informal organization wording without turning it into warehouse anchor", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 1,
|
||
matched_rows: 1,
|
||
raw_rows: [
|
||
{
|
||
Period: "2026-04-15T23:59:59Z",
|
||
Registrator: "Остатки товаров на складах",
|
||
AccountDt: "41.01",
|
||
AccountKt: "00.00",
|
||
Amount: 148261.67,
|
||
Quantity: 22,
|
||
SubcontoDt1: "Модуль прямоугольый 1400*110*750",
|
||
Warehouse: "Основной склад",
|
||
Organization: 'ООО "Альтернатива Плюс"'
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("что на складе конторы альтернатива", {
|
||
llmSemanticHints: {
|
||
scope_target_kind: "organization",
|
||
scope_target_text: "Альтернатива",
|
||
date_scope_kind: "implicit_current",
|
||
self_scope_detected: false,
|
||
selected_object_scope_detected: false
|
||
}
|
||
});
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.reply_type).toBe("factual");
|
||
expect(result?.response_type).toBe("FACTUAL_LIST");
|
||
expect(result?.debug.detected_intent).toBe("inventory_on_hand_as_of_date");
|
||
expect(result?.debug.selected_recipe).toBe("address_inventory_on_hand_as_of_date_v1");
|
||
expect(result?.debug.mcp_call_status).toBe("matched_non_empty");
|
||
expect(result?.debug.extracted_filters?.organization).toBe("Альтернатива");
|
||
expect(result?.debug.extracted_filters?.warehouse).toBeUndefined();
|
||
expect(result?.debug.semantic_frame?.scope_kind).toBe("explicit_anchor");
|
||
expect(result?.debug.semantic_frame?.anchor_kind).toBe("organization");
|
||
expect(result?.debug.semantic_frame?.anchor_value).toBe("Альтернатива");
|
||
expect(result?.debug.as_of_date_basis).toBe("implicit_current_snapshot");
|
||
expect(String(result?.reply_text ?? "")).toContain("Модуль прямоугольый 1400*110*750");
|
||
});
|
||
|
||
it("re-grounds warehouse-like informal company wording to live organization candidate set", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 1,
|
||
matched_rows: 1,
|
||
raw_rows: [
|
||
{
|
||
Period: "2026-04-15T23:59:59Z",
|
||
Registrator: "Остатки товаров на складах",
|
||
AccountDt: "41.01",
|
||
AccountKt: "00.00",
|
||
Amount: 833.33,
|
||
Quantity: 1,
|
||
SubcontoDt1: "Четки Пост (84*117)",
|
||
Warehouse: "Основной склад",
|
||
Organization: "ООО КОТ ССЫТ ВО ДВОРЕ"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("что на складе конторы ссыт кот", {
|
||
activeOrganization: "ООО КОТ ССЫТ ВО ДВОРЕ",
|
||
knownOrganizations: ["ООО КОТ ССЫТ ВО ДВОРЕ", "ООО Альтернатива Плюс"]
|
||
});
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.reply_type).toBe("factual");
|
||
expect(result?.debug.extracted_filters?.organization).toBe("ООО КОТ ССЫТ ВО ДВОРЕ");
|
||
expect(result?.debug.extracted_filters?.warehouse).toBeUndefined();
|
||
expect(result?.debug.anchor_type).toBe("organization");
|
||
expect(result?.debug.reasons).toContain("warehouse_anchor_regrounded_to_organization_scope");
|
||
expect(result?.debug.reasons).toContain("organization_scope_live_grounding_recovered_rows");
|
||
expect(String(result?.reply_text ?? "")).toContain("Четки Пост (84*117)");
|
||
});
|
||
|
||
it("handles slang stock-state wording as current inventory snapshot for grounded organization scope", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 1,
|
||
matched_rows: 1,
|
||
raw_rows: [
|
||
{
|
||
Period: "2026-04-15T23:59:59Z",
|
||
Registrator: "Остатки товаров на складах",
|
||
AccountDt: "41.01",
|
||
AccountKt: "00.00",
|
||
Amount: 34490,
|
||
Quantity: 1,
|
||
SubcontoDt1: "Диван трехместный",
|
||
Warehouse: "Основной склад",
|
||
Organization: "ООО Альтернатива Плюс"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("чекни плиз чо там на складе альтернативы происходит", {
|
||
activeOrganization: "ООО Альтернатива Плюс",
|
||
knownOrganizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд"]
|
||
});
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.reply_type).toBe("factual");
|
||
expect(result?.debug.detected_intent).toBe("inventory_on_hand_as_of_date");
|
||
expect(result?.debug.selected_recipe).toBe("address_inventory_on_hand_as_of_date_v1");
|
||
expect(result?.debug.extracted_filters?.organization).toBe("ООО Альтернатива Плюс");
|
||
expect(result?.debug.extracted_filters?.warehouse).toBeUndefined();
|
||
expect(result?.debug.as_of_date_basis).toBe("implicit_current_snapshot");
|
||
expect(String(result?.reply_text ?? "")).toContain("Диван трехместный");
|
||
});
|
||
|
||
it("handles short colloquial stock query as current inventory snapshot for grounded organization scope", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 1,
|
||
matched_rows: 1,
|
||
raw_rows: [
|
||
{
|
||
Period: "2026-04-15T23:59:59Z",
|
||
Registrator: "Остатки товаров на складах",
|
||
AccountDt: "41.01",
|
||
AccountKt: "00.00",
|
||
Amount: 6490,
|
||
Quantity: 1,
|
||
SubcontoDt1: "Пуф арий",
|
||
Warehouse: "Основной склад",
|
||
Organization: "ООО Альтернатива Плюс"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("че на складах альтернативы", {
|
||
activeOrganization: "ООО Альтернатива Плюс",
|
||
knownOrganizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд"]
|
||
});
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.reply_type).toBe("factual");
|
||
expect(result?.debug.detected_intent).toBe("inventory_on_hand_as_of_date");
|
||
expect(result?.debug.selected_recipe).toBe("address_inventory_on_hand_as_of_date_v1");
|
||
expect(result?.debug.extracted_filters?.organization).toBe("ООО Альтернатива Плюс");
|
||
expect(result?.debug.extracted_filters?.warehouse).toBeUndefined();
|
||
expect(result?.debug.anchor_type).toBe("organization");
|
||
expect(result?.debug.as_of_date_basis).toBe("implicit_current_snapshot");
|
||
expect(String(result?.reply_text ?? "")).toContain("Пуф арий");
|
||
});
|
||
});
|