import assert from "node:assert/strict"; import test from "node:test"; import { createConsultationPlan } from "../src/lib/consultation-plan.ts"; import { projectConsultationWorkflowRequest } from "../src/lib/consultation-workflow-request.ts"; import { runConsultationWorkflow } from "../src/mastra/index.ts"; import type { ConsultationInput } from "../src/mastra/index.ts"; test("timing questions use a legal report theme and preserve a timing route hint", () => { // Given: a public timing consultation question. const question = "未来哪些阶段值得把握?"; // When: its workflow request is projected for the Python service. const plan = createConsultationPlan({ userIntent: question, theme: "timing", consultationMode: "unverified_birth_time", }); const request = projectConsultationWorkflowRequest(question, "timing", plan); // Then: the public theme remains a canonical allowlisted domain with strict route metadata. assert.equal(request.question, "应期与阶段问题:未来哪些阶段值得把握?"); assert.deepEqual(request.themes, ["timing"]); assert.equal(request.strictWorkflowRoute, "timing"); assert.ok(request.requiredLayers.includes("Narayana")); assert.ok(request.requiredLayers.includes("negative holdout gate")); assert.equal(request.claimBoundary, "candidate_day_month_window_only_until_holdout_passes"); assert.equal(request.plan_version, "consultation-plan-v2"); assert.equal(request.precision_boundary, "precise_timing_blocked"); assert.equal(request.timing_horizon, "next_12_months"); }); test("consultation workflow allows a cold engine run to finish", async () => { const source = await import("node:fs/promises").then(({ readFile }) => readFile(new URL("../src/mastra/consultation-workflow.ts", import.meta.url), "utf8") ); assert.match(source, /AbortSignal\.timeout\(90_000\)/); }); test("timing input projects only legal private workflow fields", async () => { // Given: a public timing input and a Python workflow response. const input: ConsultationInput = { year: 1990, month: 1, day: 1, hour: 12, minute: 0, lat: 25, lon: 121, tz: 8, city: "台北", question: "未来哪些阶段值得把握?", theme: "timing", entryMode: "direct_chart", }; let requestBody = ""; const originalFetch = globalThis.fetch; globalThis.fetch = async (_input, init) => { requestBody = typeof init?.body === "string" ? init.body : ""; return new Response(JSON.stringify({ success: true, chart: {}, routing: {}, consumer_context: { route: "career", core_status: "ready", available_layers: [], missing_route_layers: [], hard_blockers: [], answer_policy: { can_answer_direction: true, can_answer_precise_timing: false, }, }, }), { status: 200 }); }; try { // When: the adapter invokes the Python workflow. await runConsultationWorkflow(input, { plan: createConsultationPlan({ userIntent: input.question, theme: input.theme, consultationMode: "unverified_birth_time", }), }); } finally { globalThis.fetch = originalFetch; } // Then: private workflow fields are projected without mutating public input. const body = JSON.parse(requestBody); assert.equal(body.question, "应期与阶段问题:未来哪些阶段值得把握?"); assert.equal(body.question_text, "应期与阶段问题:未来哪些阶段值得把握?"); assert.deepEqual(body.theme, ["timing"]); assert.match(requestBody, /"timing"/); assert.equal(body.plan_version, "consultation-plan-v2"); assert.equal(body.strict_workflow_route, "timing"); assert.ok(body.required_layers.includes("negative holdout gate")); assert.equal(body.claim_boundary, "candidate_day_month_window_only_until_holdout_passes"); assert.equal(body.precision_boundary, "precise_timing_blocked"); assert.deepEqual(body.requested_domains, ["timing"]); assert.deepEqual(body.required_evidence_categories, ["natal_foundation", "timing", "validation"]); assert.equal(input.question, "未来哪些阶段值得把握?"); assert.equal(input.theme, "timing"); });