cart: back projection cache with platform/catalog.ProjectionStore
Consumer side of the catalog-projection seam (C1): - catalogProjectionCache now wraps ProjectionStore[catalog.Projection] (identity transform — the cart's overlay uses ~all fields); delivery handler routes bus frames via HandleFrame (snapshot begin|chunk|end + epoch-stamped deltas). - Per-pod cold-start-ready .bin under CART_PROJECTION_DIR (pod-local, never shared) + IsDeleted tombstone passthrough; HTTP fetch demoted to last-resort. - Tests: test-only Apply shim + mustCache helper keep existing cases green; new snapshot-framing integration test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -142,7 +142,7 @@ func TestApplyProjectionOverlay_NilMsgSafe(t *testing.T) {
|
||||
// bypass the tombstone branch and fall through to the HTTP fetch — failing
|
||||
// the test for a wiring reason rather than the contract under test.
|
||||
func TestAddSkuToCartHandler_TombstoneReturns404(t *testing.T) {
|
||||
idx := newCatalogProjectionCache()
|
||||
idx := mustCache(t)
|
||||
idx.Apply([]catalog.ProjectionUpdate{
|
||||
{Projection: catalog.Projection{SKU: "DEL", PriceIncVat: money.Cents(10_00)}},
|
||||
{Projection: catalog.Projection{SKU: "DEL"}, Deleted: true},
|
||||
@@ -181,7 +181,7 @@ func TestAddSkuToCartHandler_TombstoneReturns404(t *testing.T) {
|
||||
// TestIsDeletedVsGet asserts the two methods don't conflict: a tombstoned SKU
|
||||
// is reported by both IsDeleted (true) and Get (miss).
|
||||
func TestIsDeletedVsGet(t *testing.T) {
|
||||
c := newCatalogProjectionCache()
|
||||
c := mustCache(t)
|
||||
c.Apply([]catalog.ProjectionUpdate{
|
||||
{Projection: catalog.Projection{SKU: "DL", PriceIncVat: money.Cents(10_00)}},
|
||||
{Projection: catalog.Projection{SKU: "DL"}, Deleted: true},
|
||||
@@ -395,7 +395,7 @@ func TestBuildItemGroups_CacheOnlySkipsHTTP(t *testing.T) {
|
||||
prod := newFakeProductServer(t)
|
||||
t.Setenv("PRODUCT_BASE_URL", prod.srv.URL)
|
||||
|
||||
idx := newCatalogProjectionCache()
|
||||
idx := mustCache(t)
|
||||
idx.Apply([]catalog.ProjectionUpdate{
|
||||
{Projection: catalog.Projection{
|
||||
SKU: "TOP",
|
||||
@@ -463,7 +463,7 @@ func TestBuildItemGroups_HTTPFallbackWhenChildrenPresent(t *testing.T) {
|
||||
prod := newFakeProductServer(t)
|
||||
t.Setenv("PRODUCT_BASE_URL", prod.srv.URL)
|
||||
|
||||
idx := newCatalogProjectionCache()
|
||||
idx := mustCache(t)
|
||||
idx.Apply([]catalog.ProjectionUpdate{
|
||||
{Projection: catalog.Projection{
|
||||
SKU: "PARENT", PriceIncVat: money.Cents(99_00),
|
||||
@@ -518,7 +518,7 @@ func TestAddSkuToCartHandler_CacheOnlySkipsHTTP(t *testing.T) {
|
||||
defer failSrv.Close()
|
||||
t.Setenv("PRODUCT_BASE_URL", failSrv.URL)
|
||||
|
||||
idx := newCatalogProjectionCache()
|
||||
idx := mustCache(t)
|
||||
idx.Apply([]catalog.ProjectionUpdate{
|
||||
{Projection: catalog.Projection{
|
||||
SKU: "OK", PriceIncVat: money.Cents(99_00),
|
||||
@@ -550,7 +550,7 @@ func TestBuildItemGroups_HTTPFallbackWhenCacheMiss(t *testing.T) {
|
||||
prod := newFakeProductServer(t)
|
||||
t.Setenv("PRODUCT_BASE_URL", prod.srv.URL)
|
||||
|
||||
idx := newCatalogProjectionCache() // empty
|
||||
idx := mustCache(t) // empty
|
||||
groups := buildItemGroups(context.Background(), []Item{
|
||||
{Sku: "MISS", Quantity: 1},
|
||||
}, "se", idx)
|
||||
@@ -575,7 +575,7 @@ func TestBuildItemGroups_HTTPFallbackWhenHasRequiredFieldsFalse(t *testing.T) {
|
||||
prod := newFakeProductServer(t)
|
||||
t.Setenv("PRODUCT_BASE_URL", prod.srv.URL)
|
||||
|
||||
idx := newCatalogProjectionCache()
|
||||
idx := mustCache(t)
|
||||
idx.Apply([]catalog.ProjectionUpdate{
|
||||
{Projection: catalog.Projection{
|
||||
SKU: "PARTIAL", PriceIncVat: money.Cents(99_00),
|
||||
|
||||
Reference in New Issue
Block a user