package main import ( "strconv" "testing" "git.k6n.net/mats/platform/catalog" "git.k6n.net/mats/platform/money" ) // frameMeta builds the event.Meta a producer stamps for a snapshot frame / delta. func frameMeta(phase string, epoch int64) map[string]string { m := map[string]string{catalog.MetaEpoch: strconv.FormatInt(epoch, 10), catalog.MetaTenant: "se"} if phase != "" { m[catalog.MetaSnapshotPhase] = phase } return m } func enc(t *testing.T, ups []catalog.ProjectionUpdate) []byte { t.Helper() b, err := catalog.EncodeUpdates(ups) if err != nil { t.Fatalf("encode: %v", err) } return b } // TestCartCache_SnapshotThenDelta exercises the cart's cache through the real bus // framing: a full begin→chunk→end snapshot makes SKUs Get-able cold (no Apply), // then an epoch-matched delta overrides one and tombstones another. func TestCartCache_SnapshotThenDelta(t *testing.T) { c := mustCache(t) const epoch = int64(1000) if err := c.HandleFrame(frameMeta(catalog.SnapshotBegin, epoch), enc(t, nil)); err != nil { t.Fatal(err) } if err := c.HandleFrame(frameMeta(catalog.SnapshotChunk, epoch), enc(t, []catalog.ProjectionUpdate{ {Projection: catalog.Projection{ID: "sku:A", SKU: "A", PriceIncVat: money.Cents(100_00), TaxClass: "standard", ItemID: 1}}, {Projection: catalog.Projection{ID: "sku:B", SKU: "B", PriceIncVat: money.Cents(50_00), TaxClass: "reduced", ItemID: 2}}, })); err != nil { t.Fatal(err) } // end carries the total count for the completeness guard. endMeta := frameMeta(catalog.SnapshotEnd, epoch) endMeta[catalog.MetaCount] = "2" if err := c.HandleFrame(endMeta, enc(t, nil)); err != nil { t.Fatal(err) } if got, ok := c.Get("A"); !ok || got.PriceIncVat != 100_00 { t.Fatalf("A from snapshot = %+v ok=%v, want 10000", got, ok) } // Epoch-matched delta: bump A's price, delete B. if err := c.HandleFrame(frameMeta("", epoch), enc(t, []catalog.ProjectionUpdate{ {Projection: catalog.Projection{SKU: "A", PriceIncVat: money.Cents(90_00), TaxClass: "standard", ItemID: 1}}, {Projection: catalog.Projection{SKU: "B"}, Deleted: true}, })); err != nil { t.Fatal(err) } if got, _ := c.Get("A"); got.PriceIncVat != 90_00 { t.Fatalf("A after delta = %d, want 9000", got.PriceIncVat) } if _, ok := c.Get("B"); ok { t.Fatalf("B after delete should miss") } if !c.IsDeleted("B") { t.Fatalf("B should be tombstoned (skip HTTP fetch)") } }