Files
Jyotisha/frontend/tests/rectification-adaptive-selector.test.ts
T
2026-09-03 23:49:42 +08:00

106 lines
3.8 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { parseRectificationScoring } from "../src/lib/birth-time-journey-adapters.ts";
import { projectJourneyTurn } from "../src/lib/birth-time-journey-turn.ts";
import type { CandidateResult } from "../src/lib/birth-time-evidence.ts";
const snapshot = {
state: "rectifying",
assistantIntent: "continue_rectification_questions",
input: "rectification_questions",
route: "rectification",
confidence: "low",
canApply: false,
activeTime: null,
reportedRange: { label: "10:00—11:00", startTime: "10:00", endTime: "11:00" },
} as const;
const lowResult: CandidateResult = {
resultId: "1d8ee348-61a3-433d-8907-ff6d281b9992",
confidence: "low",
canApply: false,
winningSegment: null,
eventCount: 3,
domainCount: 3,
topScore: 8,
secondScore: 7,
marginPercent: 12.5,
reasons: [],
evidence: [],
algorithmVersion: "birth-time-event-scoring-v1",
};
const questionnaire = { samples: [
{ d4Sign: "Aries", d9Sign: "Leo", d10Sign: "Virgo", d24Sign: "Gemini", d30Sign: "Pisces" },
{ d4Sign: "Aries", d9Sign: "Leo", d10Sign: "Libra", d24Sign: "Gemini", d30Sign: "Pisces" },
] } as const;
const lifeEvents = [
{ id: "5cb071d6-6d99-46be-85dc-a9bf59ef6ac5", domain: "education", date: "2011", precision: "year" },
{ id: "0790866c-ad5e-4a45-b2b4-a5c73f6be6ea", domain: "relocation", date: "2019", precision: "year" },
{ id: "0ef52e51-ab5f-453b-81e5-adb44a929224", domain: "health_pressure", date: "2021", precision: "year" },
] as const;
test("scoring adapter prefers the server-selected next question", () => {
const scoring = parseRectificationScoring({
answered_count: 1,
candidate_cluster_rankings: [],
next_round: 2,
next_round_questions: [{ id: "fallback", domain: "career", prompt: "fallback" }],
next_round_selection: {
selected_questions: [{ id: "selected", domain: "relationship", prompt: "selected" }],
},
});
assert.equal(scoring.nextRoundQuestions[0]?.id, "selected");
assert.equal(scoring.nextRoundQuestions[0]?.domain, "relationship");
});
test("adaptive projection prefers a valid server-selected question", () => {
const turn = projectJourneyTurn({
turnVersion: 1,
snapshot,
questionnaire,
persistedProgress: { adaptiveRound: 0, askedDomains: [] },
candidateResult: lowResult,
serverSelectedQuestion: { id: "relationship_followup", domain: "relationship" },
lifeEvents,
});
assert.equal(turn.nextAction.kind, "ask_adaptive_evidence");
if (turn.nextAction.kind === "ask_adaptive_evidence") {
assert.equal(turn.nextAction.question.questionId, "relationship_followup");
assert.equal(turn.nextAction.question.domain, "relationship");
}
});
test("invalid server domains fall back locally and baseline stays local", () => {
const adaptive = projectJourneyTurn({
turnVersion: 1,
snapshot,
questionnaire,
persistedProgress: { adaptiveRound: 0, askedDomains: [] },
candidateResult: lowResult,
serverSelectedQuestion: { id: "invalid", domain: "unsupported" },
lifeEvents,
});
assert.equal(adaptive.nextAction.kind, "ask_adaptive_evidence");
if (adaptive.nextAction.kind === "ask_adaptive_evidence") {
assert.equal(adaptive.nextAction.question.domain, "career");
}
const baseline = projectJourneyTurn({
turnVersion: 0,
snapshot: { ...snapshot, confidence: null },
questionnaire,
persistedProgress: { adaptiveRound: 0, askedDomains: [] },
candidateResult: null,
serverSelectedQuestion: { id: "relationship_followup", domain: "relationship" },
lifeEvents: lifeEvents.slice(0, 1),
});
assert.equal(baseline.nextAction.kind, "ask_baseline_evidence");
if (baseline.nextAction.kind === "ask_baseline_evidence") {
assert.equal(baseline.nextAction.question.domain, "career");
}
});