Files
Jyotisha/frontend/tests/rectification-year-focus-overlay-20260916.test.ts
T
jesse-ux 6aabbe386f
Independent Staging Quality Gate / validate (push) Successful in 10m1s
Independent Staging Quality Gate / publish (push) Successful in 36m54s
feat(rectification): 删掉年月录入卡,年月阶段打字回答且不被追问覆盖
2026-09-16 23:22:32 +08:00

155 lines
6.4 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { withSpokenPrompt } from "../src/lib/rectification-agentic/v9/spoken-prompt.ts";
import { projectCurrentQuestion } from "../src/lib/rectification-agentic/v9/turn-decision.ts";
import {
TARGETED_YEAR_PROMPT,
isYearStageQuestionId,
pendingTargetedYearDomain,
} from "../src/lib/rectification-agentic/v9/collection-question-pool.ts";
import {
collectFocusSchema,
isCollectFocusSchema,
persistServerOwnedFocus,
shouldResolveCollectFocusAfterEvidence,
} from "../src/lib/rectification-agentic/v9/server-focus.ts";
import { shouldHostReaskYearEntry, userMessageHasYear } from "../src/lib/rectification-agentic/v9/year-entry-host.ts";
import { TARGETED_COLLECT_OPTION_A } from "../src/lib/rectification-agentic/v9/choice-card.ts";
import { targetedCollectYearFollowup as yearFollowup } from "../src/lib/rectification-agentic/v9/method-followup.ts";
import {
CASE_ID,
FOCUS_ID,
USER_ID,
fakeAccounting,
} from "./rectification-v9-test-support.ts";
const YEAR_Q = "collect:targeted:health_pressure:year";
const SERVER_PROMPT = TARGETED_YEAR_PROMPT;
const FOLLOWUP = "2019年3月这次手术,是你本人做的吗?";
test("year-stage set-focus overlay keeps the server prompt (BUG-908)", () => {
const schema = {
collect: true,
prompt: SERVER_PROMPT,
};
const next = withSpokenPrompt(schema, FOLLOWUP, YEAR_Q);
assert.equal(next.prompt, SERVER_PROMPT);
assert.equal(next.spoken_prompt, FOLLOWUP);
const question = projectCurrentQuestion({
id: FOCUS_ID,
questionId: YEAR_Q,
intent: "collect_method_evidence",
targetDomain: "health_pressure",
expectedAnswerSchema: next,
});
assert.equal(question?.prompt, SERVER_PROMPT);
assert.equal(question?.kind, "collect_spoken");
});
test("year-stage questions are typed collect, not an entry card (T0)", () => {
assert.equal(isYearStageQuestionId(YEAR_Q), true);
assert.equal(isYearStageQuestionId("collect:targeted:health_pressure"), false);
assert.equal(TARGETED_COLLECT_OPTION_A, "有,我来说");
const chat = readFileSync(new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url), "utf8");
assert.doesNotMatch(chat, /EventDateEntryCard|event_date_entry/);
const followup = yearFollowup("health_pressure");
const schema = collectFocusSchema(followup);
assert.ok(isCollectFocusSchema(schema));
assert.equal(schema?.event_date_entry, undefined);
assert.equal(followup.spoken_prompt, TARGETED_YEAR_PROMPT);
});
test("batch and propose-evidence share the same collect-focus resolve (BUG-909)", () => {
const tools = readFileSync(new URL("../src/mastra/rectification-v9-tools.ts", import.meta.url), "utf8");
const first = tools.indexOf("await resolveActiveCollectFocusAfterEvidence(");
const second = tools.indexOf("await resolveActiveCollectFocusAfterEvidence(", first + 1);
assert.ok(first > 0 && second > first);
assert.equal(tools.indexOf("await resolveActiveCollectFocusAfterEvidence(", second + 1), -1);
});
test("dated evidence closes the year stage for that domain (BUG-909)", () => {
const topics = [{
questionId: "collect:targeted:health_pressure",
target_domain: "health_pressure",
status: "resolved",
intent: "collect_method_evidence",
}];
assert.equal(pendingTargetedYearDomain(topics, []), "health_pressure");
assert.equal(pendingTargetedYearDomain(topics, [{
status: "confirmed",
domain: "health",
datePrecision: "month",
occurredFrom: "2019-03-01",
occurredTo: "2019-03-01",
}]), null);
const schema = collectFocusSchema(yearFollowup("health_pressure"));
assert.equal(shouldResolveCollectFocusAfterEvidence({
intent: "collect_method_evidence",
questionId: YEAR_Q,
expectedAnswerSchema: schema ?? { collect: true, prompt: SERVER_PROMPT },
}), true);
});
test("persist year followup writes a collect schema that batch can resolve (BUG-909 a)", async () => {
const accounting = fakeAccounting({
set_agentic_rectification_conversation_focus: (_fn, args) => ({
focus: {
id: FOCUS_ID,
case_id: CASE_ID,
question_id: args.p_question_id,
intent: args.p_intent,
target_domain: args.p_target_domain,
expected_answer_schema: args.p_expected_answer_schema,
status: "active",
asked_at: "2026-09-16T00:00:00.000Z",
},
idempotent: false,
}),
});
const result = await persistServerOwnedFocus({
accounting: accounting.client,
userId: USER_ID,
caseId: CASE_ID,
activeFocus: null,
decisionReceipt: null,
followup: yearFollowup("health_pressure"),
});
assert.equal(result.status, "created");
assert.equal(isCollectFocusSchema(result.focus?.expectedAnswerSchema ?? null), true);
assert.equal(result.focus?.expectedAnswerSchema.prompt, TARGETED_YEAR_PROMPT);
assert.equal(result.focus?.expectedAnswerSchema.event_date_entry, undefined);
});
test("yearless short replies are held by the host pre-step (BUG-910)", () => {
assert.equal(userMessageHasYear("是"), false);
assert.equal(userMessageHasYear("不是本人"), false);
assert.equal(userMessageHasYear("2019年3月做过手术"), true);
assert.equal(userMessageHasYear("2020 年住院"), true);
const focus = {
questionId: YEAR_Q,
expectedAnswerSchema: { collect: true, prompt: SERVER_PROMPT },
};
assert.equal(shouldHostReaskYearEntry(focus, "是"), SERVER_PROMPT);
assert.equal(shouldHostReaskYearEntry(focus, "2019年3月做过手术"), null);
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
const reliability = agentRun.indexOf("classifyDateReliabilityUtterance");
const yearHost = agentRun.indexOf("shouldHostReaskYearEntry");
const reserve = agentRun.indexOf("billing.reserve");
assert.ok(reliability > 0 && yearHost > reliability && reserve > yearHost);
});
test("standalone persisted question shares the assistant inset (BUG-911)", () => {
const styles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
assert.match(
styles,
/\.rectification-message-question\.is-standalone \{[\s\S]*margin-inline-start: var\(--assistant-content-inset\)/,
);
assert.match(
styles,
/\.rectification-message-question\.is-standalone \.rectification-choice-card\.is-embedded \{[\s\S]*margin-inline-start: 0/,
);
assert.doesNotMatch(styles, /\.rectification-event-entry \{/);
});