From 4cb2f457bd09ffa649a0249e225e21375973c58e Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 25 Jul 2026 05:07:22 +0100 Subject: [PATCH 1/2] Record line 381 dayCount equivalent, reaching 100% mutation The base.dayCount:1 mutant is provably equivalent: childDaysFromParent uses dayCount only in the customisable_days branch, which processParentApiBooking rejects before reaching the fold. All 63 mutants now killed or suppressed. --- TODO.md | 18 ++++-------------- scripts/mutation/equivalent-mutants.txt | 1 + 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/TODO.md b/TODO.md index fc71b0c4c1..5a1b887da4 100644 --- a/TODO.md +++ b/TODO.md @@ -1397,19 +1397,9 @@ reorganisation. Starting point: move the two phone-index describe blocks into `storedPhoneIndex` helpers in whichever file needs them (or lift to `#test-utils` if both do). The webhook test file drops to ~380 lines. -## Mutation survivor in `src/features/api/folded-booking.ts` (direct tests) +## Mutation coverage of `src/features/api/folded-booking.ts` (direct tests) Direct tests at `test/features/api/folded-booking.test.ts` and -`test/features/api/folded-booking/parent-booking.test.ts` kill all but one mutant on the unchanged `folded-booking.ts`; -four equivalents (lines 87, 118, 176, 301) are recorded in -`scripts/mutation/equivalent-mutants.txt`. One survivor remains, noted here -because it is a deliberate non-kill (testing it would assert an -implementation detail, not a user-visible contract): - -- `src/features/api/folded-booking.ts:381:17 1 → 0` — - `dayCount: 1` in the `FoldBase` `processParentApiBooking` builds. The fold - computes its own `fold.dayCount` (e.g. `3` for a customisable child) which - the intent carries, so the base value does not surface in any observable - output of the parent flow. Killing it would require asserting on the - intermediate `parentResolvedDuration(parent.listing, base.dayCount)` value, - which is an implementation detail rather than a user-visible contract. +`test/features/api/folded-booking/parent-booking.test.ts` kill every mutant on the unchanged `folded-booking.ts`. +Five equivalents (lines 87, 118, 176, 301, 381) are recorded in +`scripts/mutation/equivalent-mutants.txt` with proofs — no unsuppressed survivors remain. diff --git a/scripts/mutation/equivalent-mutants.txt b/scripts/mutation/equivalent-mutants.txt index 216108067c..7909c96861 100644 --- a/scripts/mutation/equivalent-mutants.txt +++ b/scripts/mutation/equivalent-mutants.txt @@ -908,3 +908,4 @@ src/features/api/folded-booking.ts:87:42 ?? → || # childrenByParentId is Ma src/features/api/folded-booking.ts:118:31 ?? → || # qtyByChild accumulates ApiQuantitySchema values (positive integers >= 1) or is unset; it is never 0 or NaN for validated inputs, so ?? 0 and || 0 agree src/features/api/folded-booking.ts:176:53 1 → 0 # the thankYouUrl guard checks fold.listings.length > 1 only when parentThankYouUrl is truthy (parent flow), which always folds parent + >= 1 child (length >= 2); package callers pass no parentThankYouUrl, so the && short-circuits and the check never runs src/features/api/folded-booking.ts:301:70 0 → 1 # feeSubtotal flows into priceCheckout → owedOrderForLedger, which drops all extras and sets total to 0; bookingFactsFromOrder reads extras (empty) → bookingFee: 0 regardless. The feeSubtotal value never reaches any observable output. +src/features/api/folded-booking.ts:381:17 1 → 0 # base.dayCount is read only by parentResolvedDuration → childDaysFromParent(parent, dayCount, 1), which uses its first argument (dayCount) only in the parent.customisable_days branch; processParentApiBooking rejects customisable parents at line 335 before reaching the fold, so that branch is unreachable. For daily parents the function returns clampDurationDays(parent.duration_days), and for standard parents it returns 1 (the standardValue) — neither reads dayCount, so 1 and 0 produce the same duration and thus the same fold output for every reachable input. From 026667a8f1783a08c68c791d162f5c1ba832f44f Mon Sep 17 00:00:00 2001 From: Stefan Date: Sat, 25 Jul 2026 05:48:56 +0100 Subject: [PATCH 2/2] Distinguish killed from suppressed equivalents in TODO --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 5a1b887da4..7bcffb636e 100644 --- a/TODO.md +++ b/TODO.md @@ -1400,6 +1400,6 @@ if both do). The webhook test file drops to ~380 lines. ## Mutation coverage of `src/features/api/folded-booking.ts` (direct tests) Direct tests at `test/features/api/folded-booking.test.ts` and -`test/features/api/folded-booking/parent-booking.test.ts` kill every mutant on the unchanged `folded-booking.ts`. +`test/features/api/folded-booking/parent-booking.test.ts` kill every non-equivalent mutant on the unchanged `folded-booking.ts`. Five equivalents (lines 87, 118, 176, 301, 381) are recorded in `scripts/mutation/equivalent-mutants.txt` with proofs — no unsuppressed survivors remain.