NODEDC_1C/llm_normalizer/backend/tests/addressTruthGatePolicy.test.ts

102 lines
4.0 KiB
TypeScript
Raw 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 } from "vitest";
import { resolveAddressTruthGate } from "../src/services/addressTruthGatePolicy";
describe("address truth gate policy", () => {
it("treats factual exact no-match as confirmed negative with reusable root scope", () => {
const gate = resolveAddressTruthGate({
intent: "open_items_by_counterparty_or_contract",
filters: {
account: "60",
period_from: "2022-08-01",
period_to: "2022-08-31"
},
selectedRecipe: "open_items_by_counterparty_or_contract",
rowsMatched: 0,
limitedReasonCategory: "empty_match",
runtimeReadiness: "LIVE_QUERYABLE_WITH_LIMITS",
reasons: ["open_items_account_exact_negative_response"],
routeExpectationStatus: "matched",
replyType: "factual"
});
expect(gate.truth_gate_status).toBe("full_confirmed");
expect(gate.carryover_eligibility).toBe("root_only");
expect(gate.blocked_or_limited_explanation).toBeNull();
expect(gate.reason_codes).toContain("limited_category_empty_match");
});
it("keeps open-items movement fallback partial even when heuristic rows are found", () => {
const gate = resolveAddressTruthGate({
intent: "open_items_by_counterparty_or_contract",
filters: {
account: "60",
period_from: "2020-08-01",
period_to: "2020-08-31",
organization: "ООО Альтернатива Плюс"
},
selectedRecipe: "address_open_items_by_party_or_contract_v1",
rowsMatched: 8,
runtimeReadiness: "LIVE_QUERYABLE_WITH_LIMITS",
reasons: [
"confirmed_balance_unavailable_fallback_to_heuristic_candidates",
"open_items_account_query_override_to_movements"
],
routeExpectationStatus: "matched",
replyType: "factual"
});
expect(gate.truth_gate_status).toBe("partial_supported");
expect(gate.carryover_eligibility).toBe("root_only");
expect(gate.blocked_or_limited_explanation).toBe("evidence_or_coverage_is_partial");
expect(gate.reason_codes).toContain("confirmed_balance_unavailable_fallback_to_heuristic_candidates");
});
it("keeps selected-item limited answers object-scoped", () => {
const gate = resolveAddressTruthGate({
intent: "inventory_sale_trace_for_item",
filters: {
item: "Рабочая станция"
},
semanticFrame: {
scope_kind: "selected_object_scope",
anchor_kind: "selected_object",
anchor_value: "Рабочая станция",
date_scope_kind: "explicit",
date_basis_hint: "explicit_as_of_date",
self_scope_detected: false,
selected_object_scope_detected: true
},
selectedRecipe: "inventory_sale_trace_for_item",
rowsMatched: 0,
limitedReasonCategory: "empty_match",
runtimeReadiness: "LIVE_QUERYABLE_WITH_LIMITS",
limitations: ["no_rows_after_recipe_and_scope_filter"],
routeExpectationStatus: "matched",
replyType: "partial_coverage"
});
expect(gate.truth_gate_status).toBe("partial_supported");
expect(gate.carryover_eligibility).toBe("object_only");
expect(gate.blocked_or_limited_explanation).toBe("evidence_or_coverage_is_partial");
});
it("blocks missing-anchor clarifications instead of pretending they are reusable evidence", () => {
const gate = resolveAddressTruthGate({
intent: "inventory_purchase_provenance_for_item",
selectedRecipe: "inventory_purchase_provenance_for_item",
rowsMatched: 0,
limitedReasonCategory: "missing_anchor",
runtimeReadiness: "LIVE_QUERYABLE_WITH_LIMITS",
missingRequiredFilters: ["item"],
limitations: ["missing_required_filters"],
routeExpectationStatus: "matched",
replyType: "partial_coverage"
});
expect(gate.truth_gate_status).toBe("blocked_missing_anchor");
expect(gate.carryover_eligibility).toBe("none");
expect(gate.blocked_or_limited_explanation).toBe("required_anchor_missing");
expect(gate.reason_codes).toContain("missing_filter_item");
});
});