NODEDC_1C/llm_normalizer/backend/tests/assistantMcpCatalogIndex.te...

292 lines
12 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 { ASSISTANT_MCP_DISCOVERY_PRIMITIVES, buildAssistantMcpDiscoveryPlan } from "../src/services/assistantMcpDiscoveryPolicy";
import {
buildAssistantMcpCatalogIndex,
getAssistantMcpCatalogChainTemplate,
getAssistantMcpCatalogPrimitive,
reviewAssistantMcpDiscoveryPlanAgainstCatalog,
searchAssistantMcpCatalogPrimitivesByDecompositionCandidates,
searchAssistantMcpCatalogPrimitivesByFactAxis,
searchAssistantMcpCatalogPrimitivesByMetadataSurface
} from "../src/services/assistantMcpCatalogIndex";
describe("assistant MCP catalog index", () => {
it("declares a catalog contract for every reviewed discovery primitive", () => {
const index = buildAssistantMcpCatalogIndex();
const primitiveIds = index.primitives.map((entry) => entry.primitive_id);
expect(index.reason_codes).toContain("catalog_covers_all_discovery_primitives");
expect(index.reason_codes).toContain("catalog_chain_templates_reference_reviewed_primitives");
expect(primitiveIds).toEqual([...ASSISTANT_MCP_DISCOVERY_PRIMITIVES]);
for (const entry of index.primitives) {
expect(entry.safe_for_model_planning).toBe(true);
expect(entry.runtime_must_execute).toBe(true);
expect(entry.decomposition_hints.length).toBeGreaterThan(0);
expect(Array.isArray(entry.supported_fact_families)).toBe(true);
expect(Array.isArray(entry.supported_action_families)).toBe(true);
expect(Array.isArray(entry.planning_tags)).toBe(true);
expect(entry.required_axes_any_of.length).toBeGreaterThan(0);
expect(entry.output_fact_kinds.length).toBeGreaterThan(0);
}
expect(index.chain_templates.map((entry) => entry.chain_id)).toEqual([
"metadata_inspection",
"catalog_drilldown",
"entity_resolution",
"document_evidence",
"movement_evidence",
"value_flow",
"value_flow_comparison",
"value_flow_ranking",
"lifecycle"
]);
for (const template of index.chain_templates) {
expect(template.safe_for_model_planning).toBe(true);
expect(template.requires_evidence_gate).toBe(true);
expect(template.semantic_data_need.length).toBeGreaterThan(0);
expect(template.chain_summary.length).toBeGreaterThan(0);
expect(template.fallback_primitives.length).toBeGreaterThan(0);
for (const primitive of template.fallback_primitives) {
expect(primitiveIds).toContain(primitive);
}
}
});
it("exposes reusable chain templates for planner route-fabric selection", () => {
const documentTemplate = getAssistantMcpCatalogChainTemplate("document_evidence");
const movementTemplate = getAssistantMcpCatalogChainTemplate("movement_evidence");
const valueFlowTemplate = getAssistantMcpCatalogChainTemplate("value_flow");
const valueFlowComparisonTemplate = getAssistantMcpCatalogChainTemplate("value_flow_comparison");
const valueFlowRankingTemplate = getAssistantMcpCatalogChainTemplate("value_flow_ranking");
const lifecycleTemplate = getAssistantMcpCatalogChainTemplate("lifecycle");
expect(documentTemplate.fallback_primitives).toEqual([
"resolve_entity_reference",
"query_documents",
"probe_coverage"
]);
expect(movementTemplate.fallback_primitives).toEqual([
"resolve_entity_reference",
"query_movements",
"probe_coverage"
]);
expect(valueFlowTemplate.base_required_axes).toEqual(["aggregate_axis", "amount", "coverage_target"]);
expect(valueFlowComparisonTemplate.fallback_primitives).toEqual(["query_movements", "probe_coverage"]);
expect(valueFlowRankingTemplate.fallback_primitives).toEqual([
"query_movements",
"aggregate_by_axis",
"probe_coverage"
]);
expect(lifecycleTemplate.base_required_axes).toEqual([
"document_date",
"coverage_target",
"evidence_basis",
"activity_window",
"legal_fact_boundary"
]);
expect(lifecycleTemplate.planning_tags).toEqual(
expect.arrayContaining(["bounded_inference", "activity_window", "legal_fact_boundary"])
);
});
it("can search reviewed primitives from data-need decomposition candidates", () => {
const primitives = searchAssistantMcpCatalogPrimitivesByDecompositionCandidates({
decomposition_candidates: [
"resolve_entity_reference",
"collect_scoped_movements",
"aggregate_checked_amounts",
"probe_coverage"
]
});
expect(primitives).toEqual([
"resolve_entity_reference",
"query_movements",
"aggregate_by_axis",
"probe_coverage"
]);
});
it("can suppress aggregate_by_axis for decomposition shapes that derive comparison without an aggregate primitive", () => {
const primitives = searchAssistantMcpCatalogPrimitivesByDecompositionCandidates({
decomposition_candidates: [
"collect_incoming_movements",
"collect_outgoing_movements",
"aggregate_by_month",
"probe_coverage"
],
allow_aggregate_by_axis: false
});
expect(primitives).toEqual(["query_movements", "probe_coverage"]);
});
it("can search reviewed primitives directly from fact family and required axes", () => {
const primitives = searchAssistantMcpCatalogPrimitivesByFactAxis({
business_fact_family: "document_evidence",
action_family: "list_documents",
has_subject_candidates: true,
required_axes: ["counterparty", "period", "coverage_target"]
});
expect(primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
});
it("treats all-time organization-scoped value-flow as catalog-compatible without inventing a fake period axis", () => {
const primitives = searchAssistantMcpCatalogPrimitivesByFactAxis({
business_fact_family: "value_flow",
action_family: "turnover",
has_subject_candidates: false,
required_axes: ["organization", "all_time_scope", "aggregate_axis", "amount", "coverage_target"]
});
expect(primitives).toEqual(["query_movements", "aggregate_by_axis", "probe_coverage"]);
});
it("can search reviewed primitives directly from a confirmed document metadata surface", () => {
const primitives = searchAssistantMcpCatalogPrimitivesByMetadataSurface({
downstream_route_family: "document_evidence",
selected_entity_set: "Document",
selected_surface_objects: ["Document.InvoiceIssued"],
recommended_next_primitive: "query_documents",
required_axes: ["counterparty", "period", "coverage_target"]
});
expect(primitives).toEqual(["query_documents", "resolve_entity_reference", "probe_coverage"]);
});
it("can search reviewed primitives directly from a confirmed movement metadata surface", () => {
const primitives = searchAssistantMcpCatalogPrimitivesByMetadataSurface({
downstream_route_family: "movement_evidence",
selected_entity_set: "AccumulationRegister",
selected_surface_objects: ["Register.BankOperations"],
recommended_next_primitive: "query_movements",
required_axes: ["counterparty", "period", "coverage_target"]
});
expect(primitives).toEqual(["query_movements", "resolve_entity_reference", "probe_coverage"]);
});
it("can search reviewed primitives directly from a confirmed catalog metadata surface without inventing an unsupported drilldown primitive", () => {
const primitives = searchAssistantMcpCatalogPrimitivesByMetadataSurface({
downstream_route_family: "catalog_drilldown",
selected_entity_set: "Catalog",
selected_surface_objects: ["Catalog.Counterparties"],
recommended_next_primitive: "drilldown_related_objects",
required_axes: ["metadata_scope"]
});
expect(primitives).toEqual(["inspect_1c_metadata"]);
});
it("marks a counterparty turnover discovery plan as catalog-compatible when required axes exist", () => {
const plan = buildAssistantMcpDiscoveryPlan({
semanticDataNeed: "counterparty turnover evidence",
turnMeaning: {
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_entity_candidates: ["SVK"]
},
proposedPrimitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis"],
requiredAxes: ["counterparty", "period", "aggregate_axis", "amount"]
});
const review = reviewAssistantMcpDiscoveryPlanAgainstCatalog(plan);
expect(review.review_status).toBe("catalog_compatible");
expect(review.reason_codes).toContain("catalog_plan_compatible");
expect(review.missing_axes_by_primitive).toEqual({});
expect(review.evidence_floors.query_movements).toBe("rows_matched");
});
it("asks for more axes before document discovery can run safely", () => {
const plan = buildAssistantMcpDiscoveryPlan({
semanticDataNeed: "document evidence",
turnMeaning: {
asked_domain_family: "counterparty_documents",
asked_action_family: "list_documents"
},
proposedPrimitives: ["query_documents"],
requiredAxes: ["amount"]
});
const review = reviewAssistantMcpDiscoveryPlanAgainstCatalog(plan);
expect(review.review_status).toBe("needs_more_axes");
expect(review.reason_codes).toContain("catalog_required_axes_missing_for_primitive");
expect(review.missing_axes_by_primitive.query_documents).toEqual([
["document"],
["counterparty"],
["contract"],
["period", "organization"],
["all_time_scope", "counterparty"],
["all_time_scope", "organization"]
]);
});
it("marks an all-time organization-scoped value-flow plan as catalog-compatible without requiring an explicit period", () => {
const plan = buildAssistantMcpDiscoveryPlan({
semanticDataNeed: "organization-scoped value-flow evidence",
turnMeaning: {
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_organization_scope: "ООО Альтернатива Плюс"
},
proposedPrimitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
requiredAxes: ["organization", "all_time_scope", "aggregate_axis", "amount", "coverage_target"]
});
const review = reviewAssistantMcpDiscoveryPlanAgainstCatalog(plan);
expect(review.review_status).toBe("catalog_compatible");
expect(review.reason_codes).toContain("catalog_plan_compatible");
expect(review.missing_axes_by_primitive).toEqual({});
});
it("marks an all-time organization-scoped document plan as catalog-compatible without requiring an explicit period", () => {
const plan = buildAssistantMcpDiscoveryPlan({
semanticDataNeed: "document evidence",
turnMeaning: {
asked_domain_family: "documents",
asked_action_family: "list_documents",
explicit_organization_scope: "ООО Альтернатива Плюс",
metadata_scope_hint: "НДС",
subject_resolution_optional: true
},
proposedPrimitives: ["query_documents", "probe_coverage"],
requiredAxes: ["organization", "all_time_scope", "metadata_scope", "coverage_target"]
});
const review = reviewAssistantMcpDiscoveryPlanAgainstCatalog(plan);
expect(review.review_status).toBe("catalog_compatible");
expect(review.reason_codes).toContain("catalog_plan_compatible");
expect(review.missing_axes_by_primitive).toEqual({});
});
it("preserves source-summary evidence floors for metadata and coverage primitives", () => {
expect(getAssistantMcpCatalogPrimitive("inspect_1c_metadata").evidence_floor).toBe("source_summary");
expect(getAssistantMcpCatalogPrimitive("probe_coverage").evidence_floor).toBe("source_summary");
expect(getAssistantMcpCatalogPrimitive("resolve_entity_reference").evidence_floor).toBe("rows_matched");
});
it("turns a non-allowed discovery plan into a catalog-level blocked review", () => {
const plan = buildAssistantMcpDiscoveryPlan({
semanticDataNeed: "raw model sql",
turnMeaning: {
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_entity_candidates: ["SVK"]
},
proposedPrimitives: ["raw_sql"],
requiredAxes: ["counterparty"]
});
const review = reviewAssistantMcpDiscoveryPlanAgainstCatalog(plan);
expect(plan.plan_status).toBe("blocked");
expect(review.review_status).toBe("catalog_blocked");
expect(review.reason_codes).toContain("catalog_review_received_non_allowed_plan");
expect(review.reason_codes).toContain("catalog_plan_blocked");
});
});