package pgstore import ( "encoding/json" "testing" "textmachine/platform/internal/ingest" ) // EVERY history frame carries the book's revision and its structure version (canon §EventBase), and // this is the only test that asserts it over frames it did not name one by one. // // The asymmetry that made it necessary: the two fields are stamped in ONE place, `emitFrame`, so // every frame gains them for free — and a battery that reads frames for their own payloads gains // nothing from that. Removing both lines passed all 532 tests of this zone (reproduced by landing // the mutation, acceptance of the P7 pack, 21.08). What it costs on the wire is not cosmetic: a // client applies a frame only when its revision is at or above the one it holds, and drops it // otherwise — a frame with no revision is either dropped by every reader or applied out of order, // depending on how forgiving that reader is. // // It is written over the BUFFER rather than over one emitter for the same reason the stamping is in // one place: a frame kind added later is covered by this test on the day it is added, and a kind // that grew its own emitter without the stamp fails here rather than on a screen. // // Mutation caught: dropping either `payload["revision"]` or `payload["structure_version"]` from // emitFrame. func TestEveryFrameCarriesTheBooksRevisionAndStructureVersion(t *testing.T) { s, sink, _, _ := sinkFixture(t) ctx := t.Context() // ALL FIVE kinds through their real writers, which is what makes this a check of the stamp // rather than of one payload: progress from the bar, chapter and note from the fold of a flagged // pair, status from the bank stop, bank from the sidecar read-out. if err := apply(t, sink, 2, ingest.TypeProgress, ingest.Progress{ Draft: ingest.Counter{Done: 1, Total: 50}, Edit: ingest.Counter{Done: 0, Total: 50}, }); err != nil { t.Fatal(err) } if err := apply(t, sink, 3, ingest.TypeUnitDone, ingest.UnitDone{ Chapter: 1, Unit: 2, Wave: ingest.WaveDraft, Shipped: true, Flagged: true, Reason: "glossary_miss", }); err != nil { t.Fatal(err) } if err := apply(t, sink, 4, ingest.TypeBankStop, ingest.BankStop{}); err != nil { t.Fatal(err) } if err := s.SaveBank(ctx, "bk1", []ingest.BankTerm{ {ID: "t1", Src: "s", Dst: "d", Status: "proposed", Origin: "found", Sense: "one"}, }); err != nil { t.Fatal(err) } var revision int64 var structure int if err := s.pool.QueryRow(ctx, `select revision, structure_version from books where id = 'bk1'`).Scan(&revision, &structure); err != nil { t.Fatal(err) } frames, err := s.ReadFrames(ctx, "bk1", 0, 100) if err != nil { t.Fatal(err) } if len(frames) < 5 { t.Fatalf("the fixture produced %d frames: it is not exercising the emitters it claims", len(frames)) } seen := map[string]bool{} var last int64 for _, f := range frames { seen[f.Event] = true var base struct { Revision *int64 `json:"revision"` StructureVersion *int `json:"structure_version"` } if err := json.Unmarshal(f.Data, &base); err != nil { t.Fatalf("frame %d (%s) does not decode: %v", f.Position, f.Event, err) } switch { case base.Revision == nil: t.Errorf("frame %d (%s) carries no revision: a client cannot order it against what it holds", f.Position, f.Event) case *base.Revision < last: // Within one book the stamp comes off the row the emitting transaction has already moved, // so it never goes backwards. A frame that does is a stamp read before its own write. t.Errorf("frame %d (%s) carries revision %d after %d", f.Position, f.Event, *base.Revision, last) default: last = *base.Revision } if base.StructureVersion == nil { t.Errorf("frame %d (%s) carries no structure_version: a client cannot tell whether its chapter ids still exist", f.Position, f.Event) } else if *base.StructureVersion != structure { t.Errorf("frame %d (%s) carries structure_version %d and the book is at %d", f.Position, f.Event, *base.StructureVersion, structure) } } if last != revision { t.Errorf("the newest frame carries revision %d and the book is at %d", last, revision) } for _, kind := range []string{FrameStatus, FrameProgress, FrameChapter, FrameNote, FrameBank} { if !seen[kind] { t.Errorf("no %s frame was produced: this test is not covering the emitter it names", kind) } } }