76 lines
2.9 KiB
TypeScript
76 lines
2.9 KiB
TypeScript
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 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");
|
|
});
|
|
});
|