all the refactor
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
messages "git.k6n.net/mats/go-cart-actor/proto/cart"
|
||||
"git.k6n.net/mats/platform/catalog"
|
||||
"git.k6n.net/mats/platform/money"
|
||||
)
|
||||
|
||||
// TestApplyProjectionOverlay_PricePositiveOnly covers the contract that a cache
|
||||
// hit with a positive PriceIncVat overrides HTTP-fetched price, but a zero
|
||||
// (unbroadcast) PriceIncVat must NOT overwrite a valid HTTP price — it would
|
||||
// be a silent corruption.
|
||||
func TestApplyProjectionOverlay_PricePositiveOnly(t *testing.T) {
|
||||
startPrice := int64(95_00)
|
||||
base := func() *messages.AddItem {
|
||||
return &messages.AddItem{Sku: "X", Price: startPrice, Name: "http-name", Image: "http.jpg", Tax: 2500}
|
||||
}
|
||||
|
||||
// Positive cache value wins.
|
||||
m := base()
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", Title: "cache-name", Image: "cache.jpg", PriceIncVat: money.Cents(120_00), TaxClass: "reduced"})
|
||||
if m.Price != 120_00 {
|
||||
t.Errorf("positive cache price: got %d, want 12000", m.Price)
|
||||
}
|
||||
|
||||
// Zero cache value must NOT overwrite (treat as "no value").
|
||||
m = base()
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", PriceIncVat: money.Cents(0)})
|
||||
if m.Price != startPrice {
|
||||
t.Errorf("zero cache price overwrote HTTP price: got %d, want %d (HTTP)", m.Price, startPrice)
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyProjectionOverlay_TaxOnlyWhenClassSet verifies the TaxClass-skip on
|
||||
// an unclassified cache entry: HTTP rate is more trustworthy than a guessed
|
||||
// 2500 default, so msg.Tax stays untouched.
|
||||
func TestApplyProjectionOverlay_TaxOnlyWhenClassSet(t *testing.T) {
|
||||
base := func() *messages.AddItem {
|
||||
return &messages.AddItem{Sku: "X", Tax: 600} // HTTP-fetched lowered 6%
|
||||
}
|
||||
|
||||
// Empty TaxClass: HTTP rate preserved.
|
||||
m := base()
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", TaxClass: ""})
|
||||
if m.Tax != 600 {
|
||||
t.Errorf("empty TaxClass overwrote HTTP tax: got %d, want 600", m.Tax)
|
||||
}
|
||||
|
||||
// Non-empty: cache rate wins.
|
||||
m = base()
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", TaxClass: "reduced"})
|
||||
if m.Tax != 1200 {
|
||||
t.Errorf("TaxClass=reduced: got %d, want 1200", m.Tax)
|
||||
}
|
||||
|
||||
m = base()
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", TaxClass: "zero"})
|
||||
if m.Tax != 0 {
|
||||
t.Errorf("TaxClass=zero: got %d, want 0", m.Tax)
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyProjectionOverlay_DisplayFields verifies Title/Image override only
|
||||
// when the cache actually carries a value (an unbus-fed "" isn't propagated
|
||||
// over a valid HTTP value).
|
||||
func TestApplyProjectionOverlay_DisplayFields(t *testing.T) {
|
||||
base := func() *messages.AddItem {
|
||||
return &messages.AddItem{Sku: "X", Name: "http-name", Image: "http.jpg"}
|
||||
}
|
||||
|
||||
m := base()
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", Title: "cache-name", Image: "cache.jpg"})
|
||||
if m.Name != "cache-name" {
|
||||
t.Errorf("Title overlay: got %q, want %q", m.Name, "cache-name")
|
||||
}
|
||||
if m.Image != "cache.jpg" {
|
||||
t.Errorf("Image overlay: got %q, want %q", m.Image, "cache.jpg")
|
||||
}
|
||||
|
||||
// Empty cache Title/Image must NOT overwrite.
|
||||
m = base()
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", Title: "", Image: ""})
|
||||
if m.Name != "http-name" {
|
||||
t.Errorf("empty cache Title wiped HTTP name: got %q", m.Name)
|
||||
}
|
||||
if m.Image != "http.jpg" {
|
||||
t.Errorf("empty cache Image wiped HTTP image: got %q", m.Image)
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyProjectionOverlay_FlagFlipToTrueOnly locks down the
|
||||
// InventoryTracked / DropShip positive-flip semantics. False in the cache
|
||||
// is the proto zero value and may be unset on the wire; we don't second-guess.
|
||||
func TestApplyProjectionOverlay_FlagFlipToTrueOnly(t *testing.T) {
|
||||
// HTTP set both true (e.g. live SKU). Cache also has them true. Should
|
||||
// stay true.
|
||||
m := &messages.AddItem{InventoryTracked: true, DropShip: true}
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", InventoryTracked: true, DropShip: true})
|
||||
if !m.InventoryTracked || !m.DropShip {
|
||||
t.Fatalf("true-from-cache over a true-from-HTTP: %+v", m)
|
||||
}
|
||||
|
||||
// HTTP set false (default), cache says true → upgrade to true.
|
||||
m = &messages.AddItem{InventoryTracked: false, DropShip: false}
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", InventoryTracked: true, DropShip: true})
|
||||
if !m.InventoryTracked || !m.DropShip {
|
||||
t.Errorf("true-from-cache should flip false-from-HTTP: %+v", m)
|
||||
}
|
||||
|
||||
// HTTP already true, cache says false → must NOT downgrade (false would
|
||||
// be the proto zero value; trust the live HTTP signal here).
|
||||
m = &messages.AddItem{InventoryTracked: true, DropShip: true}
|
||||
ApplyProjectionOverlay(m, catalog.Projection{SKU: "X", InventoryTracked: false, DropShip: false})
|
||||
if !m.InventoryTracked || !m.DropShip {
|
||||
t.Errorf("false-from-cache must NOT overwrite true-from-HTTP: %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyProjectionOverlay_NilMsgSafe confirms the helper tolerates a nil
|
||||
// pointer (defensive — caller already validates, but a future caller may not).
|
||||
func TestApplyProjectionOverlay_NilMsgSafe(t *testing.T) {
|
||||
// Must not panic.
|
||||
ApplyProjectionOverlay(nil, catalog.Projection{SKU: "X"})
|
||||
}
|
||||
|
||||
// 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.Apply([]catalog.ProjectionUpdate{
|
||||
{Projection: catalog.Projection{SKU: "DL", PriceIncVat: money.Cents(10_00)}},
|
||||
{Projection: catalog.Projection{SKU: "DL"}, Deleted: true},
|
||||
})
|
||||
if _, ok := c.Get("DL"); ok {
|
||||
t.Fatalf("Get on tombstone: ok=true")
|
||||
}
|
||||
if !c.IsDeleted("DL") {
|
||||
t.Fatalf("IsDeleted on tombstone: false")
|
||||
}
|
||||
if c.IsDeleted("DL-fresh") {
|
||||
t.Fatalf("IsDeleted on absent entry: true")
|
||||
}
|
||||
if _, ok := c.Get("DL-fresh"); ok {
|
||||
t.Fatalf("Get on absent entry: ok=true")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user