Files
Jyotisha/frontend/tests/birth-time-journey-client.test.ts
2026-07-18 18:40:23 +08:00

142 lines
4.2 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { parseJourneyResponse } from "../src/lib/birth-time-journey-client.ts";
import {
highConfirmationTurn,
snapshot,
} from "./birth-time-journey-client-test-support.ts";
test("journey client parses the sanitized API response", () => {
const parsed = parseJourneyResponse({
caseId: highConfirmationTurn.caseId,
snapshot,
questionnaire: {
questions: [{
id: "education_environment_shift",
prompt: "是否有明显学业变化?",
options: [{ key: "A", label: "明确有" }],
}],
samples: [],
raw: {},
},
scoring: null,
});
assert.equal(parsed.snapshot.route, "rectification");
assert.equal(parsed.questionnaire?.questions[0]?.id, "education_environment_shift");
});
test("journey client preserves deterministic next rectification questions", () => {
const parsed = parseJourneyResponse({
caseId: highConfirmationTurn.caseId,
snapshot: {
...snapshot,
state: "candidate",
assistantIntent: "present_saved_candidate_range",
},
questionnaire: {
questions: [
{ id: "education_environment_shift", prompt: "是否有明显学业变化?" },
{ id: "residence_relocation_shift", prompt: "是否有明显居住变化?" },
{ id: "relationship_or_partner_entry", prompt: "是否有明显关系变化?" },
],
samples: [],
raw: {},
},
scoring: {
answeredCount: 3,
candidateClusterRankings: [{ cluster: "middle_candidate_cluster", score: 5 }],
nextRound: 2,
nextRoundQuestions: [{
id: "health_crisis_or_low_period",
prompt: "是否有明显健康或低谷阶段?",
options: [{ key: "A", label: "明确有" }],
}],
raw: {},
},
answers: {
education_environment_shift: "A",
residence_relocation_shift: "A",
relationship_or_partner_entry: "B",
},
});
assert.equal(parsed.scoring?.nextRound, 2);
assert.equal(
parsed.scoring?.nextRoundQuestions[0]?.id,
"health_crisis_or_low_period",
);
});
test("journey client rejects an applied rectification result", () => {
assert.throws(() => parseJourneyResponse({
caseId: highConfirmationTurn.caseId,
snapshot: { ...snapshot, canApply: true, activeTime: "14:24" },
questionnaire: null,
scoring: null,
}));
});
test("journey client accepts only a guarded high-confidence confirmation", () => {
const parsed = parseJourneyResponse({
...highConfirmationTurn,
turnVersion: undefined,
});
assert.equal(parsed.snapshot.state, "confirming");
assert.equal(
parsed.candidateResult?.winningSegment?.representativeTime,
"14:24",
);
assert.throws(() => parseJourneyResponse({
...parsed,
turnVersion: undefined,
snapshot: { ...parsed.snapshot, state: "candidate", input: "candidate_actions" },
}));
});
test("client rejects a nonterminal turn without nextAction", () => {
assert.throws(() => parseJourneyResponse({
...highConfirmationTurn,
nextAction: undefined,
}));
});
test("client derives Agent permission without exposing legacy canApply", () => {
const parsed = parseJourneyResponse(highConfirmationTurn);
assert.equal(parsed.permissions.canConfirmCandidate, true);
assert.equal("canApply" in parsed.permissions, false);
});
test("versioned client responses require every response field", () => {
assert.throws(() => parseJourneyResponse({
...highConfirmationTurn,
answers: undefined,
}));
});
test("client preserves every candidate varga sign", () => {
const sample = {
ascendantSign: "Aries",
d4Sign: "Taurus",
d9Sign: "Gemini",
d10Sign: "Cancer",
d24Sign: "Leo",
d30Sign: "Virgo",
};
const parsed = parseJourneyResponse({
...highConfirmationTurn,
questionnaire: { questions: [], samples: [sample], raw: {} },
});
assert.deepEqual(parsed.questionnaire?.samples[0], sample);
});
test("versioned response parsing returns a readonly public boundary", () => {
const parsed = parseJourneyResponse(highConfirmationTurn);
assert.equal(Object.isFrozen(parsed), true);
assert.equal(Object.isFrozen(parsed.candidateResult), true);
});