Files
Jyotisha/frontend/tests/rectification-v9-test-support.ts
2026-08-15 00:56:06 +08:00

258 lines
9.0 KiB
TypeScript

import type { RectificationRpcClient } from "../src/lib/rectification-agentic/v9/tool-service.ts";
export type FakeRpcHandler = (
fn: string,
args: Record<string, unknown>,
) => unknown | Promise<unknown>;
export type FakeAccounting = {
calls: Array<{ fn: string; args: Record<string, unknown> }>;
client: RectificationRpcClient;
};
export function fakeAccounting(
handlers: Partial<Record<string, FakeRpcHandler>>,
options: { fallback?: FakeRpcHandler } = {},
): FakeAccounting {
const calls: Array<{ fn: string; args: Record<string, unknown> }> = [];
const client: RectificationRpcClient = {
rpc(fn, args) {
calls.push({ fn, args });
const handler = handlers[fn] ?? options.fallback;
if (!handler) {
return Promise.resolve({
data: null,
error: { message: `unexpected rpc ${fn}` },
});
}
return Promise.resolve()
.then(() => handler(fn, args))
.then(
(data) => ({ data, error: null }),
(error: unknown) => ({
data: null,
error: { message: error instanceof Error ? error.message : String(error) },
}),
);
},
};
return { calls, client };
}
export const CASE_ID = "11111111-1111-4111-8111-111111111111";
export const SESSION_ID = "22222222-2222-4222-8222-222222222222";
export const TURN_ID = "33333333-3333-4333-8333-333333333333";
export const ATTEMPT_ID = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
export const FOCUS_ID = "abababab-abab-4bab-8bab-abababababab";
export const EVIDENCE_ID = "44444444-4444-4444-8444-444444444444";
export const RESULT_ID = "55555555-5555-4555-8555-555555555555";
export const CANDIDATE_ID = "88888888-8888-4888-8888-888888888881";
export const SECOND_CANDIDATE_ID = "88888888-8888-4888-8888-888888888882";
export const USER_ID = "66666666-6666-4666-8666-666666666666";
export const SOURCE_TURN_ID = "77777777-7777-4777-8777-777777777777";
export const RECTIFICATION_SKILL_SHA256 = "5acb3103e80993ea611b93d2c1746e70b74aff8f8636bcffc80a837b954b470d";
export const RECTIFICATION_SKILL_SOURCE_COMMIT = "0fd111d16b45796086a6c1d0945dbd3de6755d8a";
export const CANDIDATE_RANGE = { start_time: "04:50", end_time: "05:10" };
export function dossierFixture(overrides: {
status?: string;
turnCount?: number;
evidenceCount?: number;
latestResult?: unknown;
evidence?: unknown[];
turns?: unknown[];
candidateRange?: { start_time: string; end_time: string } | null;
conversationSummary?: unknown;
} = {}) {
return {
case: {
case_id: CASE_ID,
session_id: SESSION_ID,
status: overrides.status ?? "collecting_evidence",
skill_name: "jyotish-birth-time-rectification",
skill_version: "9.0.0",
candidate_range: overrides.candidateRange ?? CANDIDATE_RANGE,
accepted_time: null,
confirmed_time: null,
completed_at: null,
closed_reason: null,
last_activity_at: "2026-08-12T10:00:00.000Z",
evidence_count: overrides.evidenceCount ?? 1,
turn_count: overrides.turnCount ?? 2,
},
turns: overrides.turns ?? [
{
id: TURN_ID,
role: "user",
text: "2016年9月离开家去北京开始工作",
status: "completed",
created_at: "2026-08-12T10:00:00.000Z",
completed_at: "2026-08-12T10:00:05.000Z",
},
],
evidence: overrides.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",
},
],
conversation_summary: overrides.conversationSummary ?? {
confirmed_evidence_summary: [],
pending_revisions: [],
active_focus: null,
declined_skipped_topics: [],
candidate_divergence_summary: null,
missing_evidence_categories: [],
last_result_policy: null,
summary_version: 1,
updated_at: "2026-08-12T10:00:06.000Z",
},
latest_result: overrides.latestResult ?? null,
};
}
export function conversationSummaryFixture(overrides: {
activeFocus?: unknown;
declinedSkippedTopics?: unknown[];
pendingRevisions?: unknown[];
} = {}) {
return {
confirmed_evidence_summary: [],
pending_revisions: overrides.pendingRevisions ?? [],
active_focus: overrides.activeFocus ?? null,
declined_skipped_topics: overrides.declinedSkippedTopics ?? [],
candidate_divergence_summary: null,
missing_evidence_categories: [],
last_result_policy: null,
summary_version: 1,
updated_at: "2026-08-12T10:00:06.000Z",
};
}
export function activeFocusFixture(overrides: {
id?: string;
targetEvidenceId?: string | null;
intent?: string;
targetDomain?: string | null;
targetKind?: string | null;
expectedAnswerSchema?: Record<string, unknown>;
} = {}) {
return {
id: overrides.id ?? FOCUS_ID,
case_id: CASE_ID,
question_id: "question-1",
intent: overrides.intent ?? "confirm_revision",
target_evidence_id: overrides.targetEvidenceId ?? null,
target_domain: overrides.targetDomain ?? "career",
target_kind: overrides.targetKind ?? "career_entry",
expected_answer_schema: overrides.expectedAnswerSchema ?? {},
status: "active",
asked_at: "2026-08-12T10:00:05.000Z",
resolved_at: null,
};
}
export function computeFixture(overrides: {
baselineBirthSnapshot?: Record<string, unknown>;
baselineProfileFingerprint?: string;
} = {}) {
return {
case_id: CASE_ID,
skill_version: "9.0.0",
baseline_profile_fingerprint: overrides.baselineProfileFingerprint ?? "a".repeat(64),
baseline_birth_snapshot: overrides.baselineBirthSnapshot ?? {
birth_date: "1997-08-08",
birth_place_label: "河北省邯郸市",
latitude: 36.420487,
longitude: 114.209936,
timezone_id: "Asia/Shanghai",
timezone_offset: 8,
birth_time_source: "family_exact",
reported_birth_time: "05:00",
active_birth_time: null,
uncertainty_before_minutes: 10,
uncertainty_after_minutes: 10,
},
candidate_range: CANDIDATE_RANGE,
};
}
/** Tool receipt RPCs always succeed. */
export const receiptHandlers: Partial<Record<string, FakeRpcHandler>> = {
get_agentic_rectification_skill_identity: () => ({
skill_name: "jyotish-birth-time-rectification",
skill_version: "9.0.0",
skill_sha256: RECTIFICATION_SKILL_SHA256,
skill_source_commit: RECTIFICATION_SKILL_SOURCE_COMMIT,
}),
create_agentic_rectification_run_attempt: (_fn, args) => ({
attempt_id: Number(args.p_attempt_number) === 2
? "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
: ATTEMPT_ID,
idempotent: false,
}),
finalize_agentic_rectification_run_attempt: (_fn, args) => ({
attempt_id: args.p_attempt_id ?? ATTEMPT_ID,
status: args.p_status ?? "completed",
}),
insert_agentic_rectification_skill_run_receipt: () => ({
receipt_id: "88888888-8888-4888-8888-888888888888",
skill_name: "jyotish-birth-time-rectification",
skill_version: "9.0.0",
skill_sha256: RECTIFICATION_SKILL_SHA256,
source_commit: RECTIFICATION_SKILL_SOURCE_COMMIT,
}),
insert_agentic_rectification_tool_receipt: () => ({ receipt_id: "99999999-9999-4999-8999-999999999999" }),
insert_agentic_rectification_run_phase: () => ({ phase_id: "99999999-9999-4999-8999-999999999999" }),
transition_agentic_rectification_case_status: () => ({ case_id: CASE_ID, status: "collecting_evidence", idempotent: false }),
};
export function candidateSnapshotFixture(overrides: {
confirmationAllowed?: boolean;
selectionAllowed?: boolean;
representativeTime?: string | null;
selectedTime?: string | null;
selectionKind?: string | null;
candidates?: unknown[];
} = {}) {
return {
result_id: RESULT_ID,
candidates: overrides.candidates ?? [
{ candidate_id: CANDIDATE_ID, rank: 1, time: "05:02", relative_support: 58, tied_minute_count: 2 },
{ candidate_id: SECOND_CANDIDATE_ID, rank: 2, time: "04:55", relative_support: 42, tied_minute_count: 3 },
],
overall_confidence: "medium",
decision_receipt: {
receipt_version: "candidate-decision-receipt-v2",
policy_version: "rectification-candidate-policy-v2",
selection_allowed: overrides.selectionAllowed ?? true,
acceptance_allowed: overrides.selectionAllowed ?? true,
confirmation_allowed: overrides.confirmationAllowed ?? false,
representative_candidate_id: overrides.representativeTime ? CANDIDATE_ID : null,
overall_confidence: "medium",
},
execution_ledger: [{ method: "d1-rashi", status: "executed" }],
representative_time: overrides.representativeTime ?? null,
selected_time: overrides.selectedTime ?? null,
selection_kind: overrides.selectionKind ?? null,
evidence_ledger_fingerprint: "b".repeat(64),
candidate_range_fingerprint: "c".repeat(64),
skill_version: "9.0.0",
algorithm_version: "rectification-v5",
created_at: "2026-08-12T10:05:00.000Z",
invalidated_at: null,
};
}