package pricing import ( "testing" "textmachine/platform/internal/money" ) func model(t *testing.T) Model { t.Helper() m, err := New(DefaultHoldFactorPercent) if err != nil { t.Fatal(err) } return m } // book is a priced book of `n` equal chapters, one unit each. func book(n int, perChapter, stepMax, bookOnce money.MicroUSD) Book { b := Book{StepMax: stepMax, BookOnce: bookOnce} for i := 1; i <= n; i++ { b.Remaining = append(b.Remaining, Chapter{Number: i, Units: 1, Expected: perChapter, SourceChars: 1000}) } return b } // A cushion below the engine's own expected bill is a discount, not a cushion: every purchase under // it would latch before it delivered what it quoted. Refused at construction so an operator learns // it at boot rather than from a user's paused run. func TestAHoldFactorBelowTheExpectedBillIsRefused(t *testing.T) { for _, v := range []int{99, 0, -1} { if _, err := New(v); err == nil { t.Errorf("a hold factor of %d%% was accepted", v) } } if _, err := New(100); err != nil { t.Errorf("exactly the expected bill is a legal (if tight) cushion: %v", err) } } // ⛔ THE REGRESSION OF PD-440, AND IT IS THE REASON THIS PACKAGE WAS REWRITTEN. // // Under the per-chapter constant a run's new headroom was `chaptersLeft × $0.03`, so the last two // chapters of ANY book were bought with $0.06 against a single editor reservation of about $0.07: // the run was admitted, died on its first reservation, moved nothing, and did so at ANY balance. // The measured floor was the same by two independent readings (9.25 and 9.31 chapters), so a book // was translatable only from ten undelivered chapters up. // // The cure is not a bigger constant — the old headroom SHRINKS with the book while the cost of one // indivisible call does not — it is that `step_max` is ADDED rather than compared against. func TestTheLastTwoChaptersOfABookAreBuyable(t *testing.T) { m := model(t) // The measured shapes of 04.09: a chapter's expected bill around five cents, one editor // reservation $0.069828 (`denied estimate` in the engine's own refusal), the book-level bound $2. const chapter, stepMax, bookOnce = money.MicroUSD(50_000), money.MicroUSD(69_828), money.MicroUSD(2_000_000) tail := book(2, chapter, stepMax, bookOnce) q := m.Quote(tail, money.MicroUSD(500_000), 0) if q.Hold <= stepMax { t.Fatalf("the last two chapters reserve %s, which does not clear ONE reservation of %s — "+ "the run would be admitted and die having moved nothing (PD-440)", q.Hold.USD(), stepMax.USD()) } // And the old arithmetic, stated here so the regression is legible rather than implied. if old := money.MicroUSD(2 * 30_000); old >= stepMax { t.Fatalf("the fixture no longer reproduces the defect: 2 chapters at the old $0.03 rate is %s, "+ "which already clears a reservation of %s", old.USD(), stepMax.USD()) } // One chapter left is the hardest case of all and it must still be buyable. if one := m.Quote(book(1, chapter, stepMax, bookOnce), money.MicroUSD(500_000), 0); one.Hold <= stepMax { t.Errorf("the last chapter reserves %s against a reservation of %s", one.Hold.USD(), stepMax.USD()) } } // ⛔ THE OTHER HALF OF THE SAME WALL, and it is the one that would have been REINTRODUCED by the // obvious reading of the engine's projection. // // The book-level figure `expected_usd` INCLUDES `book_once_usd`, and that term is a BOUND on two // gates — $2.00 flat on the shipped arm, for a book of three chapters and one of three hundred // alike. Priced from it, a five-chapter book expected to cost a quarter of a dollar would demand // two dollars and change: unbuyable to anyone holding less, at any length of book. So the base of // the hold is the ORDER's own chapters, and the bound is added only when the balance carries it. func TestAFlatBookLevelBoundDoesNotPutATwoDollarThresholdUnderEveryPurchase(t *testing.T) { m := model(t) const chapter, stepMax, bookOnce = money.MicroUSD(50_000), money.MicroUSD(69_828), money.MicroUSD(2_000_000) b := book(5, chapter, stepMax, bookOnce) // A buyer holding one dollar — less than the book-level bound, more than the book costs. const balance = money.MicroUSD(1_000_000) opts := m.Order(b, balance) if opts.Verdict != VerdictCoversAll { t.Fatalf("a $1 balance over a book expected to cost %s answered %q; the flat $%s bound is "+ "back under the purchase", (chapter * 5).USD(), opts.Verdict, bookOnce.USD()) } if opts.AffordableChapters != 5 { t.Errorf("affordable %d of 5 chapters at a balance of %s", opts.AffordableChapters, balance.USD()) } // …and the buyer is TOLD what they did not get. A silent degradation of the book-wide // consistency pass is the failure this member exists to make visible (D39.198, D39.202 §3). if opts.Whole.BondFunded { t.Error("a balance that cannot carry the book-level pass reported it as funded") } } // The bond is funded whenever the balance carries it on top of the order, because the pass it pays // for is the mechanism behind consistency of terms across a book — the owner's first priority. What // is refused is only the version of it that makes short books unbuyable. func TestTheBookLevelBoundIsFundedWheneverTheBalanceCarriesIt(t *testing.T) { m := model(t) const chapter, stepMax, bookOnce = money.MicroUSD(50_000), money.MicroUSD(69_828), money.MicroUSD(2_000_000) b := book(5, chapter, stepMax, bookOnce) rich := m.Quote(b, money.MicroUSD(25_000_000), 0) if !rich.BondFunded { t.Fatal("a balance of $25 did not fund a $2 book-level pass") } poor := m.Quote(b, money.MicroUSD(1_000_000), 0) if rich.Hold-poor.Hold != bookOnce { t.Errorf("the funded hold exceeds the unfunded one by %s, want exactly the bound %s", (rich.Hold - poor.Hold).USD(), bookOnce.USD()) } // A deployment that runs no book-level pass has nothing to warn about, and «true» is the honest // answer there: no consistency work is going unpaid. if q := m.Quote(book(5, chapter, stepMax, 0), money.MicroUSD(1000), 0); !q.BondFunded { t.Error("a deployment with no book-level pass reported an unfunded one") } } // The hold is `k × expected + step_max`, and the `+` is load-bearing rather than cosmetic: the // engine compares its ceiling against the book's CUMULATIVE spend on every reservation, so the LAST // call of an order needs a whole reservation of room ON TOP of everything the order has already // been billed. Under `max(k × expected, step_max)` any order whose bill exceeds one reservation // ends one call short of what was bought. func TestTheHoldIsTheCushionedBillPlusOneWholeReservation(t *testing.T) { m := model(t) const expected, stepMax = money.MicroUSD(1_000_000), money.MicroUSD(70_000) hold, _ := m.Hold(expected, stepMax, 0, money.MicroUSD(0)) want := expected*money.MicroUSD(DefaultHoldFactorPercent)/100 + stepMax if hold != want { t.Fatalf("hold %s, want %s", hold.USD(), want.USD()) } if hold <= expected+stepMax { t.Errorf("the hold %s leaves no cushion over the expected bill %s plus a reservation %s", hold.USD(), expected.USD(), stepMax.USD()) } } // Affordability WALKS the chapters. There is no average to divide by — that assumption is what the // per-chapter constant was — and a book whose chapters differ must be judged chapter by chapter. func TestAffordabilityWalksTheChaptersRatherThanDividingByAnAverage(t *testing.T) { m := model(t) b := Book{StepMax: 70_000, Remaining: []Chapter{ {Number: 1, Units: 1, Expected: 10_000}, {Number: 2, Units: 1, Expected: 10_000}, {Number: 3, Units: 1, Expected: 5_000_000}, // one enormous chapter }} // Enough for the two small ones and their reservation, nowhere near the third. got := m.Affordable(b, money.MicroUSD(200_000)) if got != 2 { t.Fatalf("affordable %d of 3, want 2 — the third chapter alone is %s", got, b.Remaining[2].Expected.USD()) } // An average would have said otherwise, and stating it here is what keeps the test about the // method rather than about the numbers. avg := (10_000 + 10_000 + 5_000_000) / 3 if byAverage := int(200_000 / avg); byAverage == got { t.Errorf("the fixture does not separate the two methods: an average of %d gives the same answer", avg) } } // The three verdicts, and the one that has to be told apart from an empty book: a book with nothing // left carries `covers_none` AND `chapters_left: 0`, which is what makes the two distinguishable on // the wire without a fourth word. func TestTheVerdictAnswersTheBuyersQuestionAndNamesTheEmptyBookByItsCount(t *testing.T) { m := model(t) const chapter, stepMax = money.MicroUSD(50_000), money.MicroUSD(69_828) b := book(10, chapter, stepMax, 0) for _, tc := range []struct { name string balance money.MicroUSD want string }{ {"the whole book fits", 25_000_000, VerdictCoversAll}, {"part of it fits", 400_000, VerdictCoversPart}, {"not even one chapter", 1_000, VerdictCoversNone}, } { if got := m.Order(b, tc.balance).Verdict; got != tc.want { t.Errorf("%s: verdict %q, want %q", tc.name, got, tc.want) } } empty := m.Order(Book{StepMax: stepMax}, money.MicroUSD(25_000_000)) if empty.Verdict != VerdictCoversNone || empty.ChaptersLeft != 0 { t.Errorf("a fully translated book: %q with %d chapters left", empty.Verdict, empty.ChaptersLeft) } } // A partial order names the chapter it stops at; the whole remainder deliberately does not. The // asymmetry is what keeps «the whole book» meaning the whole book after a re-cut moves the numbers. func TestOnlyAPartialOrderFreezesAChapterNumber(t *testing.T) { m := model(t) b := book(10, 50_000, 69_828, 0) if q := m.Quote(b, money.MicroUSD(25_000_000), 4); q.ThroughChapter != 4 || q.Chapters != 4 { t.Errorf("a four-chapter order: through %d, chapters %d", q.ThroughChapter, q.Chapters) } for _, n := range []int{0, 10, 99} { if q := m.Quote(b, money.MicroUSD(25_000_000), n); q.ThroughChapter != 0 || q.Chapters != 10 { t.Errorf("an order of %d over ten chapters: through %d, chapters %d — the whole remainder "+ "must not freeze a chapter number", n, q.ThroughChapter, q.Chapters) } } } // The character order resolves to a prefix of UNITS and rounds UP: a buyer who asks for ten thousand // characters gets the unit that CONTAINS the ten-thousandth, never one short of it. Characters are // what a person expresses; units are what the engine ships and stops at. func TestACharacterOrderRoundsUpToAWholeUnitAndCannotOverrunTheBook(t *testing.T) { units := []Unit{{ID: "u1", SourceChars: 100}, {ID: "u2", SourceChars: 100}, {ID: "u3", SourceChars: 100}} for _, tc := range []struct { want int64 n int ok bool }{{1, 1, true}, {100, 1, true}, {101, 2, true}, {200, 2, true}, {201, 3, true}, {10_000, 3, true}, {0, 0, false}, {-5, 0, false}} { got, ok := UnitsFor(units, tc.want) if got != tc.n || ok != tc.ok { t.Errorf("%d characters of a 300-character book buys %d units (ok %v), want %d (ok %v)", tc.want, got, ok, tc.n, tc.ok) } } if got, ok := UnitsFor(nil, 500); got != 0 || ok { t.Errorf("a book with nothing left buys %d units (ok %v)", got, ok) } // ⚠ A UNIT OF UNKNOWN SIZE STOPS THE ANSWER rather than being skipped. Zero sizes make every // prefix fall short, so a loop that ran to the end would answer «the whole book» — a partial // order silently becoming a total one, which is the shape this whole pack exists to prevent. blind := []Unit{{ID: "u1", SourceChars: 100}, {ID: "u2"}, {ID: "u3", SourceChars: 100}} if got, ok := UnitsFor(blind, 5000); ok { t.Errorf("a book whose unit sizes did not arrive sold %d units as if they had", got) } } // ⛔ A CHARACTER ORDER IS PRICED FROM ITS UNITS, not from the chapters they fall in — and on a book // with no chapter structure those are the same chapter, so the difference is the whole order. // // Measured on the shape that made it visible: a `none` book is ONE chapter of five units, so quoting // a one-unit order at the chapter's price quotes the WHOLE BOOK. The buyer holding enough for one // unit was refused, and the buyer holding more had five times too much frozen. func TestACharacterOrderIsPricedFromItsUnitsAndNotFromTheChapterTheyFallIn(t *testing.T) { m := model(t) const chapter, stepMax = money.MicroUSD(150_000), money.MicroUSD(69_828) // one chapter, five units b := Book{StepMax: stepMax, Remaining: []Chapter{ {ID: "c1", Number: 1, Units: 5, Expected: chapter, SourceChars: 5000}, }} units := make([]Unit, 5) for i := range units { units[i] = Unit{ID: "u" + string(rune('1'+i)), Expected: 30_000, SourceChars: 1000} } one := m.QuoteUnits(b, money.MicroUSD(10_000_000), units, 1, 1) if one.Expected != 30_000 { t.Fatalf("one unit is quoted at %s, want the unit's own %s — the chapter's price is %s", one.Expected.USD(), money.MicroUSD(30_000).USD(), chapter.USD()) } whole := m.Quote(b, money.MicroUSD(10_000_000), 0) if one.Hold >= whole.Hold { t.Errorf("a one-unit order reserves %s and the whole book %s: the order does not exist as a "+ "purchase", one.Hold.USD(), whole.Hold.USD()) } // And the buyer who can afford exactly one unit is not refused it. if afford := one.Hold; afford > money.MicroUSD(120_000) { t.Errorf("one unit of a $0.03 book reserves %s, which no buyer of one unit holds", afford.USD()) } } // A book with nothing left to buy quotes ZERO, not «one reservation plus a flat book-level bound». // The pair «expected $0.00, reserve $2.07» is arithmetic reaching a screen as an offer for a book // that is finished. func TestAFinishedBookQuotesNothingRatherThanTheBookLevelBound(t *testing.T) { m := model(t) got := m.Order(Book{StepMax: 69_828, BookOnce: 2_000_000}, money.MicroUSD(25_000_000)) if got.Whole.Expected != 0 || got.Whole.Hold != 0 { t.Errorf("a finished book is quoted %s expected / %s held", got.Whole.Expected.USD(), got.Whole.Hold.USD()) } if !got.Whole.BondFunded { t.Error("a finished book reports an unfunded consistency pass it will never run") } } // An absurd projection — one no book produces, but one a document that is not a manifest could — must // not wrap the int64 into a hold SMALLER than the bill it covers. Not a wrap into the negative, which // the ledger would refuse: a plausible, small, wrong number. func TestAnAbsurdProjectionCannotWrapTheHoldBelowTheBill(t *testing.T) { m := model(t) for _, expected := range []money.MicroUSD{maxExpected, maxExpected * 4, 1 << 62} { hold, _ := m.Hold(expected, 69_828, 0, 0) if hold <= 0 { t.Errorf("expected %d gave a hold of %d", expected, hold) } if capped := maxExpected*money.MicroUSD(DefaultHoldFactorPercent)/100 + 69_828; hold != capped { t.Errorf("expected %d gave a hold of %d, want the bounded %d", expected, hold, capped) } } } // The minimum of the money slider is ONE indivisible reservation, so the slider cannot express an // order under which no run could move at all (unified backlog row 276). func TestTheMoneySlidersMinimumIsOneIndivisibleReservation(t *testing.T) { m := model(t) const stepMax = money.MicroUSD(69_828) got := m.Order(book(10, 50_000, stepMax, 0), money.MicroUSD(25_000_000)) if got.MinHold != stepMax { t.Errorf("the slider's minimum is %s, want the engine's own step %s", got.MinHold.USD(), stepMax.USD()) } } // ⚠ THE CUSHION IS DERIVED, NOT MEASURED, and this test pins the band its derivation gives rather // than the value — so that the measurement of unified backlog row 281, when it lands, REPLACES the // number instead of being argued against it. // // Above 100%: `expected_usd` excludes retries, escalation, repair and additive reasoning by // construction, so a factor of exactly one latches on the first regeneration of every purchase. // Below 162%: the only live figures there are — two chapters of one paid run, $0.093 and $0.151 — // bound the guess at a ratio of 1.62, and that is TWO POINTS rather than a distribution. The // direction of the error is ratified (D39.206): a short hold costs one top-up, a long one freezes // credit, so err low. func TestTheHoldFactorStaysInTheBandItsDerivationGives(t *testing.T) { if DefaultHoldFactorPercent <= 100 || DefaultHoldFactorPercent >= 162 { t.Errorf("the hold factor is %d%%, outside the band its derivation justifies (100..162, exclusive)", DefaultHoldFactorPercent) } } // The running sum in Affordable and the prefix sum in Quote must answer the SAME thing, because the // first is the second made linear and nothing else. Two arithmetics that must agree are exactly // where a silent drift lives, so they are compared over a book whose chapters differ. func TestAffordabilityAgreesWithQuotingEveryPrefix(t *testing.T) { m := model(t) b := Book{StepMax: 69_828, BookOnce: 2_000_000} for i := 1; i <= 40; i++ { // Deliberately uneven, and one chapter far larger than its neighbours: an average would sail // past it and a running sum must not. expected := money.MicroUSD(10_000 + (i*7919)%90_000) if i == 17 { expected = 3_000_000 } b.Remaining = append(b.Remaining, Chapter{ID: "c", Number: i, Units: 1, Expected: expected}) } for _, balance := range []money.MicroUSD{0, 1, 69_828, 200_000, 1_000_000, 3_000_000, 25_000_000, 1 << 40} { want := 0 for i := range b.Remaining { if m.Quote(b, balance, i+1).Hold > balance { break } want = i + 1 } if got := m.Affordable(b, balance); got != want { t.Errorf("balance %s: the running sum says %d chapters and quoting every prefix says %d", balance.USD(), got, want) } } }