wishlist and dashboard
Build and Publish / BuildAndDeployAmd64 (push) Failing after 4s
Build and Publish / BuildAndDeployArm64 (push) Failing after 5s

This commit is contained in:
2026-07-01 13:04:55 +02:00
parent 4a37cb479c
commit 1c97a4bdb0
16 changed files with 792 additions and 340 deletions
+6
View File
@@ -6,6 +6,7 @@ import (
"time"
"git.k6n.net/mats/go-cart-actor/pkg/voucher"
cart_messages "git.k6n.net/mats/go-cart-actor/proto/cart"
"git.k6n.net/mats/go-redis-inventory/pkg/inventory"
)
@@ -136,6 +137,7 @@ type CartGrain struct {
Language string `json:"language"`
Version uint `json:"version"`
InventoryReserved bool `json:"inventoryReserved"`
Type cart_messages.CartType `json:"type,omitempty"`
Id CartId `json:"id"`
Items []*CartItem `json:"items"`
TotalPrice *Price `json:"totalPrice"`
@@ -298,6 +300,10 @@ func (c *CartGrain) UpdateTotals() {
}
if c.Type == cart_messages.CartType_WISHLIST || c.Type == cart_messages.CartType_OFFER {
return
}
for _, voucher := range c.Vouchers {
_, ok := voucher.AppliesTo(c)
voucher.Applied = false
+1
View File
@@ -87,6 +87,7 @@ func NewCartMultationRegistry(context *CartMutationContext) actor.MutationRegist
actor.NewMutation(RemoveLineItemMarking),
actor.NewMutation(SetLineItemCustomFields),
actor.NewMutation(SubscriptionAdded),
actor.NewMutation(context.SetCartType),
// actor.NewMutation(SubscriptionRemoved),
)
return reg
+2 -2
View File
@@ -64,7 +64,7 @@ func (c *CartMutationContext) AddItem(g *CartGrain, m *cart_messages.AddItem) er
if !sameStore {
continue
}
if c.UseReservations(existing) {
if g.Type == cart_messages.CartType_REGULAR && c.UseReservations(existing) {
if err := c.ReleaseItem(ctx, g.Id, existing.Sku, existing.StoreId); err != nil {
log.Printf("failed to release item %d: %v", existing.Id, err)
}
@@ -152,7 +152,7 @@ func (c *CartMutationContext) AddItem(g *CartGrain, m *cart_messages.AddItem) er
DropShip: m.DropShip,
}
if needsReservation && c.UseReservations(cartItem) {
if g.Type == cart_messages.CartType_REGULAR && needsReservation && c.UseReservations(cartItem) {
endTime, err := c.ReserveItem(ctx, g.Id, m.Sku, m.StoreId, uint16(m.Quantity))
if err != nil {
return err
+97
View File
@@ -0,0 +1,97 @@
package cart
import (
"context"
"testing"
"time"
cart_messages "git.k6n.net/mats/go-cart-actor/proto/cart"
"git.k6n.net/mats/platform/inventory"
)
type mockReservationPolicy struct {
reserveCount int
releaseCount int
}
func (m *mockReservationPolicy) Reserve(ctx context.Context, req inventory.CartReserveRequest) error {
m.reserveCount++
return nil
}
func (m *mockReservationPolicy) Release(ctx context.Context, sku inventory.SKU, locationID inventory.LocationID, cartID inventory.CartID) error {
m.releaseCount++
return nil
}
func (m *mockReservationPolicy) Available(ctx context.Context, sku inventory.SKU, locationID inventory.LocationID) (int64, error) {
return 100, nil
}
func TestCartType_TransitionsAndReservations(t *testing.T) {
mockPolicy := &mockReservationPolicy{}
mutationCtx := NewCartMutationContext(mockPolicy)
reg := NewCartMultationRegistry(mutationCtx)
g := NewCartGrain(1, time.Now())
ctx := context.Background()
// 1. Initially REGULAR, adding item should trigger reservation
g.Type = cart_messages.CartType_REGULAR
_, err := reg.Apply(ctx, g, &cart_messages.AddItem{
ItemId: 101,
Sku: "SKU-1",
Quantity: 1,
Price: 1000,
InventoryTracked: true,
})
if err != nil {
t.Fatalf("AddItem (REGULAR) failed: %v", err)
}
if mockPolicy.reserveCount != 1 {
t.Errorf("Expected 1 reserve call, got %d", mockPolicy.reserveCount)
}
// 2. Set type to WISHLIST. This should release active reservations.
_, err = reg.Apply(ctx, g, &cart_messages.SetCartType{
Type: cart_messages.CartType_WISHLIST,
})
if err != nil {
t.Fatalf("SetCartType (WISHLIST) failed: %v", err)
}
if g.Type != cart_messages.CartType_WISHLIST {
t.Errorf("Expected type to be WISHLIST, got %v", g.Type)
}
if mockPolicy.releaseCount != 1 {
t.Errorf("Expected 1 release call during transition, got %d", mockPolicy.releaseCount)
}
// 3. AddItem in WISHLIST should NOT trigger reservation
_, err = reg.Apply(ctx, g, &cart_messages.AddItem{
ItemId: 102,
Sku: "SKU-2",
Quantity: 1,
Price: 2000,
InventoryTracked: true,
})
if err != nil {
t.Fatalf("AddItem (WISHLIST) failed: %v", err)
}
if mockPolicy.reserveCount != 1 { // Should still be 1 from step 1
t.Errorf("Expected reserve count to remain 1, got %d", mockPolicy.reserveCount)
}
// 4. Verify simple price sum is updated, but vouchers are not applied
g.Vouchers = append(g.Vouchers, &Voucher{
Code: "DISCOUNT",
Value: 500,
Applied: false,
})
g.UpdateTotals()
// Total price should be sum of 1000 + 2000 = 3000
if g.TotalPrice.IncVat != 3000 {
t.Errorf("Expected total price 3000, got %d", g.TotalPrice.IncVat)
}
if g.Vouchers[0].Applied {
t.Errorf("Expected voucher to NOT be applied in WISHLIST")
}
}
+1 -1
View File
@@ -64,7 +64,7 @@ func (c *CartMutationContext) ChangeQuantity(g *CartGrain, m *messages.ChangeQua
if item == nil {
return fmt.Errorf("ChangeQuantity: item id %d not found", m.Id)
}
if c.UseReservations(item) {
if g.Type == messages.CartType_REGULAR && c.UseReservations(item) {
if item.ReservationEndTime != nil {
err := c.ReleaseItem(ctx, g.Id, item.Sku, item.StoreId)
if err != nil {
+40
View File
@@ -0,0 +1,40 @@
package cart
import (
"context"
"fmt"
messages "git.k6n.net/mats/go-cart-actor/proto/cart"
)
func (c *CartMutationContext) SetCartType(g *CartGrain, m *messages.SetCartType) error {
if m == nil {
return fmt.Errorf("SetCartType: nil payload")
}
oldType := g.Type
newType := m.Type
if oldType == newType {
return nil
}
g.mu.Lock()
defer g.mu.Unlock()
g.Type = newType
// If transitioning from regular to wishlist or offer, release any active reservations
if oldType == messages.CartType_REGULAR && (newType == messages.CartType_WISHLIST || newType == messages.CartType_OFFER) {
ctx := context.Background()
for _, item := range g.Items {
if item.ReservationEndTime != nil {
_ = c.ReleaseItem(ctx, g.Id, item.Sku, item.StoreId)
item.ReservationEndTime = nil
}
}
}
g.UpdateTotals()
return nil
}