Files
HealthLog/src/lib/targets/__tests__/builders.test.ts
T
Marc-André Bombeck a7fcd6d249 fix(targets): show the weight target the user set, not the WHO band
The targets reference panel graded weight against the WHO BMI band even
for an account that had entered its own target, so the panel under the
weight chart contradicted the chart above it. An override now wins and
the card's source reads Custom, the same posture the glucose builder has
always taken with a user-pinned band. The BMI classification stays
WHO-derived: naming which category a reading falls in is a reference
fact, not a goal.
2026-07-27 23:27:14 +02:00

203 lines
5.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { buildGlucoseTargets } from "../glucose-builder";
import { buildMedicationTarget } from "../medication-builder";
import { buildMoodTargets } from "../mood-builder";
import { buildSleepTarget } from "../sleep-builder";
import { buildTargetPageSummary } from "../summary-builder";
import { buildVitalTargets } from "../vitals-builder";
const NOW = new Date("2026-07-21T12:00:00.000Z");
const TZ = "UTC";
describe("target section builders", () => {
it("keeps the vital target order and profile-gated cards on an empty profile", () => {
const result = buildVitalTargets({
recentMeasurements: [],
latestByType: {},
average30ByType: {},
heightCm: null,
age: null,
gender: null,
timezone: TZ,
now: NOW,
weightTargetOverride: null,
});
expect(result.targets.map((target) => target.type)).toEqual([
"WEIGHT",
"BLOOD_PRESSURE",
"PULSE",
"BODY_FAT",
"ACTIVITY_STEPS",
]);
expect(result.bpRange).toBeNull();
expect(result.targets.every((target) => target.insufficientData)).toBe(
true,
);
});
it("returns the sleep card with the exact empty-series public shape", () => {
expect(
buildSleepTarget({
sleepStageRows: [],
timezone: TZ,
sourcePriorityJson: null,
now: NOW,
}),
).toEqual({
type: "SLEEP_DURATION",
label: "Sleep duration",
current: null,
average30: null,
trend: null,
unit: "h",
range: { min: 7, max: 9 },
classification: null,
source: "AASM/SRS",
daysInRange7d: 0,
daysLogged7d: 0,
daysInRange30d: 0,
daysLogged30d: 0,
lastMetGoalAt: null,
streakDays: 0,
insufficientData: true,
consistency7d: [null, null, null, null, null, null, null],
});
});
it("omits medication compliance when no scheduled medications are active", () => {
expect(
buildMedicationTarget({
activeMedications: [],
intakeEvents: [],
timezone: TZ,
now: NOW,
}),
).toBeNull();
});
it("keeps mood hidden below the three-entry threshold", () => {
expect(
buildMoodTargets({
moodRollups: [
{
bucketStart: NOW,
count: 2,
mean: 4,
},
],
recentRawMood: null,
latestMoodEntry: { score: 4, moodLoggedAt: NOW },
timezone: TZ,
now: NOW,
}),
).toEqual([]);
});
it("emits glucose cards in public context order and skips empty contexts", () => {
const rows = [
{
value: 130,
measuredAt: NOW,
glucoseContext: "RANDOM" as const,
},
{
value: 90,
measuredAt: NOW,
glucoseContext: "FASTING" as const,
},
];
const targets = buildGlucoseTargets({
rows,
profile: {
heightCm: 180,
dateOfBirth: new Date("1985-01-01T00:00:00.000Z"),
gender: "MALE",
glucoseUnit: "mg/dL",
hasDiabetes: false,
thresholdsJson: null,
},
timezone: TZ,
now: NOW,
});
expect(targets.map((target) => target.type)).toEqual([
"BLOOD_GLUCOSE_FASTING",
"BLOOD_GLUCOSE_RANDOM",
]);
expect(targets[0]).toMatchObject({
label: "targets.glucoseFasting",
current: 90,
average30: 90,
unit: "mg/dL",
source: "ADA 2024 / DDG",
insufficientData: true,
});
});
it("keeps summary streak ties stable by public target order", () => {
expect(
buildTargetPageSummary([
{
type: "WEIGHT",
daysInRange7d: 4,
insufficientData: false,
streakDays: 3,
},
{
type: "PULSE",
daysInRange7d: 5,
insufficientData: false,
streakDays: 3,
},
{
type: "SLEEP_DURATION",
daysInRange7d: 7,
insufficientData: true,
streakDays: 2,
},
]),
).toEqual({
targetsMetThisWeek: 2,
totalTargets: 3,
streakHighlight: { metric: "WEIGHT", days: 3 },
});
});
});
/**
* v1.34 — the weight card grades against the user's own target when one is set,
* so the `/targets` reference panel and the weight chart above it name the same
* band. The panel used to show the WHO BMI band unconditionally.
*/
describe("buildVitalTargets — user weight target", () => {
const base = {
recentMeasurements: [],
latestByType: {},
average30ByType: {},
heightCm: 178,
age: 40,
gender: null,
timezone: TZ,
now: NOW,
};
it("shows the WHO BMI band when no target is set", () => {
const weight = buildVitalTargets({
...base,
weightTargetOverride: null,
}).targets.find((t) => t.type === "WEIGHT");
expect(weight?.source).toBe("WHO BMI");
expect(weight?.range).not.toEqual({ min: 74, max: 78 });
});
it("shows the user's own band, labelled as theirs, when one is set", () => {
const weight = buildVitalTargets({
...base,
weightTargetOverride: { min: 74, max: 78 },
}).targets.find((t) => t.type === "WEIGHT");
expect(weight?.range).toEqual({ min: 74, max: 78 });
expect(weight?.source).toBe("Custom");
});
});