refactor(measurements): single source of truth for cumulative day-sum types

CUMULATIVE_HK_TYPES (apple-health-mapping.ts) and CUMULATIVE_DAY_SUM_TYPES
(cumulative-day-sum.ts) enumerated the same five Apple Health cumulative
types in two literal blocks. A contributor adding a sixth type (e.g.
swimming strokes) only had to update one list and the system would
silently misreport for that type — either the list-view collapse would
not fire or the analytics pickCumulativeDaySum would skip it, depending
on which file the contributor touched.

Derive CUMULATIVE_DAY_SUM_TYPES from CUMULATIVE_HK_TYPES at module load
(sorted alphabetically for stable iteration) and add a vitest parity
assertion so the membership stays pinned. Both exports remain so every
existing import keeps working.
This commit is contained in:
Marc-André Bombeck committed 2026-05-17 15:16:56 +02:00
1 parent 6325c0e286
commit 3154c1bc36
2 files changed
+33 -12

No files matched your search

@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import { CUMULATIVE_HK_TYPES } from "../apple-health-mapping";
import {
CUMULATIVE_DAY_SUM_TYPES,
isCumulativeDaySumType,
@@ -105,3 +106,16 @@ describe("isCumulativeDaySumType", () => {
expect(isCumulativeDaySumType("MOOD")).toBe(false);
});
});
describe("CUMULATIVE_DAY_SUM_TYPES / CUMULATIVE_HK_TYPES parity", () => {
// v1.4.37 W10 — single source of truth. The day-sum array is derived
// from `CUMULATIVE_HK_TYPES` so adding a sixth cumulative HK type to
// the mapping module automatically flows through every downstream
// consumer (analytics route, exports, chart). The set equivalence
// guard pins the contract — divergence is impossible at runtime, but
// a future refactor that flips the derivation back to a literal
// would trip this assertion before shipping.
it("matches CUMULATIVE_HK_TYPES membership exactly", () => {
expect(new Set(CUMULATIVE_DAY_SUM_TYPES)).toEqual(CUMULATIVE_HK_TYPES);
});
});
+19 -12
View File
@@ -62,25 +62,32 @@ export function pickCumulativeDaySum<T extends CumulativeRow>(
return out;
}
import type { MeasurementType } from "@/generated/prisma/client";
import { CUMULATIVE_HK_TYPES } from "./apple-health-mapping";
/**
* v1.4.36 W4c — Canonical set of cumulative metric types whose
* dashboard tile should read the *day's sum* and not the latest
* slice. Kept here so the analytics route and any other consumer
* (Coach snapshot, exports) share the same source of truth.
* v1.4.36 W4c / v1.4.37 W10 — Canonical list of cumulative metric
* types whose dashboard tile should read the *day's sum* and not the
* latest slice.
*
* The set lives in `apple-health-mapping.ts` as `CUMULATIVE_HK_TYPES`
* (used by the measurements route + nightly drain + chart). This
* array is the same membership in stable alphabetical order so
* downstream consumers (analytics route, exports) can iterate
* deterministically. The W10 reconcile pass collapses the previously
* duplicated literal to a single derivation so adding a sixth type
* in `apple-health-mapping.ts` automatically flows through every
* cumulative-day-sum consumer.
*
* Note: SLEEP_DURATION is also cumulative but the route handles
* it inline because the picker is the canonical sleep source ladder
* ("sleep" metricKey vs "default"). Once Apple-Health passthrough
* lands and the sleep ladder needs the same shared treatment it can
* fold into this list.
* fold into `CUMULATIVE_HK_TYPES`.
*/
export const CUMULATIVE_DAY_SUM_TYPES = [
"ACTIVITY_STEPS",
"ACTIVE_ENERGY_BURNED",
"WALKING_RUNNING_DISTANCE",
"FLIGHTS_CLIMBED",
"TIME_IN_DAYLIGHT",
] as const;
export const CUMULATIVE_DAY_SUM_TYPES = Array.from(
CUMULATIVE_HK_TYPES,
).sort() as readonly MeasurementType[];
export type CumulativeDaySumType = (typeof CUMULATIVE_DAY_SUM_TYPES)[number];