carlos / platform Public

Per-line tax codes: the lost-answer test covers replay inside the window and recovery past it

The first mutant batch showed the recovery test advancing two hours and so
replaying under its key; past the window the listing is the only way
back, and there a line is told from its sibling by its metadata code
alone (mutant M9 now dies on it). Twenty-three mutants in a git-less tree
copy, each with its killing tests logged (taxcodes-mutants-round1.txt):
twenty-two die; M14 is equivalent (with spans in time order and
eligibility first, the span that starts during a day and the span that
holds its first instant are the same span on every eligible day).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 598a81e2775ef79f8c219478f49c0caa23b3b96a parent 24387f8
1 file changed, +24 −9
  • internal/console/billingtaxlifecycle_test.go +24 −9

CI cancelled — superseded by a newer push — run details

diff --git a/internal/console/billingtaxlifecycle_test.go b/internal/console/billingtaxlifecycle_test.go
index ce3be802b..035b6f255 100644
--- a/internal/console/billingtaxlifecycle_test.go
+++ b/internal/console/billingtaxlifecycle_test.go
@@ -90,27 +90,42 @@ func TestATwoCodeMonthInvoicesOneLinePerCode(t *testing.T) {
}
}
-// A lost answer to one of the two lines is recovered by the listing, the
-// line told from its sibling by its metadata code, through a paginated
-// listing too; nothing is sent twice.
+// A lost answer to one of the two lines is replayed under its key inside
+// the idempotency window and, past it, recovered by the listing, where the
+// line is told from its sibling (same meter, same digest, same amount) by
+// its metadata code; through a paginated listing too; nothing is sent
+// twice.
func TestATwoCodeLinesLostAnswerIsRecoveredByItsCode(t *testing.T) {
- for name, paged := range map[string]bool{"one page": false, "paged listing": true} {
+ for name, tc := range map[string]struct {
+ paged bool
+ later time.Duration
+ }{
+ "inside the window, one page": {false, 2 * time.Hour},
+ "past the window, one page": {false, 25 * time.Hour},
+ "past the window, paged listing": {true, 25 * time.Hour},
+ } {
t.Run(name, func(t *testing.T) {
ix := taxLifecycleFixture(t)
storeTwoCodeMonth(t, ix, func(total int64) int64 { return total / 2 })
- ix.fake.hasMoreLines = paged
- ix.fake.loseResponse["line-awake_seconds-txcd_personal"] = true
+ ix.fake.hasMoreLines = tc.paged
+ lost := "line-awake_seconds-txcd_personal"
+ ix.fake.loseResponse[lost] = true
if err := ix.run(t); !errors.Is(err, errBillingPending) {
t.Fatalf("lost line answer: %v", err)
}
- ix.now = ix.now.Add(2 * time.Hour)
+ ix.now = ix.now.Add(tc.later)
+ arrivalsBefore, _, _ := ix.fake.counts()
if err := ix.run(t); err != nil {
- t.Fatalf("recovery: %v", err)
+ t.Fatalf("after the loss: %v", err)
+ }
+ arrivalsAfter, _, _ := ix.fake.counts()
+ if tc.later > billingIdempotencyWindow && arrivalsAfter[lost] != arrivalsBefore[lost] {
+ t.Fatalf("%s resent past the window: %d -> %d", lost, arrivalsBefore[lost], arrivalsAfter[lost])
}
if rec := ix.month(t); rec.State != "complete" {
t.Fatalf("month: %+v", rec)
}
- ix.assertMutations(t, map[string]int{"draft": 1, "line-awake_seconds-txcd_business": 1, "line-awake_seconds-txcd_personal": 1, "finalize": 1, "pay": 1})
+ ix.assertMutations(t, map[string]int{"draft": 1, "line-awake_seconds-txcd_business": 1, lost: 1, "finalize": 1, "pay": 1})
})
}
}