163 lines
7.6 KiB
TypeScript
163 lines
7.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { resolveAddressIntent } from "../src/services/addressIntentResolver";
|
||
import { classifyAddressQueryShape } from "../src/services/addressQueryShapeClassifier";
|
||
import { extractAddressFilters } from "../src/services/addressFilterExtractor";
|
||
import { AddressQueryService } from "../src/services/addressQueryService";
|
||
import { buildAddressRecipePlan, selectAddressRecipe } from "../src/services/addressRecipeCatalog";
|
||
|
||
describe("address query shape classifier", () => {
|
||
it("classifies explain question as deep-shape", () => {
|
||
const result = classifyAddressQueryShape("Why VAT chain does not match?");
|
||
expect(result.shape).toBe("EXPLAIN_OR_REASON");
|
||
expect(result.confidence).toBe("high");
|
||
});
|
||
|
||
it("classifies aggregate lookup question", () => {
|
||
const result = classifyAddressQueryShape("who owes us today?");
|
||
expect(result.shape).toBe("AGGREGATE_LOOKUP");
|
||
});
|
||
|
||
it("classifies compound factual question", () => {
|
||
const result = classifyAddressQueryShape("who owes us and who we owe today?");
|
||
expect(result.shape).toBe("COMPOUND_FACTUAL_QUERY");
|
||
});
|
||
});
|
||
|
||
describe("address intent resolver expansion (M2.3a)", () => {
|
||
it("resolves documents by counterparty intent", () => {
|
||
const result = resolveAddressIntent("show documents by counterparty Alfa from 2020-07-01 to 2020-07-31");
|
||
expect(result.intent).toBe("list_documents_by_counterparty");
|
||
});
|
||
|
||
it("resolves bank operations by counterparty intent", () => {
|
||
const result = resolveAddressIntent("show bank operations by counterparty Alfa");
|
||
expect(result.intent).toBe("bank_operations_by_counterparty");
|
||
});
|
||
|
||
it("resolves documents forming balance intent", () => {
|
||
const result = resolveAddressIntent("which documents form balance for account 62 as of 2020-07-31");
|
||
expect(result.intent).toBe("documents_forming_balance");
|
||
});
|
||
});
|
||
|
||
describe("address filter extraction for balance drilldown", () => {
|
||
it("defaults as_of_date for documents_forming_balance when date is omitted", () => {
|
||
const result = extractAddressFilters("which documents form balance for account 62", "documents_forming_balance");
|
||
expect(result.extracted_filters.account).toBe("62");
|
||
expect(result.extracted_filters.as_of_date).toBeDefined();
|
||
expect(result.missing_required_filters).toEqual([]);
|
||
});
|
||
|
||
it("cuts period tail from counterparty anchor", () => {
|
||
const result = extractAddressFilters(
|
||
"Покажи документы по контрагенту test_cp с 2020-07-01 по 2020-07-31",
|
||
"list_documents_by_counterparty"
|
||
);
|
||
expect(result.extracted_filters.counterparty).toBe("test_cp");
|
||
expect(result.extracted_filters.period_from).toBe("2020-07-01");
|
||
expect(result.extracted_filters.period_to).toBe("2020-07-31");
|
||
});
|
||
|
||
it("cuts all-time tail from counterparty anchor and skips 90-day default window", () => {
|
||
const result = extractAddressFilters(
|
||
"Покажи документы по контрагенту тестовый за все время",
|
||
"list_documents_by_counterparty"
|
||
);
|
||
expect(result.extracted_filters.counterparty).toBe("тестовый");
|
||
expect(result.extracted_filters.period_from).toBeUndefined();
|
||
expect(result.extracted_filters.period_to).toBeUndefined();
|
||
expect(result.warnings).not.toContain("period_defaulted_last_90_days");
|
||
});
|
||
});
|
||
|
||
describe("address query limited taxonomy and stage diagnostics", () => {
|
||
it("returns missing_anchor for open items without concrete counterparty/contract anchor", async () => {
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("show open items by contract");
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
|
||
expect(result?.debug.limited_reason_category).toBe("missing_anchor");
|
||
expect(result?.debug.mcp_call_status).toBe("skipped");
|
||
});
|
||
|
||
it("returns unsupported for not-implemented contract document list intent", async () => {
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("show documents by contract 15/24");
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
|
||
expect(result?.debug.limited_reason_category).toBe("unsupported");
|
||
expect(result?.debug.mcp_call_status).toBe("skipped");
|
||
});
|
||
|
||
it("includes resolver and row-stage diagnostics", async () => {
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle("which documents form balance for account 62 as of 2020-07-31");
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
|
||
|
||
expect(result?.debug.anchor_type).toBe("account");
|
||
expect(result?.debug.rows_fetched).toBeTypeOf("number");
|
||
expect(result?.debug.raw_rows_received).toBeTypeOf("number");
|
||
expect(result?.debug.rows_after_account_scope).toBeTypeOf("number");
|
||
expect(result?.debug.rows_materialized).toBeTypeOf("number");
|
||
expect(result?.debug.rows_after_recipe_filter).toBeTypeOf("number");
|
||
expect(result?.debug.rows_matched).toBeTypeOf("number");
|
||
expect(["strict", "preferred"]).toContain(result?.debug.account_scope_mode);
|
||
expect(result?.debug.account_scope_fallback_applied).toBeTypeOf("boolean");
|
||
expect(result?.debug.mcp_call_status_legacy).toBeDefined();
|
||
expect(result?.debug.match_failure_stage).toBeDefined();
|
||
|
||
expect([
|
||
"no_raw_rows",
|
||
"raw_rows_received_but_not_materialized",
|
||
"materialized_but_not_anchor_matched",
|
||
"materialized_but_filtered_out_by_recipe",
|
||
"materialized_but_not_matched",
|
||
"matched_non_empty"
|
||
]).toContain(result?.debug.mcp_call_status);
|
||
|
||
expect(result?.debug.raw_row_keys_sample).toBeDefined();
|
||
expect(result?.debug.materialization_drop_reason).toBeDefined();
|
||
expect(result?.debug.account_scope_fields_checked).toBeDefined();
|
||
expect(result?.debug.account_scope_match_strategy).toBe("account_code_regex_plus_alias_map_v1");
|
||
expect(result?.debug.account_scope_drop_reason).toBeDefined();
|
||
});
|
||
});
|
||
|
||
describe("address recipe catalog counterparty filtering", () => {
|
||
it("boosts limit for all-time counterparty queries", () => {
|
||
const filters = extractAddressFilters(
|
||
"Покажи документы по контрагенту тестовый за все время",
|
||
"list_documents_by_counterparty"
|
||
).extracted_filters;
|
||
const selected = selectAddressRecipe("list_documents_by_counterparty", filters);
|
||
expect(selected.selected_recipe).toBeTruthy();
|
||
const plan = buildAddressRecipePlan(selected.selected_recipe!, filters);
|
||
|
||
expect(plan.limit).toBe(200);
|
||
});
|
||
|
||
it("boosts limit for english all-time counterparty queries", () => {
|
||
const filters = extractAddressFilters(
|
||
"show documents by counterparty test_cp for all time",
|
||
"list_documents_by_counterparty"
|
||
).extracted_filters;
|
||
const selected = selectAddressRecipe("list_documents_by_counterparty", filters);
|
||
expect(selected.selected_recipe).toBeTruthy();
|
||
const plan = buildAddressRecipePlan(selected.selected_recipe!, filters);
|
||
|
||
expect(plan.limit).toBe(200);
|
||
});
|
||
|
||
it("cuts english all-time tail from counterparty anchor", () => {
|
||
const result = extractAddressFilters(
|
||
"show documents by counterparty test_cp for all time",
|
||
"list_documents_by_counterparty"
|
||
);
|
||
expect(result.extracted_filters.counterparty).toBe("test_cp");
|
||
expect(result.extracted_filters.period_from).toBeUndefined();
|
||
expect(result.extracted_filters.period_to).toBeUndefined();
|
||
expect(result.warnings).not.toContain("period_defaulted_last_90_days");
|
||
});
|
||
});
|