220 lines
9.2 KiB
TypeScript
220 lines
9.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { planAssistantMcpDiscovery } from "../src/services/assistantMcpDiscoveryPlanner";
|
|
|
|
describe("assistant MCP discovery planner", () => {
|
|
it("builds a catalog-compatible value-flow discovery plan from current turn meaning", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "counterparty_value",
|
|
asked_action_family: "turnover",
|
|
explicit_entity_candidates: ["SVK"],
|
|
explicit_date_scope: "2020"
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
|
expect(result.selected_chain_id).toBe("value_flow");
|
|
expect(result.selected_chain_summary).toContain("query scoped movements");
|
|
expect(result.proposed_primitives).toEqual([
|
|
"resolve_entity_reference",
|
|
"query_movements",
|
|
"aggregate_by_axis",
|
|
"probe_coverage"
|
|
]);
|
|
expect(result.required_axes).toEqual(["counterparty", "period", "aggregate_axis", "amount", "coverage_target"]);
|
|
expect(result.catalog_review.review_status).toBe("catalog_compatible");
|
|
expect(result.discovery_plan.answer_may_use_raw_model_claims).toBe(false);
|
|
expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30);
|
|
expect(result.reason_codes).toContain("planner_enabled_chunked_coverage_probe_budget");
|
|
});
|
|
|
|
it("keeps a value-flow plan in clarification state when period axis is missing", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "counterparty_value",
|
|
asked_action_family: "turnover",
|
|
explicit_entity_candidates: ["SVK"]
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("needs_clarification");
|
|
expect(result.catalog_review.review_status).toBe("needs_more_axes");
|
|
expect(result.catalog_review.missing_axes_by_primitive.query_movements).toContainEqual(["period", "counterparty"]);
|
|
expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context");
|
|
});
|
|
|
|
it("keeps requested monthly aggregation as an explicit planning axis for value-flow discovery", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "counterparty_value",
|
|
asked_action_family: "net_value_flow",
|
|
asked_aggregation_axis: "month",
|
|
explicit_entity_candidates: ["SVK"],
|
|
explicit_date_scope: "2020"
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.proposed_primitives).toEqual([
|
|
"resolve_entity_reference",
|
|
"query_movements",
|
|
"aggregate_by_axis",
|
|
"probe_coverage"
|
|
]);
|
|
expect(result.required_axes).toEqual([
|
|
"counterparty",
|
|
"period",
|
|
"aggregate_axis",
|
|
"amount",
|
|
"coverage_target",
|
|
"calendar_month"
|
|
]);
|
|
expect(result.reason_codes).toContain("planner_selected_monthly_value_flow_recipe");
|
|
expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30);
|
|
});
|
|
|
|
it("builds a document discovery plan without falling back to movement primitives", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "counterparty_documents",
|
|
asked_action_family: "list_documents",
|
|
explicit_entity_candidates: ["SVK"]
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
|
|
expect(result.proposed_primitives).not.toContain("query_movements");
|
|
expect(result.required_axes).toEqual(["counterparty", "coverage_target"]);
|
|
});
|
|
|
|
it("builds a movement discovery plan without aggregating value-flow totals", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "movements",
|
|
asked_action_family: "list_movements",
|
|
explicit_entity_candidates: ["SVK"],
|
|
explicit_date_scope: "2020",
|
|
unsupported_but_understood_family: "movement_evidence"
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.semantic_data_need).toBe("movement evidence");
|
|
expect(result.selected_chain_id).toBe("movement_evidence");
|
|
expect(result.selected_chain_summary).toContain("movement rows");
|
|
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]);
|
|
expect(result.proposed_primitives).not.toContain("aggregate_by_axis");
|
|
expect(result.required_axes).toEqual(["counterparty", "period", "coverage_target"]);
|
|
expect(result.reason_codes).toContain("planner_selected_movement_recipe");
|
|
});
|
|
|
|
it("builds an inference-safe lifecycle plan with evidence explanation", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "counterparty_lifecycle",
|
|
asked_action_family: "activity_duration",
|
|
explicit_entity_candidates: ["SVK"]
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.proposed_primitives).toEqual([
|
|
"resolve_entity_reference",
|
|
"query_documents",
|
|
"probe_coverage",
|
|
"explain_evidence_basis"
|
|
]);
|
|
expect(result.required_axes).toEqual(["counterparty", "document_date", "coverage_target", "evidence_basis"]);
|
|
});
|
|
|
|
it("uses metadata-only planning when the user asks about available schema surface", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "metadata",
|
|
asked_action_family: "inspect_catalog"
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]);
|
|
expect(result.required_axes).toEqual(["metadata_scope"]);
|
|
expect(result.catalog_review.evidence_floors.inspect_1c_metadata).toBe("source_summary");
|
|
});
|
|
|
|
it("keeps broad metadata surface inspection on inspect_1c_metadata", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "metadata",
|
|
asked_action_family: "inspect_surface",
|
|
explicit_entity_candidates: ["НДС"]
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
|
expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]);
|
|
expect(result.proposed_primitives).not.toContain("query_documents");
|
|
expect(result.proposed_primitives).not.toContain("query_movements");
|
|
expect(result.reason_codes).toContain("planner_selected_metadata_recipe");
|
|
});
|
|
|
|
it("keeps metadata document inspection on inspect_1c_metadata instead of query_documents", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "metadata",
|
|
asked_action_family: "inspect_documents"
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]);
|
|
expect(result.proposed_primitives).not.toContain("query_documents");
|
|
});
|
|
|
|
it("keeps metadata lane-choice clarification in needs_clarification without launching MCP primitives", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
asked_domain_family: "metadata",
|
|
asked_action_family: "resolve_next_lane",
|
|
explicit_entity_candidates: ["SVK"],
|
|
explicit_date_scope: "2020",
|
|
unsupported_but_understood_family: "metadata_lane_choice_clarification"
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("needs_clarification");
|
|
expect(result.semantic_data_need).toBe("metadata lane clarification");
|
|
expect(result.selected_chain_id).toBe("metadata_lane_clarification");
|
|
expect(result.selected_chain_summary).toContain("choose the next data lane");
|
|
expect(result.proposed_primitives).toEqual([]);
|
|
expect(result.required_axes).toEqual(["counterparty", "period", "lane_family_choice"]);
|
|
expect(result.discovery_plan.plan_status).toBe("needs_clarification");
|
|
expect(result.reason_codes).toContain("planner_selected_metadata_lane_clarification_recipe");
|
|
expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context");
|
|
});
|
|
|
|
it("does not mark an unclassified turn as executable without turn meaning context", () => {
|
|
const result = planAssistantMcpDiscovery({});
|
|
|
|
expect(result.planner_status).toBe("needs_clarification");
|
|
expect(result.discovery_plan.plan_status).toBe("needs_clarification");
|
|
expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context");
|
|
});
|
|
|
|
it("exposes an explicit entity-resolution chain instead of silently collapsing into a lifecycle lane", () => {
|
|
const result = planAssistantMcpDiscovery({
|
|
turnMeaning: {
|
|
explicit_entity_candidates: ["SVK"]
|
|
}
|
|
});
|
|
|
|
expect(result.planner_status).toBe("ready_for_execution");
|
|
expect(result.semantic_data_need).toBe("entity discovery evidence");
|
|
expect(result.selected_chain_id).toBe("entity_resolution");
|
|
expect(result.selected_chain_summary).toContain("resolve the most relevant 1C reference");
|
|
expect(result.proposed_primitives).toEqual(["search_business_entity", "resolve_entity_reference", "probe_coverage"]);
|
|
});
|
|
});
|