Derive score-version fixtures from the SCORE_VERSION constant

Eight test and fixture files hardcoded scoreVersion: 2, a value the
constant has not held since v1.35.0. Now they import SCORE_VERSION and
use it, so a future bump cannot leave a fixture pinned to a stale
number.

The one genuinely historical case (record.test.ts, proving the input
fingerprint moves when the algorithm version moves) now derives both
its "previous" and "current" markers from SCORE_VERSION instead of two
unrelated literals, so the pair keeps its meaning after the next bump.
This commit is contained in:
Marc-André Bombeck committed 2026-08-02 00:15:29 +02:00
1 parent 7df25a6d92
commit af35346687
8 files changed
+35 -19

No files matched your search

@@ -22,6 +22,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
import { NextRequest } from "next/server";
import { Buffer } from "node:buffer";
import { SCORE_VERSION } from "@/lib/analytics/score/types";
process.env.ENCRYPTION_KEY =
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
@@ -269,7 +271,7 @@ function sourceClient() {
timezone: "Europe/Berlin",
composite: 76,
band: "yellow",
scoreVersion: 2,
scoreVersion: SCORE_VERSION,
composition: ["BLOOD_PRESSURE", "SLEEP", "ADIPOSITY"],
pillarScores: { BLOOD_PRESSURE: 86, SLEEP: 100, ADIPOSITY: 42 },
inputFingerprint: "a".repeat(64),
@@ -629,7 +631,7 @@ describe("backup round trip — export, wire schema, restore", () => {
timezone: "Europe/Berlin",
composite: 76,
band: "yellow",
scoreVersion: 2,
scoreVersion: SCORE_VERSION,
inputFingerprint: "a".repeat(64),
});
// The composition and the per-pillar scores are what keep the stored day
@@ -3,6 +3,7 @@ import { NextRequest } from "next/server";
import { apiError } from "@/lib/api-response";
import type { DailyDigest } from "@/lib/daily/digest";
import { SCORE_VERSION } from "@/lib/analytics/score/types";
/**
* `GET /api/daily/digest` — the P3 read seam.
@@ -63,7 +64,7 @@ const DIGEST: DailyDigest = {
band: "good",
delta: 3,
deltaReason: null,
scoreVersion: 2,
scoreVersion: SCORE_VERSION,
composition: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"],
},
topSignal: null,
@@ -6,6 +6,7 @@ import { I18nProvider } from "@/lib/i18n/context";
import { TodayHero } from "../today-hero";
import type { DailyDigest } from "@/lib/daily/digest";
import type { PriorityItem } from "@/lib/daily/priority-item";
import { SCORE_VERSION } from "@/lib/analytics/score/types";
// The hero now wires the coach check-in card's keep / let-go taps through
// `useCoachCheckinAction`, so it needs a QueryClient in the tree.
@@ -57,7 +58,7 @@ function digest(over: Partial<DailyDigest> = {}): DailyDigest {
band: "green",
delta: 3,
deltaReason: null,
scoreVersion: 2,
scoreVersion: SCORE_VERSION,
composition: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"],
},
topSignal: {
@@ -114,7 +115,7 @@ describe("<TodayHero>", () => {
band: "green",
delta: 20,
deltaReason: "algorithm_changed",
scoreVersion: 2,
scoreVersion: SCORE_VERSION,
composition: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"],
},
})}
@@ -144,8 +144,12 @@ describe("stored bands", () => {
});
describe("input fingerprint", () => {
// The fixture only needs two DIFFERENT version markers to prove the
// fingerprint is sensitive to a version move — pinned relative to
// SCORE_VERSION so the pair stays "previous vs current" rather than
// going stale (or colliding) the next time the constant bumps.
const base = {
scoreVersion: 1,
scoreVersion: SCORE_VERSION - 1,
composition: ["BLOOD_PRESSURE", "SLEEP", "ADIPOSITY"] as ScorePillarId[],
pillars: HEALTHY,
};
@@ -157,9 +161,9 @@ describe("input fingerprint", () => {
});
it("moves when the algorithm version moves", () => {
expect(healthScoreInputFingerprint({ ...base, scoreVersion: 2 })).not.toBe(
healthScoreInputFingerprint(base),
);
expect(
healthScoreInputFingerprint({ ...base, scoreVersion: SCORE_VERSION }),
).not.toBe(healthScoreInputFingerprint(base));
});
it("moves when the composition narrows", () => {
+5 -4
View File
@@ -77,7 +77,7 @@ vi.mock("@/lib/analytics/score/reader", () => ({
bandSetter: "BLOOD_PRESSURE" as const,
composition: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"] as const,
noiseFloor: 2,
scoreVersion: 2 as const,
scoreVersion: SCORE_VERSION,
},
coverage,
confidence: { score: 100, band: "high" as const },
@@ -94,7 +94,7 @@ vi.mock("@/lib/analytics/score/reader", () => ({
pillars: [],
delta: legacy?.delta ?? null,
deltaReason: null,
scoreVersion: 2 as const,
scoreVersion: SCORE_VERSION,
weightGoal: {
status: "insufficient" as const,
coverage,
@@ -154,6 +154,7 @@ import {
} from "../snapshot";
import type { ModuleKey } from "@/lib/modules/gate";
import { MODULE_KEYS } from "@/lib/modules/registry";
import { SCORE_VERSION } from "@/lib/analytics/score/types";
const emptySummary = {
count: 0,
@@ -370,7 +371,7 @@ describe("buildDashboardSnapshot — per-type thick-phase gate", () => {
confidence: { score: 100, band: "high" },
composition: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"],
deltaReason: null,
scoreVersion: 2,
scoreVersion: SCORE_VERSION,
bandSetter: "BLOOD_PRESSURE",
// v1.21.2 (A5 / A6) — additive narrative fields, null absent a
// disagreeing readiness set / a returned metric (the fake derived
@@ -516,7 +517,7 @@ describe("buildDashboardSnapshot — healthScore (warm phase only)", () => {
confidence: { score: 100, band: "high" },
composition: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"],
deltaReason: null,
scoreVersion: 2,
scoreVersion: SCORE_VERSION,
bandSetter: "BLOOD_PRESSURE",
// v1.21.2 (A5 / A6) — additive narrative fields, null here.
tension: null,