Files
Jyotisha/frontend/tests/rectification-v9-status-security.test.ts
T
Jesse_Chen f708edf365
Independent Staging Quality Gate / validate (push) Successful in 10m5s
Independent Staging Quality Gate / publish (push) Successful in 7m4s
fix(rectification): ingest first-turn events without dropping day precision or claiming a unique minute
SQL kinds now match the TypeScript ledger so batch ingest can confirm dated events. Confirm no longer burns the opening focus, recap uses server date labels, and the Agent sees indistinguishable width instead of a fake unique minute.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-19 07:44:23 +08:00

741 lines
25 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { createRectificationV9Tools } from "../src/mastra/rectification-v9-tools.ts";
import {
acceptV9Candidate,
confirmV9BirthTime,
safeToolErrorCode,
} from "../src/lib/rectification-agentic/v9/tool-service.ts";
import {
canTransitToTerminal,
evidenceWritesAllowed,
isTerminalStatus,
} from "../src/lib/rectification-agentic/v9/case-status.ts";
import {
CASE_ID,
CANDIDATE_RANGE,
CANDIDATE_ID,
SECOND_CANDIDATE_ID,
EVIDENCE_ID,
FOCUS_ID,
RESULT_ID,
SESSION_ID,
TURN_ID,
USER_ID,
activeFocusFixture,
candidateSnapshotFixture,
computeFixture,
conversationSummaryFixture,
dossierFixture,
fakeAccounting,
receiptHandlers,
} from "./rectification-v9-test-support.ts";
test("accepted is never upgraded to confirmed by the accept path", async () => {
const accounting = fakeAccounting({
accept_agentic_rectification_candidate_for_case_v2: () => ({
success: true,
saved_time: "05:02",
status: "accepted",
result_id: RESULT_ID,
case_status: "candidate_accepted",
idempotent: false,
}),
});
const result = await acceptV9Candidate(accounting.client, USER_ID, CASE_ID, RESULT_ID, CANDIDATE_ID, TURN_ID);
assert.equal(result.status, "accepted");
assert.equal(result.caseStatus, "candidate_accepted");
assert.notEqual(result.status, "confirmed");
});
test("accept fails closed if the accept-only RPC reports confirmed", async () => {
const accounting = fakeAccounting({
accept_agentic_rectification_candidate_for_case_v2: () => ({
success: true,
saved_time: "05:02",
status: "confirmed",
result_id: RESULT_ID,
candidate_id: CANDIDATE_ID,
case_status: "confirmed",
idempotent: false,
}),
});
await assert.rejects(
acceptV9Candidate(accounting.client, USER_ID, CASE_ID, RESULT_ID, CANDIDATE_ID, TURN_ID),
(error: unknown) => error instanceof Error && error.message.includes("invalid_accept_result"),
);
});
test("confirmed requires the engine gate plus explicit grounded consent", async () => {
// confirmation_allowed=false on the stored result blocks confirmation.
const blocked = fakeAccounting({
confirm_agentic_rectification_candidate_for_case_v2: () => {
throw new Error("agentic_rectification_confirmation_blocked");
},
});
await assert.rejects(
confirmV9BirthTime(blocked.client, USER_ID, CASE_ID, {
resultId: RESULT_ID,
candidateId: CANDIDATE_ID,
requestId: TURN_ID,
consentQuote: "就用05:02",
sourceTurnId: TURN_ID,
}),
(error: unknown) => error instanceof Error && error.message.includes("confirmation_blocked"),
);
// Confirming a time that is not the representative minute is rejected.
const mismatch = fakeAccounting({
confirm_agentic_rectification_candidate_for_case_v2: () => {
throw new Error("agentic_rectification_confirm_time_mismatch");
},
});
await assert.rejects(
confirmV9BirthTime(mismatch.client, USER_ID, CASE_ID, {
resultId: RESULT_ID,
candidateId: SECOND_CANDIDATE_ID,
requestId: TURN_ID,
consentQuote: "就用04:55",
sourceTurnId: TURN_ID,
}),
(error: unknown) => error instanceof Error && error.message.includes("confirm_time_mismatch"),
);
// Consent quote must be grounded in the source turn's message.
const ungrounded = fakeAccounting({
confirm_agentic_rectification_candidate_for_case_v2: () => {
throw new Error("agentic_rectification_consent_not_grounded");
},
});
await assert.rejects(
confirmV9BirthTime(ungrounded.client, USER_ID, CASE_ID, {
resultId: RESULT_ID,
candidateId: CANDIDATE_ID,
requestId: TURN_ID,
consentQuote: "用户根本没说过这句话",
sourceTurnId: TURN_ID,
}),
(error: unknown) => error instanceof Error && error.message.includes("consent_not_grounded"),
);
});
test("confirm-birth-time binds consent to the server-owned current turn", async () => {
const accounting = fakeAccounting({
...receiptHandlers,
confirm_agentic_rectification_candidate_for_case_v2: () => ({
success: true,
saved_time: "05:02",
status: "confirmed",
result_id: RESULT_ID,
case_status: "confirmed",
idempotent: false,
}),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const confirm = tools["rectification-confirm-birth-time"] as unknown as {
inputSchema: { safeParse(value: unknown): { success: boolean } };
execute(input: unknown): Promise<unknown>;
};
const input = {
caseId: CASE_ID,
resultId: RESULT_ID,
candidateId: CANDIDATE_ID,
consentQuote: "就用05:02",
};
assert.equal(confirm.inputSchema.safeParse(input).success, true);
assert.equal(confirm.inputSchema.safeParse({ ...input, sourceTurnId: "77777777-7777-4777-8777-777777777777" }).success, false);
await confirm.execute(input);
const call = accounting.calls.find((item) => item.fn === "confirm_agentic_rectification_candidate_for_case_v2");
assert.ok(call);
assert.equal(call.args.p_source_turn_id, TURN_ID);
assert.equal(call.args.p_candidate_id, CANDIDATE_ID);
assert.equal(call.args.p_request_id, TURN_ID);
assert.equal("p_time" in call.args, false);
});
test("candidate ownership is case-scoped: the RPC always receives the case id", async () => {
const accounting = fakeAccounting({
accept_agentic_rectification_candidate_for_case_v2: () => ({
success: true,
saved_time: "05:02",
status: "accepted",
result_id: RESULT_ID,
case_status: "candidate_accepted",
idempotent: false,
}),
});
await acceptV9Candidate(accounting.client, USER_ID, CASE_ID, RESULT_ID, CANDIDATE_ID, TURN_ID);
const call = accounting.calls.find((item) => item.fn === "accept_agentic_rectification_candidate_for_case_v2");
assert.ok(call);
assert.equal(call.args.p_case_id, CASE_ID);
assert.equal(call.args.p_user_id, USER_ID);
assert.equal(call.args.p_result_id, RESULT_ID);
assert.equal(call.args.p_candidate_id, CANDIDATE_ID);
assert.equal(call.args.p_request_id, TURN_ID);
assert.equal("p_time" in call.args, false);
});
test("accept is idempotent: replaying the same selection succeeds without a second write", async () => {
const accounting = fakeAccounting({
accept_agentic_rectification_candidate_for_case_v2: () => ({
success: true,
saved_time: "05:02",
status: "accepted",
result_id: RESULT_ID,
case_status: "candidate_accepted",
idempotent: true,
}),
});
const result = await acceptV9Candidate(accounting.client, USER_ID, CASE_ID, RESULT_ID, CANDIDATE_ID, TURN_ID);
assert.equal(result.idempotent, true);
assert.equal(result.status, "accepted");
});
test("terminal cases reject evidence writes and candidate actions", async () => {
assert.equal(evidenceWritesAllowed("confirmed"), false);
assert.equal(evidenceWritesAllowed("closed"), false);
assert.equal(evidenceWritesAllowed("superseded"), false);
assert.equal(isTerminalStatus("confirmed"), true);
assert.equal(canTransitToTerminal("confirmed", "closed"), false);
const accounting = fakeAccounting({
...receiptHandlers,
get_agentic_rectification_case_dossier: () => dossierFixture({ status: "closed" }),
propose_agentic_rectification_evidence: () => {
throw new Error("agentic_rectification_case_terminal");
},
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
await assert.rejects(
(tools["rectification-propose-evidence"] as unknown as { execute(input: unknown): Promise<unknown> }).execute({
caseId: CASE_ID,
quote: "2016年9月离开家去北京工作",
proposedKind: "career_entry",
subject: "self",
domain: "career",
datePrecision: "month",
occurredFrom: "2016-09",
summary: "2016年9月离家去北京工作",
}),
(error: unknown) => error instanceof Error && error.message.includes("case_terminal"),
);
});
test("baseline change invalidates results and forces needs_rebaseline", () => {
// The migration ships a profile guard trigger; the service exposes the
// status contract that resumable cases may enter needs_rebaseline.
assert.equal(canTransitToTerminal("collecting_evidence", "confirmed"), true);
// A needs_rebaseline case can still collect evidence but never reference
// stale candidates; evidence writes remain allowed while resumable.
assert.equal(evidenceWritesAllowed("needs_rebaseline"), true);
});
test("read-case gives the Agent authoritative birth context without raw internals", async () => {
const accounting = fakeAccounting({
...receiptHandlers,
get_agentic_rectification_case_dossier: () => dossierFixture({
latestResult: candidateSnapshotFixture(),
}),
get_agentic_rectification_case_compute: () => computeFixture(),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const projection = await (tools["rectification-read-case"] as unknown as {
execute(input: unknown): Promise<Record<string, unknown>>;
}).execute({ caseId: CASE_ID });
assert.deepEqual(projection.birth_context, {
birth_date: "1997-08-08",
reported_birth_time: "05:00",
active_birth_time: null,
candidate_range: CANDIDATE_RANGE,
birthplace: {
label: "河北省邯郸市",
latitude: 36.420487,
longitude: 114.209936,
},
timezone: {
id: "Asia/Shanghai",
offset: 8,
},
});
const serialized = JSON.stringify(projection);
assert.doesNotMatch(serialized, /baseline_birth_snapshot/);
assert.doesNotMatch(serialized, /event_contribution_matrix|rule_ids|raw_score|weight/);
});
test("read-case exposes bounded safe context for multi-turn follow-ups without raw ledger internals", async () => {
const revisionId = "88888888-8888-4888-8888-888888888888";
const accounting = fakeAccounting({
...receiptHandlers,
get_agentic_rectification_case_dossier: () => dossierFixture({
turnCount: 5,
evidenceCount: 2,
turns: [
{
id: TURN_ID,
role: "user",
text: "2016年9月离开家去北京开始工作,2020年又搬到了上海",
status: "completed",
created_at: "2026-08-12T10:00:00.000Z",
},
{
id: TURN_ID,
role: "assistant",
text: "第二次搬家大约发生在哪个月?",
status: "completed",
created_at: "2026-08-12T10:00:00.000Z",
},
{
id: "99999999-9999-4999-8999-999999999998",
role: "user",
text: "月份记不清了,换个方向吧",
status: "completed",
created_at: "2026-08-12T10:01:00.000Z",
},
],
conversationSummary: conversationSummaryFixture({
declinedSkippedTopics: [{
focus_id: FOCUS_ID,
intent: "collect_event_date",
target_evidence_id: revisionId,
target_domain: "relocation",
target_kind: "relocation",
status: "declined",
asked_at: "2026-08-12T10:00:00.000Z",
resolved_at: "2026-08-12T10:01:00.000Z",
}],
}),
evidence: [
{
id: EVIDENCE_ID,
source_turn_id: TURN_ID,
subject: "self",
event_kind: "career_entry",
domain: "career",
occurred_from: "2016-09-01",
occurred_to: null,
date_precision: "month",
summary: "2016年9月离开家去北京开始工作",
status: "confirmed",
supersedes_evidence_id: null,
created_at: "2026-08-12T10:00:06.000Z",
},
{
id: revisionId,
source_turn_id: TURN_ID,
subject: "self",
event_kind: "relocation",
domain: "relocation",
occurred_from: "2020-01-01",
occurred_to: null,
date_precision: "year",
summary: "2020年搬到上海",
status: "pending_confirmation",
supersedes_evidence_id: null,
created_at: "2026-08-12T10:00:07.000Z",
},
],
}),
get_agentic_rectification_case_compute: () => computeFixture(),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const projection = await (tools["rectification-read-case"] as unknown as {
execute(input: unknown): Promise<Record<string, unknown>>;
}).execute({ caseId: CASE_ID });
assert.deepEqual(projection.evidence_context, [
{
evidence_id: EVIDENCE_ID,
status: "confirmed",
summary: "2016年9月离开家去北京开始工作",
event_kind: "career_entry",
domain: "career",
date_precision: "month",
occurred_from: "2016-09-01",
occurred_to: null,
display_date_label: "2016-09",
},
{
evidence_id: revisionId,
status: "pending_confirmation",
summary: "2020年搬到上海",
event_kind: "relocation",
domain: "relocation",
date_precision: "year",
occurred_from: "2020-01-01",
occurred_to: null,
display_date_label: "2020年",
},
]);
assert.deepEqual(projection.conversation_context, {
active_followup: null,
recent_turns: [
{ role: "user", text: "2016年9月离开家去北京开始工作,2020年又搬到了上海" },
{ role: "assistant", text: "第二次搬家大约发生在哪个月?" },
{ role: "user", text: "月份记不清了,换个方向吧" },
],
declined_targets: [{
focus_id: FOCUS_ID,
intent: "collect_event_date",
target_evidence_id: revisionId,
target_domain: "relocation",
target_kind: "relocation",
status: "declined",
asked_at: "2026-08-12T10:00:00.000Z",
resolved_at: "2026-08-12T10:01:00.000Z",
}],
});
const serialized = JSON.stringify(projection);
assert.doesNotMatch(serialized, /source_turn_id|user_quote|supersedes_evidence_id|created_at/);
});
test("read-case includes the current pending user turn so a refusal closes the active follow-up immediately", async () => {
const revisionId = "88888888-8888-4888-8888-888888888888";
const accounting = fakeAccounting({
...receiptHandlers,
get_agentic_rectification_case_dossier: () => dossierFixture({
turns: [
{
id: "99999999-9999-4999-8999-999999999997",
role: "assistant",
text: "你是把入职时间改成2017年,对吗?",
status: "completed",
created_at: "2026-08-12T10:00:00.000Z",
},
{
id: TURN_ID,
role: "user",
text: "记不清了,换个方向吧",
status: "pending",
created_at: "2026-08-12T10:01:00.000Z",
},
],
conversationSummary: conversationSummaryFixture({
declinedSkippedTopics: [{
focus_id: FOCUS_ID,
intent: "confirm_revision",
target_evidence_id: revisionId,
target_domain: "career",
target_kind: "career_entry",
status: "declined",
asked_at: "2026-08-12T10:00:00.000Z",
resolved_at: "2026-08-12T10:01:00.000Z",
}],
}),
evidence: [{
id: revisionId,
source_turn_id: TURN_ID,
subject: "self",
event_kind: "career_entry",
domain: "career",
occurred_from: "2017-01-01",
occurred_to: null,
date_precision: "year",
summary: "2017年开始工作",
status: "pending_confirmation",
supersedes_evidence_id: EVIDENCE_ID,
created_at: "2026-08-12T10:00:07.000Z",
}],
}),
get_agentic_rectification_case_compute: () => computeFixture(),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const projection = await (tools["rectification-read-case"] as unknown as {
execute(input: unknown): Promise<Record<string, unknown>>;
}).execute({ caseId: CASE_ID });
assert.deepEqual(projection.conversation_context, {
active_followup: null,
recent_turns: [
{ role: "assistant", text: "你是把入职时间改成2017年,对吗?" },
{ role: "user", text: "记不清了,换个方向吧" },
],
declined_targets: [{
focus_id: FOCUS_ID,
intent: "confirm_revision",
target_evidence_id: revisionId,
target_domain: "career",
target_kind: "career_entry",
status: "declined",
asked_at: "2026-08-12T10:00:00.000Z",
resolved_at: "2026-08-12T10:01:00.000Z",
}],
});
});
test("read-case keeps an unresolved pending revision as the active follow-up", async () => {
const revisionId = "88888888-8888-4888-8888-888888888888";
const accounting = fakeAccounting({
...receiptHandlers,
get_agentic_rectification_case_dossier: () => dossierFixture({
turns: [
{
id: TURN_ID,
role: "assistant",
text: "你是把入职时间改成2017年,对吗?",
status: "completed",
created_at: "2026-08-12T10:00:00.000Z",
},
],
conversationSummary: conversationSummaryFixture({
activeFocus: activeFocusFixture({
targetEvidenceId: revisionId,
intent: "confirm_revision",
targetDomain: "career",
targetKind: "career_entry",
expectedAnswerSchema: { type: "confirmation" },
}),
}),
evidence: [{
id: revisionId,
source_turn_id: TURN_ID,
subject: "self",
event_kind: "career_entry",
domain: "career",
occurred_from: "2017-01-01",
occurred_to: null,
date_precision: "year",
summary: "2017年开始工作",
status: "pending_confirmation",
supersedes_evidence_id: EVIDENCE_ID,
created_at: "2026-08-12T10:00:07.000Z",
}],
}),
get_agentic_rectification_case_compute: () => computeFixture(),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const projection = await (tools["rectification-read-case"] as unknown as {
execute(input: unknown): Promise<Record<string, unknown>>;
}).execute({ caseId: CASE_ID });
assert.deepEqual((projection.conversation_context as { active_followup: unknown }).active_followup, {
focus_id: FOCUS_ID,
question_id: "question-1",
intent: "confirm_revision",
evidence_id: revisionId,
target_domain: "career",
target_kind: "career_entry",
expected_answer_schema: { type: "confirmation" },
});
});
test("read-case keeps conversation and evidence context within the public safety bounds", async () => {
const longQuestion = "这是一段需要截断的追问".repeat(180);
const turns = [
...Array.from({ length: 4 }, (_, index) => ({
id: `prefix-${index}`,
role: index % 2 === 0 ? "assistant" : "user",
text: `较早轮次-${index}`,
status: "completed",
created_at: `2026-08-12T09:00:${String(index).padStart(2, "0")}.000Z`,
})),
{
id: "outside-scan-question",
role: "assistant",
text: "扫描窗口外的问题",
status: "completed",
created_at: "2026-08-12T09:00:04.000Z",
},
{
id: "outside-scan-refusal",
role: "user",
text: "记不清了,换个方向吧",
status: "completed",
created_at: "2026-08-12T09:00:05.000Z",
},
...Array.from({ length: 12 }, (_, pairIndex) => [
{
id: `question-${pairIndex}`,
role: "assistant",
text: `问题-${pairIndex}-${longQuestion}`,
status: pairIndex === 8 ? "failed" : pairIndex === 9 ? "retryable" : "completed",
created_at: `2026-08-12T10:${String(pairIndex).padStart(2, "0")}:00.000Z`,
},
{
id: `answer-${pairIndex}`,
role: "user",
text: "记不清了,换个方向吧",
status: "completed",
created_at: `2026-08-12T10:${String(pairIndex).padStart(2, "0")}:01.000Z`,
},
]).flat(),
];
const evidence = Array.from({ length: 25 }, (_, index) => ({
id: `evidence-${index}`,
source_turn_id: TURN_ID,
subject: "self",
event_kind: "career_entry",
domain: "career",
occurred_from: `${2000 + index}-01-01`,
occurred_to: null,
date_precision: "year",
summary: `${2000 + index}年发生的事件`,
status: "confirmed",
supersedes_evidence_id: null,
created_at: `2026-08-12T11:${String(index).padStart(2, "0")}:00.000Z`,
}));
const accounting = fakeAccounting({
...receiptHandlers,
get_agentic_rectification_case_dossier: () => dossierFixture({
turnCount: turns.length,
evidenceCount: evidence.length,
conversationSummary: conversationSummaryFixture({
declinedSkippedTopics: [7, 10, 11].map((index) => ({
focus_id: `focus-${index}`,
intent: "collect_event_date",
target_evidence_id: `evidence-${index}`,
target_domain: "career",
target_kind: "career_entry",
status: "declined",
asked_at: `2026-08-12T10:${String(index).padStart(2, "0")}:00.000Z`,
resolved_at: `2026-08-12T10:${String(index).padStart(2, "0")}:01.000Z`,
})),
}),
turns,
evidence,
}),
get_agentic_rectification_case_compute: () => computeFixture(),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const projection = await (tools["rectification-read-case"] as unknown as {
execute(input: unknown): Promise<Record<string, unknown>>;
}).execute({ caseId: CASE_ID });
const evidenceContext = projection.evidence_context as Array<{ evidence_id: string }>;
const conversationContext = projection.conversation_context as {
recent_turns: Array<{ role: string; text: string }>;
declined_targets: Array<{ focus_id: string }>;
};
assert.equal(evidenceContext.length, 20);
assert.equal(evidenceContext[0]?.evidence_id, "evidence-5");
assert.equal(evidenceContext.at(-1)?.evidence_id, "evidence-24");
assert.equal(conversationContext.recent_turns.length, 6);
assert.ok(conversationContext.recent_turns.every((turn) => turn.text.length <= 1_600));
assert.equal(conversationContext.declined_targets.length, 3);
assert.deepEqual(
conversationContext.declined_targets.map((target) => target.focus_id),
["focus-7", "focus-10", "focus-11"],
);
const serialized = JSON.stringify(projection.conversation_context);
assert.doesNotMatch(serialized, /扫描窗口外的问题|问题-8-|问题-9-/);
});
test("safe tool error mapping downgrades unknown engine failures", () => {
assert.equal(
safeToolErrorCode(new Error("agentic_rectification_quote_not_grounded")),
"quote_not_grounded",
);
assert.equal(
safeToolErrorCode(new Error("agentic_rectification_case_terminal")),
"case_terminal",
);
assert.equal(safeToolErrorCode(new Error("connection refused")), "tool_failed");
});
test("compare-candidates refuses to run without scorable evidence", async () => {
const accounting = fakeAccounting({
...receiptHandlers,
get_agentic_rectification_case_dossier: () => dossierFixture({
evidence: [],
}),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
await assert.rejects(
(tools["rectification-compare-candidates"] as unknown as { execute(input: unknown): Promise<unknown> }).execute({ caseId: CASE_ID }),
(error: unknown) => error instanceof Error && error.message.includes("no_scorable_evidence"),
);
});
test("close-case is a user completion, never an engine confirmation", async () => {
const accounting = fakeAccounting({
...receiptHandlers,
close_agentic_rectification_case: () => ({
success: true,
case_id: CASE_ID,
status: "closed",
idempotent: false,
}),
});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const result = await (tools["rectification-close-case"] as unknown as {
execute(input: unknown): Promise<{ status: string }>;
}).execute({ caseId: CASE_ID, reason: "completed_by_user" });
assert.equal(result.status, "closed");
assert.notEqual(result.status, "confirmed");
const call = accounting.calls.find((item) => item.fn === "close_agentic_rectification_case");
assert.ok(call);
assert.equal(call.args.p_reason, "completed_by_user");
});
test("no sensitive birth data in candidate accept inputs", () => {
const accounting = fakeAccounting({});
const tools = createRectificationV9Tools({
userId: USER_ID,
caseId: CASE_ID,
turnId: TURN_ID,
accounting: accounting.client as never,
});
const schema = (tools["rectification-accept-candidate"] as unknown as {
inputSchema: { safeParse(value: unknown): { success: boolean } };
}).inputSchema;
const valid = schema.safeParse({ caseId: CASE_ID, resultId: RESULT_ID, candidateId: CANDIDATE_ID });
assert.equal(valid.success, true);
const withBirth = schema.safeParse({
caseId: CASE_ID,
resultId: RESULT_ID,
candidateId: CANDIDATE_ID,
birth_date: "1997-08-08",
active_birth_time: "05:00",
});
assert.equal(withBirth.success, false);
void CANDIDATE_RANGE;
void EVIDENCE_ID;
void SESSION_ID;
});