183 lines
5.8 KiB
TypeScript
183 lines
5.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { PUBLIC_RECTIFICATION_TOOLS } from "../src/lib/rectification-agentic/v9/public-receipt.ts";
|
|
import { createRectificationV9Tools } from "../src/mastra/rectification-v9-tools.ts";
|
|
import {
|
|
CASE_ID,
|
|
EVIDENCE_ID,
|
|
FOCUS_ID,
|
|
RESULT_ID,
|
|
CANDIDATE_ID,
|
|
TURN_ID,
|
|
USER_ID,
|
|
fakeAccounting,
|
|
} from "./rectification-v9-test-support.ts";
|
|
|
|
type ToolSchema = {
|
|
inputSchema: {
|
|
safeParse(value: unknown): { success: boolean };
|
|
};
|
|
};
|
|
|
|
const EXACT_TOOL_KEYS = [
|
|
"rectification-read-case",
|
|
"rectification-set-focus",
|
|
"rectification-resolve-focus",
|
|
"rectification-record-evidence-batch",
|
|
"rectification-propose-evidence",
|
|
"rectification-confirm-evidence",
|
|
"rectification-revise-evidence",
|
|
"rectification-compare-candidates",
|
|
"rectification-read-diagnostics",
|
|
"rectification-offer-candidates",
|
|
"rectification-accept-candidate",
|
|
"rectification-confirm-birth-time",
|
|
"rectification-close-case",
|
|
] as const;
|
|
|
|
function toolsUnderTest() {
|
|
return createRectificationV9Tools({
|
|
userId: USER_ID,
|
|
caseId: CASE_ID,
|
|
turnId: TURN_ID,
|
|
accounting: fakeAccounting({}).client as never,
|
|
});
|
|
}
|
|
|
|
const validInputs: Record<(typeof EXACT_TOOL_KEYS)[number], Record<string, unknown>> = {
|
|
"rectification-read-case": { caseId: CASE_ID },
|
|
"rectification-set-focus": {
|
|
caseId: CASE_ID,
|
|
questionId: "career-month-question",
|
|
intent: "clarify_event_date",
|
|
targetEvidenceId: EVIDENCE_ID,
|
|
targetDomain: "career",
|
|
targetKind: "career_entry",
|
|
expectedAnswerSchema: { required: ["month"] },
|
|
},
|
|
"rectification-resolve-focus": {
|
|
caseId: CASE_ID,
|
|
focusId: FOCUS_ID,
|
|
status: "resolved",
|
|
evidenceId: EVIDENCE_ID,
|
|
},
|
|
"rectification-record-evidence-batch": {
|
|
caseId: CASE_ID,
|
|
focusId: FOCUS_ID,
|
|
items: [{
|
|
quote: "2016年9月去北京工作",
|
|
proposedKind: "career_entry",
|
|
subject: "self",
|
|
domain: "career",
|
|
datePrecision: "month",
|
|
occurredFrom: "2016-09",
|
|
summary: "2016年9月去北京工作",
|
|
}],
|
|
},
|
|
"rectification-propose-evidence": {
|
|
caseId: CASE_ID,
|
|
quote: "2016年9月去北京工作",
|
|
proposedKind: "career_entry",
|
|
subject: "self",
|
|
domain: "career",
|
|
datePrecision: "month",
|
|
occurredFrom: "2016-09",
|
|
summary: "2016年9月去北京工作",
|
|
},
|
|
"rectification-confirm-evidence": {
|
|
caseId: CASE_ID,
|
|
focusId: FOCUS_ID,
|
|
evidenceId: EVIDENCE_ID,
|
|
},
|
|
"rectification-revise-evidence": {
|
|
caseId: CASE_ID,
|
|
focusId: FOCUS_ID,
|
|
evidenceId: EVIDENCE_ID,
|
|
quote: "不是9月,是10月",
|
|
datePrecision: "month",
|
|
occurredFrom: "2016-10",
|
|
summary: "更正为2016年10月去北京工作",
|
|
},
|
|
"rectification-compare-candidates": { caseId: CASE_ID },
|
|
"rectification-read-diagnostics": { caseId: CASE_ID },
|
|
"rectification-offer-candidates": { caseId: CASE_ID },
|
|
"rectification-accept-candidate": {
|
|
caseId: CASE_ID,
|
|
resultId: RESULT_ID,
|
|
candidateId: CANDIDATE_ID,
|
|
},
|
|
"rectification-confirm-birth-time": {
|
|
caseId: CASE_ID,
|
|
resultId: RESULT_ID,
|
|
candidateId: CANDIDATE_ID,
|
|
consentQuote: "就用05:02",
|
|
},
|
|
"rectification-close-case": {
|
|
caseId: CASE_ID,
|
|
reason: "completed_by_user",
|
|
},
|
|
};
|
|
|
|
test("V10 exposes exactly the 13 allowlisted rectification tool keys", () => {
|
|
const tools = toolsUnderTest();
|
|
assert.deepEqual(Object.keys(tools), [...EXACT_TOOL_KEYS]);
|
|
assert.deepEqual([...PUBLIC_RECTIFICATION_TOOLS], [...EXACT_TOOL_KEYS]);
|
|
assert.equal(new Set(Object.keys(tools)).size, 13);
|
|
});
|
|
|
|
test("all 13 tool input schemas accept their minimal contract and reject unknown fields", () => {
|
|
const tools = toolsUnderTest() as unknown as Record<string, ToolSchema>;
|
|
for (const toolName of EXACT_TOOL_KEYS) {
|
|
const schema = tools[toolName]?.inputSchema;
|
|
assert.ok(schema, `${toolName} must expose inputSchema`);
|
|
assert.equal(
|
|
schema.safeParse(validInputs[toolName]).success,
|
|
true,
|
|
`${toolName} must accept its documented input`,
|
|
);
|
|
assert.equal(
|
|
schema.safeParse({ ...validInputs[toolName], userId: USER_ID }).success,
|
|
false,
|
|
`${toolName} must be strict and reject server-owned userId`,
|
|
);
|
|
assert.equal(
|
|
schema.safeParse({ ...validInputs[toolName], candidate_range: { start_time: "04:50", end_time: "05:10" } }).success,
|
|
false,
|
|
`${toolName} must reject server-owned candidate ranges`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("focus/evidence mutation schemas require durable focus and evidence references", () => {
|
|
const tools = toolsUnderTest() as unknown as Record<string, ToolSchema>;
|
|
const confirm = tools["rectification-confirm-evidence"].inputSchema;
|
|
const revise = tools["rectification-revise-evidence"].inputSchema;
|
|
const resolve = tools["rectification-resolve-focus"].inputSchema;
|
|
|
|
assert.equal(confirm.safeParse({ caseId: CASE_ID, evidenceId: EVIDENCE_ID }).success, false);
|
|
assert.equal(confirm.safeParse({ caseId: CASE_ID, focusId: FOCUS_ID }).success, false);
|
|
assert.equal(revise.safeParse({
|
|
...validInputs["rectification-revise-evidence"],
|
|
focusId: undefined,
|
|
}).success, false);
|
|
assert.equal(resolve.safeParse({ caseId: CASE_ID, status: "declined" }).success, false);
|
|
});
|
|
|
|
test("batch item schemas are independently strict and bounded", () => {
|
|
const tools = toolsUnderTest() as unknown as Record<string, ToolSchema>;
|
|
const schema = tools["rectification-record-evidence-batch"].inputSchema;
|
|
const valid = validInputs["rectification-record-evidence-batch"];
|
|
const item = (valid.items as Record<string, unknown>[])[0]!;
|
|
|
|
assert.equal(schema.safeParse({ ...valid, items: [] }).success, false);
|
|
assert.equal(schema.safeParse({
|
|
...valid,
|
|
items: [{ ...item, evidenceId: EVIDENCE_ID }],
|
|
}).success, false);
|
|
assert.equal(schema.safeParse({
|
|
...valid,
|
|
items: Array.from({ length: 13 }, () => item),
|
|
}).success, false);
|
|
});
|