Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
116 lines
6.3 KiB
TypeScript
116 lines
6.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
import { buildSegmentSummary, type SegmentMinute } from "../src/lib/rectification-agentic/core/segment-summary.ts";
|
|
import {
|
|
CASE_DOSSIER_RPC,
|
|
chartTiersFromDossier,
|
|
clearAdoptedChartTierCache,
|
|
loadAdoptedChartTiers,
|
|
segmentProvenance,
|
|
} from "../src/lib/rectification-adopted-chart-tiers.ts";
|
|
|
|
// Real native scan of a public Rodden-AA case (31-minute window), not a hand-built shape.
|
|
const golden = JSON.parse(readFileSync(new URL("./fixtures/varga-api-response.golden.json", import.meta.url), "utf8"));
|
|
const realMinutes = golden.scan.minutes as SegmentMinute[];
|
|
|
|
const userId = "11111111-1111-4111-8111-111111111111";
|
|
const caseId = "22222222-2222-4222-8222-222222222222";
|
|
const resultId = "33333333-3333-4333-8333-333333333333";
|
|
const newerResultId = "44444444-4444-4444-8444-444444444444";
|
|
const segmentV1 = { contract: "segment-v1", case_id: caseId, result_id: resultId, candidate_time: "09:05" };
|
|
|
|
// Real 31-minute scan: D1 is one sign (certain); every candidate weighs on the
|
|
// first D9/D10 segments so the posterior picks them.
|
|
function realSummary() {
|
|
const candidates = realMinutes.filter((_, index) => index % 2 === 0).map((minute) => ({ time: minute.time, score: minute.offset <= 4 ? 12 : 1 }));
|
|
return buildSegmentSummary({ minutes: realMinutes, candidates, windowMinutes: realMinutes.length, scanComplete: true });
|
|
}
|
|
|
|
// Explicitly fictional ±60 control: a 121-minute window makes D9/D10 blocked by width alone.
|
|
function wideSummary() {
|
|
const minutes: SegmentMinute[] = Array.from({ length: 121 }, (_, offset) => ({
|
|
offset, date: "2000-06-14", time: `${String(8 + Math.floor(offset / 60)).padStart(2, "0")}:${String(offset % 60).padStart(2, "0")}`,
|
|
signs: { D1: 4, D9: Math.floor(offset / 13) % 12, D10: Math.floor(offset / 12) % 12 },
|
|
}));
|
|
return buildSegmentSummary({ minutes, candidates: [{ time: minutes[60]!.time, score: 10 }], windowMinutes: 121, scanComplete: true });
|
|
}
|
|
|
|
function dossier(summary: unknown, id = resultId, owningCase = caseId) {
|
|
return { case: { case_id: owningCase }, latest_result: { result_id: id, decision_receipt: { inference_state: { segment_summary: summary } } } };
|
|
}
|
|
|
|
function client(data: unknown, error: unknown = null) {
|
|
const calls: { fn: string; args: Record<string, unknown> }[] = [];
|
|
return {
|
|
calls,
|
|
rpc: async (fn: string, args: Record<string, unknown>) => { calls.push({ fn, args }); return { data, error }; },
|
|
};
|
|
}
|
|
|
|
test("provenance: only segment-v1 with uuid case/result is read", () => {
|
|
assert.deepEqual(segmentProvenance(segmentV1), { caseId, resultId });
|
|
for (const value of [null, {}, { contract: "candidate-v2", case_id: caseId, result_id: resultId }, { ...segmentV1, case_id: "x" }, "segment-v1"]) {
|
|
assert.equal(segmentProvenance(value), null);
|
|
}
|
|
});
|
|
|
|
test("five inputs: segment-v1 / legacy adoption / unrectified / missing result / corrupt summary", async () => {
|
|
clearAdoptedChartTierCache();
|
|
const warnings: string[] = [];
|
|
const warn = (payload: { reason: string }) => { warnings.push(payload.reason); };
|
|
|
|
// 1. segment-v1 with a wide window: D9 and D10 blocked, D1 determined.
|
|
const wide = client(dossier(wideSummary()));
|
|
assert.deepEqual(await loadAdoptedChartTiers(wide, { userId, provenance: segmentV1 }, warn),
|
|
{ source: "segment-v1", resultMatch: "adopted", unreliable: ["D9", "D10"] });
|
|
assert.deepEqual(wide.calls, [{ fn: CASE_DOSSIER_RPC, args: { p_user_id: userId, p_case_id: caseId } }]);
|
|
|
|
// 2. Legacy (candidate-minute) adoption and 3. unrectified profile: no RPC, null.
|
|
const untouched = client(dossier(wideSummary()));
|
|
assert.equal(await loadAdoptedChartTiers(untouched, { userId, provenance: { contract: "candidate-v2", case_id: caseId, result_id: resultId } }, warn), null);
|
|
assert.equal(await loadAdoptedChartTiers(untouched, { userId, provenance: null }, warn), null);
|
|
assert.equal(untouched.calls.length, 0);
|
|
|
|
// 4. Result missing (RPC error, or a dossier without a latest result): null and a warning.
|
|
clearAdoptedChartTierCache();
|
|
assert.equal(await loadAdoptedChartTiers(client(null, { code: "P0001" }), { userId, provenance: segmentV1 }, warn), null);
|
|
assert.equal(await loadAdoptedChartTiers(client({ case: { case_id: caseId } }), { userId, provenance: segmentV1 }, warn), null);
|
|
|
|
// 5. Corrupt summary (tier inconsistent with its own shares): null and a warning.
|
|
const corrupt = JSON.parse(JSON.stringify(wideSummary()));
|
|
corrupt.charts[1].tier = "credible";
|
|
assert.equal(await loadAdoptedChartTiers(client(dossier(corrupt)), { userId, provenance: segmentV1 }, warn), null);
|
|
assert.deepEqual(warnings, ["P0001", "invalid_segment_summary", "invalid_segment_summary"]);
|
|
});
|
|
|
|
test("real narrow scan: certain D1 and credible D9/D10 are not flagged", () => {
|
|
const summary = realSummary();
|
|
const tiers = Object.fromEntries(summary.charts.map((chart) => [chart.chart, chart.tier]));
|
|
assert.equal(tiers.D1, "certain");
|
|
const result = chartTiersFromDossier(dossier(summary), { caseId, resultId });
|
|
assert.notEqual(result, "invalid");
|
|
if (result === "invalid") return;
|
|
assert.deepEqual(result.unreliable, summary.charts.filter((chart) => chart.tier === "blocked" || chart.tier === "indistinct").map((chart) => chart.chart));
|
|
assert.ok(!result.unreliable.includes("D1"));
|
|
});
|
|
|
|
test("a newer result of the same Case is read; another Case never is", () => {
|
|
const newer = chartTiersFromDossier(dossier(wideSummary(), newerResultId), { caseId, resultId });
|
|
assert.deepEqual(newer, { source: "segment-v1", resultMatch: "latest", unreliable: ["D9", "D10"] });
|
|
assert.equal(chartTiersFromDossier(dossier(wideSummary(), resultId, "55555555-5555-4555-8555-555555555555"), { caseId, resultId }), "invalid");
|
|
});
|
|
|
|
test("results are cached per adopted result for five minutes", async () => {
|
|
clearAdoptedChartTierCache();
|
|
const reader = client(dossier(wideSummary()));
|
|
let now = 1_000;
|
|
await loadAdoptedChartTiers(reader, { userId, provenance: segmentV1 }, () => {}, () => now);
|
|
now += 60_000;
|
|
await loadAdoptedChartTiers(reader, { userId, provenance: segmentV1 }, () => {}, () => now);
|
|
assert.equal(reader.calls.length, 1);
|
|
now += 5 * 60_000;
|
|
await loadAdoptedChartTiers(reader, { userId, provenance: segmentV1 }, () => {}, () => now);
|
|
assert.equal(reader.calls.length, 2);
|
|
});
|