mutation fixes
This commit is contained in:
@@ -68,3 +68,125 @@ func TestAddItem_MergeCascadesToChildren(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Item identity for merge purposes includes ParentId: a standalone root
|
||||
// line (ParentId == nil) and a child line (ParentId == &someParentLine) that
|
||||
// happen to share an ItemId are different roles and MUST stay as distinct
|
||||
// lines. Merging would silently turn an accessory into a +N on the root
|
||||
// (or vice versa), corrupting the parent-child link.
|
||||
func TestAddItem_DoesNotMergeWhenParentIdDiffers(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
// Standalone root (no ParentId).
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 1, Price: 1000}); err != nil {
|
||||
t.Fatalf("add standalone: %v", err)
|
||||
}
|
||||
parentLine := g.Items[0].Id
|
||||
|
||||
// Same ItemId+StoreId but with ParentId set — must create a NEW line,
|
||||
// not bump the standalone's qty.
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 1, Price: 1000, ParentId: &parentLine}); err != nil {
|
||||
t.Fatalf("add child-form: %v", err)
|
||||
}
|
||||
|
||||
if len(g.Items) != 2 {
|
||||
t.Fatalf("items = %d, want 2 (different ParentId => distinct lines)", len(g.Items))
|
||||
}
|
||||
// Standalone untouched at qty 1; child-form is its own line at qty 1.
|
||||
for _, it := range g.Items {
|
||||
if it.Quantity != 1 {
|
||||
t.Errorf("line %d (parentId=%v) qty = %d, want 1 (no merge)", it.Id, it.ParentId, it.Quantity)
|
||||
}
|
||||
// One of them has nil ParentId and the other has &parentLine — guard
|
||||
// against an accidental merge ever flipping both to non-nil.
|
||||
}
|
||||
var seenNil, seenSet bool
|
||||
for _, it := range g.Items {
|
||||
if it.ParentId == nil {
|
||||
seenNil = true
|
||||
} else if *it.ParentId == parentLine {
|
||||
seenSet = true
|
||||
}
|
||||
}
|
||||
if !seenNil || !seenSet {
|
||||
t.Errorf("expected one line with nil ParentId and one with &parentLine; got nil=%v set=%v", seenNil, seenSet)
|
||||
}
|
||||
}
|
||||
|
||||
// Positive control: when BOTH sides have ParentId set to the SAME parent
|
||||
// line, the merge proceeds (this guards the new sameParent guard against
|
||||
// accidentally rejecting a legitimate "re-add same child of same parent"
|
||||
// case — e.g. an idempotent retry from a flaky request).
|
||||
func TestAddItem_MergesWhenBothParentIdMatch(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 1, Price: 1000}); err != nil {
|
||||
t.Fatalf("add parent: %v", err)
|
||||
}
|
||||
parentLine := g.Items[0].Id
|
||||
|
||||
// First child: ItemId 200 under parent.
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 200, Sku: "C", Quantity: 1, Price: 100, ParentId: &parentLine}); err != nil {
|
||||
t.Fatalf("add child: %v", err)
|
||||
}
|
||||
// Re-add the SAME child (same ItemId+StoreId+ParentId) — must merge into
|
||||
// qty 2, not create a second accessory line.
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 200, Sku: "C", Quantity: 1, Price: 100, ParentId: &parentLine}); err != nil {
|
||||
t.Fatalf("re-add child: %v", err)
|
||||
}
|
||||
|
||||
if len(g.Items) != 2 {
|
||||
t.Fatalf("items = %d, want 2 (parent + merged child)", len(g.Items))
|
||||
}
|
||||
for _, it := range g.Items {
|
||||
switch it.Id {
|
||||
case parentLine:
|
||||
if it.Quantity != 1 {
|
||||
t.Errorf("parent qty = %d, want 1 (untouched)", it.Quantity)
|
||||
}
|
||||
default:
|
||||
if it.Quantity != 2 {
|
||||
t.Errorf("child qty = %d, want 2 (merged on same-PARENT re-add)", it.Quantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same ItemId+StoreId+both with ParentId set to *different* parent lines
|
||||
// must also stay distinct — children belong to their own parent.
|
||||
func TestAddItem_DoesNotMergeAcrossDifferentParentLines(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 100, Sku: "P1", Quantity: 1, Price: 500}); err != nil {
|
||||
t.Fatalf("add parent A: %v", err)
|
||||
}
|
||||
parentA := g.Items[0].Id
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 200, Sku: "P2", Quantity: 1, Price: 500}); err != nil {
|
||||
t.Fatalf("add parent B: %v", err)
|
||||
}
|
||||
parentB := g.Items[1].Id
|
||||
|
||||
// Two children of different parents sharing an ItemId — distinct lines.
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 999, Sku: "C", Quantity: 1, Price: 100, ParentId: &parentA}); err != nil {
|
||||
t.Fatalf("add child of A: %v", err)
|
||||
}
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.AddItem{ItemId: 999, Sku: "C", Quantity: 1, Price: 100, ParentId: &parentB}); err != nil {
|
||||
t.Fatalf("add child of B: %v", err)
|
||||
}
|
||||
|
||||
// 2 parent lines + 2 child lines = 4 total; no merges across.
|
||||
if len(g.Items) != 4 {
|
||||
t.Errorf("items = %d, want 4 (parents A,B + their distinct children)", len(g.Items))
|
||||
}
|
||||
for _, it := range g.Items {
|
||||
if it.Quantity != 1 {
|
||||
t.Errorf("line %d qty = %d, want 1 (no accidental merge)", it.Id, it.Quantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user