package pgstore import ( "context" "testing" "time" "textmachine/platform/internal/money" ) // spendbound_test.go: the word `min` in SpendBound, executed as a CHOICE. // // SpendBound says "the SMALLEST meter reading any LATER attempt of the same book recorded", and the // smallest is the only correct one: a later reading already contains the spend of everything // between, so bounding by a bigger one charges this attempt for its successor's work, which the // successor then pays for again — PD-159, measured at $0.10 charged as $2.10. // // The pin PD-159 names (runs.TestADeferredSettlementIsNotChargedForTheNextRunOfTheSameBook) puts // exactly ONE later attempt in the book, and over a set of one `min` and `max` are the same row — so // nothing in the battery executed the word. Measured: `min` -> `max` in SpendBound leaves the FULL // battery green (register row PD-376). Here there are three later readings and the smallest is // neither the first nor the last of them, so "the first later one" and "the last later one" fail it // as well. // // Taken from the review pack's own draft (docs/p8-review/axis1-money/r1_spendbound_test.go.txt) in // preference to its sibling a1_: that one raw-INSERTs its runs and its smallest baseline happens to // be the first later attempt, so an "order by id limit 1" mutant would survive it. // // Mutations caught: `min` -> `max`; `min` -> the first later attempt; dropping the `> $2` that makes // "later" mean later. func TestTheSpendBoundIsTheSmallestLaterBaselineAndNotMerelyALaterOne(t *testing.T) { s, ctx := testDB(t) now := fundedAccount(t, s, ctx, "u1", "10") seedBook(t, s, ctx, "bk1", "u1", 500) // A SECOND book of the same account, whose attempts are later still and cheaper than any of // bk1's. Without it the bound is the smallest later baseline in the DATABASE and the smallest // later baseline of THIS BOOK by coincidence, so dropping `r.book_id = $1` from the query — which // would charge one book's settlement against another book's meter — passes unnoticed. seedBook(t, s, ctx, "bk2", "u1", 500) subject := attemptWithBaseline(t, s, ctx, "bk1", now, 50_000) // Three attempts start AFTER it. Their baselines are deliberately out of id order: the smallest // sits in the MIDDLE, so neither "the first later one" nor "the last later one" is it. attemptWithBaseline(t, s, ctx, "bk1", now, 900_000) attemptWithBaseline(t, s, ctx, "bk1", now, 200_000) attemptWithBaseline(t, s, ctx, "bk1", now, 700_000) // Later than all of them and smaller than all of them — and about ANOTHER book. attemptWithBaseline(t, s, ctx, "bk2", now, 1_000) bound, err := s.SpendBound(ctx, "bk1", subject) if err != nil { t.Fatal(err) } if bound == nil { t.Fatal("no bound at all, want the smallest later baseline: the fixture is not the one this test claims") } if want := money.MicroUSD(200_000); *bound != want { t.Fatalf("the bound is %s, want %s — the SMALLEST later baseline OF THIS BOOK. A larger one "+ "already contains what the attempts between it and this one spent, so this settlement "+ "pays for their work and they pay for it again; another BOOK's baseline is not a bound "+ "on this one at all", bound.USD(), want.USD()) } } // attemptWithBaseline admits one run on bk1, records a spawn for it carrying the given book-meter // reading, then finishes the run so the next one can be admitted (runs_one_live_per_book). // // Through the ordinary doors and not raw INSERTs: what is under test is a query over rows the // platform itself writes, and a fixture that writes them by hand can agree with a query that no // producer would ever satisfy. func attemptWithBaseline(t *testing.T, s *Store, ctx context.Context, bookID string, now time.Time, baseline money.MicroUSD) int64 { t.Helper() run, err := s.StartRun(ctx, StartRunInput{UserID: "u1", BookID: bookID, OrderedChapters: 10, Ceiling: money.MicroUSD(300_000), Now: now}, 0, nil) if err != nil { t.Fatal(err) } claimed, err := s.RecordSpawn(ctx, SpawnRecord{AttemptID: run.AttemptID, Unit: "tm-run-" + run.ID, Binary: "/opt/engine/1/tmctl", Ceiling: 300_000, CeilingArg: 300_000 + baseline, Baseline: baseline}) if err != nil || !claimed { t.Fatalf("record spawn: claimed=%v err=%v", claimed, err) } exec(t, s, ctx, `update runs set finished_at = $2, status = 'ready' where id = $1`, run.ID, now) return run.AttemptID }