package promotions import "testing" // TestEvaluatePartialCart exercises the stateless evaluation path used by // POST /promotions/evaluate: items drive the total, qualifying effects apply, // and near-miss rules surface as pending nudges. func TestEvaluatePartialCart(t *testing.T) { svc := NewPromotionService(nil) rules := []PromotionRule{volymrabattRule()} now := timeZero() t.Run("items qualify for a tier", func(t *testing.T) { // 2 × 25 000 kr (incl. VAT) = 50 000 kr -> 9% tier. resp := svc.Evaluate(rules, EvaluateRequest{ Now: &now, Items: []EvalItem{ {Sku: "A", Quantity: 2, PriceIncVat: 2500000}, }, }) if resp.TotalDiscount.IncVat != 450000 { t.Errorf("discount = %d, want 450000", resp.TotalDiscount.IncVat) } if len(resp.AppliedPromotions) != 1 || resp.AppliedPromotions[0].Pending { t.Fatalf("want 1 applied (non-pending) effect, got %+v", resp.AppliedPromotions) } }) t.Run("total-only fallback surfaces a pending nudge", func(t *testing.T) { // No items, just a total below the entry tier. resp := svc.Evaluate(rules, EvaluateRequest{Now: &now, CartTotalIncVat: 1500000}) if resp.TotalDiscount.IncVat != 0 { t.Errorf("nothing should apply, discount = %d", resp.TotalDiscount.IncVat) } if len(resp.AppliedPromotions) != 1 || !resp.AppliedPromotions[0].Pending { t.Fatalf("want 1 pending nudge, got %+v", resp.AppliedPromotions) } if got := resp.AppliedPromotions[0].Progress["remaining"]; got != int64(500000) { t.Errorf("remaining = %v, want 500000", got) } }) }