From 779cd3a0c8de40b62a23eaea27d63ffadbbd57e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 10 Sep 2026 00:37:22 +0200 Subject: [PATCH] test: ban a length rescale, not every literal thousand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guard read `* 1000` anywhere in the two mapping files as a re-implemented kilometre factor. Both files also turn seconds into milliseconds, so an unrelated conversion would have failed it for the wrong reason and the next person would have deleted the rule. A rescale now counts only in a length context: a distance identifier on the same line or within the five above it, which covers the body of a distance-named helper. The offending line is named in the failure. The matcher's limit is written next to it — a length conversion in a helper named nothing like a distance, more than five lines below its signature, still slips through. Checked both ways against the real file: a millisecond conversion passes, a `km * 1000` helper fails. Review finding L4. --- .../apple-health-unit-audit-guard.test.ts | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/__tests__/apple-health-unit-audit-guard.test.ts b/src/__tests__/apple-health-unit-audit-guard.test.ts index 2975cd92d..b8799aeaf 100644 --- a/src/__tests__/apple-health-unit-audit-guard.test.ts +++ b/src/__tests__/apple-health-unit-audit-guard.test.ts @@ -206,17 +206,41 @@ describe("Apple Health mapping — the record's own unit (issue #944)", () => { it("leaves the km/mi factors in one shared module", () => { // The workout path used to carry its own copy of the km/mi branch; the // duplicate is what let the `` path stay wrong for ten years. + // + // `* 1000` alone is not the offence — these files also turn seconds into + // milliseconds — so a rescale only counts when it stands in a LENGTH + // context: a distance identifier on the same line, or within the five + // lines above it (which covers a rescale in the body of a + // distance-named helper). The limit is the usual one for a matcher of + // this shape: a length conversion inside a helper named nothing like a + // distance, more than five lines below its own signature, slips it. const shared = join(process.cwd(), "src/lib/measurements/hk-units.ts"); const owners = [ "src/lib/measurements/apple-health-mapping.ts", "src/lib/measurements/import-apple-health-export.ts", ]; expect(readFileSync(shared, "utf8")).toContain("1609.344"); + + const RESCALE = /\*\s*1_?000(?!\d)/; + const LENGTH = /distance|metre|meter|\bkm\b/i; for (const rel of owners) { const source = readFileSync(join(process.cwd(), rel), "utf8"); - expect(source, `${rel} re-implements a length factor`).not.toMatch( - /1609\.344|\* 1000\b/, + expect(source, `${rel} re-implements the mile factor`).not.toContain( + "1609.344", ); + const lines = source.split("\n"); + const offenders = lines + .map((line, i) => ({ line, i })) + .filter( + ({ line, i }) => + RESCALE.test(line) && + lines.slice(Math.max(0, i - 5), i + 1).some((l) => LENGTH.test(l)), + ) + .map(({ line, i }) => `${rel}:${i + 1}: ${line.trim()}`); + expect( + offenders, + `${rel} re-implements a length factor — the km/mi conversion belongs in hk-units.ts`, + ).toEqual([]); } }); });