Files
2026-09-14 20:50:19 +02:00

143 lines
3.6 KiB
Go

package pricing_test
import (
"context"
"errors"
"testing"
"github.com/google/uuid"
"backend/internal/modules/pricing"
)
// fakeRepository is an in-memory pricing.Repository used to unit-test
// pricing.Service without a real database.
type fakeRepository struct {
tiers map[uuid.UUID]*pricing.PriceTier
}
func newFakeRepository() *fakeRepository {
return &fakeRepository{tiers: map[uuid.UUID]*pricing.PriceTier{}}
}
func (r *fakeRepository) Create(_ context.Context, t *pricing.PriceTier) error {
cp := *t
r.tiers[t.ID] = &cp
return nil
}
func (r *fakeRepository) FindByID(_ context.Context, id uuid.UUID) (*pricing.PriceTier, error) {
t, ok := r.tiers[id]
if !ok {
return nil, pricing.ErrNotFound
}
cp := *t
return &cp, nil
}
func (r *fakeRepository) ListByProduct(_ context.Context, productID uuid.UUID) ([]*pricing.PriceTier, error) {
var list []*pricing.PriceTier
for _, t := range r.tiers {
if t.ProductID == productID {
cp := *t
list = append(list, &cp)
}
}
return list, nil
}
func (r *fakeRepository) Update(_ context.Context, t *pricing.PriceTier) error {
if _, ok := r.tiers[t.ID]; !ok {
return pricing.ErrNotFound
}
cp := *t
r.tiers[t.ID] = &cp
return nil
}
func (r *fakeRepository) Delete(_ context.Context, id uuid.UUID) error {
if _, ok := r.tiers[id]; !ok {
return pricing.ErrNotFound
}
delete(r.tiers, id)
return nil
}
func TestService_PriceForQuantity_MultipliesTierPrice(t *testing.T) {
repo := newFakeRepository()
svc := pricing.NewService(repo)
ctx := context.Background()
productID := uuid.New()
unitID := uuid.New()
// "5 kg -> 45.00" tier, spec section 16 example.
tier, err := svc.Create(ctx, productID, unitID, 5, 4500, 0)
if err != nil {
t.Fatalf("Create() error = %v", err)
}
total, gotTier, err := svc.PriceForQuantity(ctx, tier.ID, 2)
if err != nil {
t.Fatalf("PriceForQuantity() error = %v", err)
}
if total != 9000 {
t.Fatalf("PriceForQuantity() total = %d, want 9000 (2x 4500)", total)
}
if gotTier.ID != tier.ID {
t.Fatalf("PriceForQuantity() returned tier %s, want %s", gotTier.ID, tier.ID)
}
}
func TestService_PriceForQuantity_ZeroOrNegativeMultiplierTreatedAsOne(t *testing.T) {
repo := newFakeRepository()
svc := pricing.NewService(repo)
ctx := context.Background()
tier, err := svc.Create(ctx, uuid.New(), uuid.New(), 1, 1000, 0)
if err != nil {
t.Fatalf("Create() error = %v", err)
}
total, _, err := svc.PriceForQuantity(ctx, tier.ID, 0)
if err != nil {
t.Fatalf("PriceForQuantity() error = %v", err)
}
if total != 1000 {
t.Fatalf("PriceForQuantity() with multiplier=0 total = %d, want 1000 (treated as 1x)", total)
}
}
func TestService_PriceForQuantity_UnknownTierRejected(t *testing.T) {
svc := pricing.NewService(newFakeRepository())
_, _, err := svc.PriceForQuantity(context.Background(), uuid.New(), 1)
if !errors.Is(err, pricing.ErrNotFound) {
t.Fatalf("PriceForQuantity() error = %v, want ErrNotFound", err)
}
}
func TestService_ListByProduct_FiltersByProduct(t *testing.T) {
repo := newFakeRepository()
svc := pricing.NewService(repo)
ctx := context.Background()
productA := uuid.New()
productB := uuid.New()
unitID := uuid.New()
if _, err := svc.Create(ctx, productA, unitID, 1, 1000, 0); err != nil {
t.Fatalf("Create() error = %v", err)
}
if _, err := svc.Create(ctx, productB, unitID, 1, 2000, 0); err != nil {
t.Fatalf("Create() error = %v", err)
}
list, err := svc.ListByProduct(ctx, productA)
if err != nil {
t.Fatalf("ListByProduct() error = %v", err)
}
if len(list) != 1 || list[0].ProductID != productA {
t.Fatalf("ListByProduct(productA) = %+v, want exactly one tier for productA", list)
}
}