more stuff

This commit is contained in:
mats committed 2026-07-25 18:13:22 +02:00
1 parent de2c822657
commit 540aa48c44
2 files changed
+139 -99

No files matched your search

-99
View File
@@ -2,109 +2,10 @@ package inventory
import (
"context"
"sync"
"testing"
"time"
)
// InMemoryReservationPolicy is a thread-safe in-memory implementation of
// ReservationPolicy for tests — no Redis needed. Backed by a map from
// SKU+location to available quantity.
type InMemoryReservationPolicy struct {
mu sync.Mutex
qty map[string]int64 // key: "sku|location" → available quantity
held map[string]int64 // key: "sku|location|cartID" → reserved quantity
}
// NewInMemoryReservationPolicy creates a policy seeded with the given
// sku+location → available quantity entries. Each entry is a pair of
// (sku, location, qty); pass in threes.
func NewInMemoryReservationPolicy(seed ...any) *InMemoryReservationPolicy {
p := &InMemoryReservationPolicy{
qty: make(map[string]int64),
held: make(map[string]int64),
}
for i := 0; i+2 < len(seed); i += 3 {
sku, ok := seed[i].(SKU)
if !ok {
continue
}
loc, ok := seed[i+1].(LocationID)
if !ok {
continue
}
qty, ok := seed[i+2].(int64)
if !ok {
continue
}
p.qty[key(sku, loc)] = qty
}
return p
}
func key(sku SKU, loc LocationID) string { return string(sku) + "|" + string(loc) }
func heldKey(sku SKU, loc LocationID, c CartID) string {
return string(sku) + "|" + string(loc) + "|" + string(c)
}
func (p *InMemoryReservationPolicy) Reserve(_ context.Context, req CartReserveRequest) error {
p.mu.Lock()
defer p.mu.Unlock()
k := key(req.SKU, req.LocationID)
avail := p.qty[k]
hk := heldKey(req.SKU, req.LocationID, req.CartID)
if _, exists := p.held[hk]; exists {
// Already held — add to existing reservation
avail -= p.held[hk]
}
if avail < int64(req.Quantity) {
return ErrInsufficientStock
}
p.held[hk] += int64(req.Quantity)
return nil
}
func (p *InMemoryReservationPolicy) Release(_ context.Context, sku SKU, locationID LocationID, cartID CartID) error {
p.mu.Lock()
defer p.mu.Unlock()
delete(p.held, heldKey(sku, locationID, cartID))
return nil
}
func (p *InMemoryReservationPolicy) Available(_ context.Context, sku SKU, locationID LocationID) (int64, error) {
p.mu.Lock()
defer p.mu.Unlock()
k := key(sku, locationID)
avail := p.qty[k]
// Subtract all held quantities for this sku+location
for hk, held := range p.held {
if len(hk) > len(k) && hk[:len(k)] == k && hk[len(k)] == '|' {
avail -= held
}
}
if avail < 0 {
avail = 0
}
return avail, nil
}
// Reset clears all reservations and resets available quantities.
func (p *InMemoryReservationPolicy) Reset() {
p.mu.Lock()
defer p.mu.Unlock()
p.held = make(map[string]int64)
}
// ErrInsufficientStock is returned by Reserve when not enough stock is available.
var ErrInsufficientStock = &reservationError{"insufficient stock"}
type reservationError struct{ msg string }
func (e *reservationError) Error() string { return e.msg }
// ── Tests ────────────────────────────────────────────────────────────────
func TestInMemoryReserveAndRelease(t *testing.T) {