parent item mutations
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package cart
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cart_messages "git.k6n.net/mats/go-cart-actor/proto/cart"
|
||||
)
|
||||
|
||||
// Parent qty 1 → 2 resizes every direct child proportionally. Children all
|
||||
// start at qty 1 so the proportional factor of 2 doubles them.
|
||||
func TestChangeQuantity_ParentCascadesDoublingChildren(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 1, Price: 1000})
|
||||
parentLine := g.Items[0].Id
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 101, Sku: "C1", Quantity: 1, Price: 100, ParentId: &parentLine})
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 102, Sku: "C2", Quantity: 1, Price: 200, ParentId: &parentLine})
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.ChangeQuantity{Id: parentLine, Quantity: 2}); err != nil {
|
||||
t.Fatalf("change qty: %v", err)
|
||||
}
|
||||
|
||||
if g.Items[0].Quantity != 2 {
|
||||
t.Errorf("parent qty = %d, want 2", g.Items[0].Quantity)
|
||||
}
|
||||
for _, it := range g.Items {
|
||||
if it.Id == parentLine {
|
||||
continue
|
||||
}
|
||||
if it.Quantity != 2 {
|
||||
t.Errorf("child %s qty = %d, want 2 (cascaded from parent)", it.Sku, it.Quantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parent qty 2 → 3 with children at qty 2 → 3 (factor 3/2 = 1.5, integer math).
|
||||
func TestChangeQuantity_ParentCascadesNonIntegerRatio(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 2, Price: 1000})
|
||||
parentLine := g.Items[0].Id
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 101, Sku: "C1", Quantity: 2, Price: 100, ParentId: &parentLine})
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.ChangeQuantity{Id: parentLine, Quantity: 3}); err != nil {
|
||||
t.Fatalf("change qty: %v", err)
|
||||
}
|
||||
|
||||
// parent: 2 -> 3 (direct set), child: 2 * 3 / 2 = 3
|
||||
if g.Items[0].Quantity != 3 {
|
||||
t.Errorf("parent qty = %d, want 3", g.Items[0].Quantity)
|
||||
}
|
||||
if g.Items[1].Quantity != 3 {
|
||||
t.Errorf("child qty = %d, want 3 (proportional scale)", g.Items[1].Quantity)
|
||||
}
|
||||
}
|
||||
|
||||
// Scale-down that would round to 0 (parent 2 → 1, child qty 1 → (1*1)/2 = 0)
|
||||
// must clamp to 1 so the child is never silently dropped by integer math.
|
||||
func TestChangeQuantity_ParentScaleDownFloorsAtOne(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 2, Price: 1000})
|
||||
parentLine := g.Items[0].Id
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 101, Sku: "C1", Quantity: 1, Price: 100, ParentId: &parentLine})
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.ChangeQuantity{Id: parentLine, Quantity: 1}); err != nil {
|
||||
t.Fatalf("change qty: %v", err)
|
||||
}
|
||||
|
||||
if g.Items[0].Quantity != 1 {
|
||||
t.Errorf("parent qty = %d, want 1", g.Items[0].Quantity)
|
||||
}
|
||||
if g.Items[1].Quantity != 1 {
|
||||
t.Errorf("child qty = %d, want 1 (floor; integer math dropped to 0 without clamp)", g.Items[1].Quantity)
|
||||
}
|
||||
}
|
||||
|
||||
// Setting parent qty to 0 delegates to RemoveItem so the existing parent→
|
||||
// child cascade takes over. Unrelated lines are left untouched.
|
||||
func TestChangeQuantity_ZeroCascadesRemoval(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 1, Price: 1000})
|
||||
parentLine := g.Items[0].Id
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 101, Sku: "C1", Quantity: 1, Price: 100, ParentId: &parentLine})
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 200, Sku: "OTHER", Quantity: 1, Price: 500})
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.ChangeQuantity{Id: parentLine, Quantity: 0}); err != nil {
|
||||
t.Fatalf("change qty 0: %v", err)
|
||||
}
|
||||
|
||||
if len(g.Items) != 1 {
|
||||
t.Fatalf("items = %d, want 1 (only OTHER survives)", len(g.Items))
|
||||
}
|
||||
if g.Items[0].Sku != "OTHER" {
|
||||
t.Errorf("remaining sku = %q, want OTHER", g.Items[0].Sku)
|
||||
}
|
||||
}
|
||||
|
||||
// Changing a child's qty directly updates only that child — siblings and
|
||||
// the parent are untouched. (The UI no longer exposes this control, but
|
||||
// the HTTP surface still allows it for tools & direct API calls.)
|
||||
func TestChangeQuantity_ChildDoesNotCascade(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 1, Price: 1000})
|
||||
parentLine := g.Items[0].Id
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 101, Sku: "C1", Quantity: 1, Price: 100, ParentId: &parentLine})
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 102, Sku: "C2", Quantity: 1, Price: 200, ParentId: &parentLine})
|
||||
targetChild := g.Items[1].Id
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.ChangeQuantity{Id: targetChild, Quantity: 3}); err != nil {
|
||||
t.Fatalf("change child qty: %v", err)
|
||||
}
|
||||
|
||||
for _, it := range g.Items {
|
||||
switch it.Id {
|
||||
case parentLine:
|
||||
if it.Quantity != 1 {
|
||||
t.Errorf("parent qty = %d, want 1 (untouched on child edit)", it.Quantity)
|
||||
}
|
||||
case targetChild:
|
||||
if it.Quantity != 3 {
|
||||
t.Errorf("target child qty = %d, want 3", it.Quantity)
|
||||
}
|
||||
default:
|
||||
// Sibling
|
||||
if it.Quantity != 1 {
|
||||
t.Errorf("sibling %s qty = %d, want 1 (untouched on child edit)", it.Sku, it.Quantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cascade descends into grandchildren so deeply nested accessory trees
|
||||
// remain internally consistent with the top-level parent change.
|
||||
func TestChangeQuantity_NestedChildren(t *testing.T) {
|
||||
reg := NewCartMultationRegistry(NewCartMutationContext(nil))
|
||||
g := NewCartGrain(1, time.Now())
|
||||
ctx := context.Background()
|
||||
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 100, Sku: "P", Quantity: 1, Price: 1000})
|
||||
parentLine := g.Items[0].Id
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 101, Sku: "C1", Quantity: 1, Price: 100, ParentId: &parentLine})
|
||||
childLine := g.Items[1].Id
|
||||
mustApply(t, reg, g, &cart_messages.AddItem{ItemId: 102, Sku: "G1", Quantity: 1, Price: 50, ParentId: &childLine})
|
||||
|
||||
if _, err := reg.Apply(ctx, g, &cart_messages.ChangeQuantity{Id: parentLine, Quantity: 2}); err != nil {
|
||||
t.Fatalf("change qty: %v", err)
|
||||
}
|
||||
|
||||
// parent 1 -> 2; child 1 -> 2 (parent -> child cascade); grandchild 1 -> 2 (child -> grandchild cascade)
|
||||
for _, it := range g.Items {
|
||||
if it.Quantity != 2 {
|
||||
t.Errorf("line %s qty = %d, want 2 (nested cascade)", it.Sku, it.Quantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user