NODEDC_1C/llm_normalizer/backend/tests/assistantMcpDiscoveryRuntim...

98 lines
4.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { planAssistantMcpDiscovery } from "../src/services/assistantMcpDiscoveryPlanner";
import { buildAssistantMcpDiscoveryRuntimeDryRun } from "../src/services/assistantMcpDiscoveryRuntimeAdapter";
describe("assistant MCP discovery runtime adapter", () => {
it("turns a catalog-compatible value-flow plan into an execution-ready dry-run package", () => {
const planner = planAssistantMcpDiscovery({
turnMeaning: {
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_entity_candidates: ["SVK"],
explicit_date_scope: "2020"
}
});
const dryRun = buildAssistantMcpDiscoveryRuntimeDryRun(planner);
expect(dryRun.adapter_status).toBe("dry_run_ready");
expect(dryRun.mcp_execution_performed).toBe(false);
expect(dryRun.user_facing_fallback).toBeNull();
expect(dryRun.execution_steps.map((step) => step.primitive_id)).toEqual([
"resolve_entity_reference",
"query_movements",
"aggregate_by_axis",
"probe_coverage"
]);
expect(dryRun.execution_steps.every((step) => step.step_status === "ready")).toBe(true);
expect(dryRun.execution_steps.every((step) => step.dry_run_only)).toBe(true);
expect(dryRun.evidence_gate).toEqual({
required: true,
expected_inputs: [
"probe_results",
"confirmed_facts",
"inferred_facts",
"unknown_facts",
"source_rows_summary",
"query_limitations"
],
answer_may_use_raw_model_claims: false
});
expect(dryRun.reason_codes).toContain("runtime_dry_run_ready_without_mcp_execution");
});
it("keeps execution in clarification state when catalog axes are missing", () => {
const planner = planAssistantMcpDiscovery({
turnMeaning: {
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_entity_candidates: ["SVK"]
}
});
const dryRun = buildAssistantMcpDiscoveryRuntimeDryRun(planner);
const movementStep = dryRun.execution_steps.find((step) => step.primitive_id === "query_movements");
expect(dryRun.adapter_status).toBe("needs_clarification");
expect(dryRun.mcp_execution_performed).toBe(false);
expect(dryRun.user_facing_fallback).toBe("ask_for_missing_scope_before_mcp_discovery");
expect(movementStep?.step_status).toBe("missing_axes");
expect(movementStep?.missing_axis_options).toContainEqual(["period", "counterparty"]);
expect(dryRun.reason_codes).toContain("runtime_dry_run_needs_clarification_before_mcp_execution");
});
it("keeps blocked planner output blocked before any MCP execution", () => {
const planner = planAssistantMcpDiscovery({});
planner.planner_status = "blocked";
planner.discovery_plan.plan_status = "blocked";
planner.catalog_review.review_status = "catalog_blocked";
const dryRun = buildAssistantMcpDiscoveryRuntimeDryRun(planner);
expect(dryRun.adapter_status).toBe("blocked");
expect(dryRun.mcp_execution_performed).toBe(false);
expect(dryRun.user_facing_fallback).toBe("explain_that_runtime_policy_blocked_mcp_discovery");
expect(dryRun.execution_steps.every((step) => step.step_status === "blocked")).toBe(true);
expect(dryRun.reason_codes).toContain("runtime_dry_run_blocked_before_mcp_execution");
});
it("uses source-summary stop conditions for metadata-only dry runs", () => {
const planner = planAssistantMcpDiscovery({
turnMeaning: {
asked_domain_family: "metadata",
asked_action_family: "inspect_catalog"
}
});
const dryRun = buildAssistantMcpDiscoveryRuntimeDryRun(planner);
expect(dryRun.adapter_status).toBe("dry_run_ready");
expect(dryRun.execution_steps).toHaveLength(1);
expect(dryRun.execution_steps[0]).toMatchObject({
primitive_id: "inspect_1c_metadata",
evidence_floor: "source_summary",
stop_condition: "stop_after_allowed_probe_returns_source_summary_or_limitation"
});
});
});