package tests import ( "gestion/db" "gestion/models" "testing" ) // ── Persistance des settings (save→reload) ────────────────────────────────── func TestUpdateSettings_FreeGiftsRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.FreeGiftsEnabled = true s.FreeGifts = []models.CategoryFreeGiftConfig{ { Category: "test", AllProducts: false, Products: []models.FreeGiftProductQuantity{ {ProductID: 111, Tiers: []models.FreeGiftTier{ {BuyQuantity: 10, FreeQuantity: 1}, {BuyQuantity: 20, FreeQuantity: 3}, }}, }, }, } if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings: %v", err) } loaded, err := testDB.GetSettings() if err != nil { t.Fatalf("GetSettings: %v", err) } if !loaded.FreeGiftsEnabled { t.Fatal("free_gifts_enabled devrait être true après reload") } if len(loaded.FreeGifts) != 1 { t.Fatalf("free_gifts: got=%d want=1: %+v", len(loaded.FreeGifts), loaded.FreeGifts) } gift := loaded.FreeGifts[0] if gift.Category != "test" || len(gift.Products) != 1 { t.Fatalf("free gift mal persistée: got=%+v", gift) } if len(gift.Products[0].Tiers) != 2 || gift.Products[0].Tiers[1].BuyQuantity != 20 || gift.Products[0].Tiers[1].FreeQuantity != 3 { t.Errorf("tiers mal persistés: got=%+v", gift.Products[0].Tiers) } // Désactivation : doit persister à false, pas de résurrection (même // classe de bug que TestUpdateSettings_DisablingPointsRewardPersistsAsNil). s.FreeGiftsEnabled = false if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings (désactivation): %v", err) } loaded, err = testDB.GetSettings() if err != nil { t.Fatalf("GetSettings (désactivation): %v", err) } if loaded.FreeGiftsEnabled { t.Error("free_gifts_enabled devrait rester false après désactivation") } } // ── Résolution de la quantité offerte (logique pure) ──────────────────────── func TestResolveFreeGift_AllProductsAtOrAboveThreshold(t *testing.T) { settings := &models.AppSettings{ FreeGiftsEnabled: true, FreeGifts: []models.CategoryFreeGiftConfig{ {Category: "fleurs", AllProducts: true, Tiers: []models.FreeGiftTier{ {BuyQuantity: 10, FreeQuantity: 1}, }}, }, } if got := db.ResolveFreeGift(settings, 42, "fleurs", 10); got != 1 { t.Errorf("quantité offerte: got=%.2f want=1", got) } if got := db.ResolveFreeGift(settings, 42, "fleurs", 15); got != 1 { t.Errorf("au-dessus du seuil, le cadeau reste dû: got=%.2f want=1", got) } if got := db.ResolveFreeGift(settings, 42, "fleurs", 9); got != 0 { t.Errorf("sous le seuil, aucun cadeau: got=%.2f want=0", got) } } func TestResolveFreeGift_DisabledReturnsZero(t *testing.T) { settings := &models.AppSettings{ FreeGiftsEnabled: false, FreeGifts: []models.CategoryFreeGiftConfig{ {Category: "fleurs", AllProducts: true, Tiers: []models.FreeGiftTier{ {BuyQuantity: 10, FreeQuantity: 1}, }}, }, } if got := db.ResolveFreeGift(settings, 42, "fleurs", 10); got != 0 { t.Errorf("offres désactivées: aucun cadeau attendu: got=%.2f", got) } } func TestResolveFreeGift_PerProductHighestTierApplies(t *testing.T) { settings := &models.AppSettings{ FreeGiftsEnabled: true, FreeGifts: []models.CategoryFreeGiftConfig{ { Category: "fleurs", AllProducts: false, Products: []models.FreeGiftProductQuantity{ {ProductID: 111, Tiers: []models.FreeGiftTier{ {BuyQuantity: 10, FreeQuantity: 1}, {BuyQuantity: 20, FreeQuantity: 3}, }}, }, }, }, } if got := db.ResolveFreeGift(settings, 111, "fleurs", 10); got != 1 { t.Errorf("seuil 10g: got=%.2f want=1", got) } // 25g dépasse les deux seuils : le plus élevé (20g→3g) doit être retenu, // pas le premier de la liste (10g→1g). if got := db.ResolveFreeGift(settings, 111, "fleurs", 25); got != 3 { t.Errorf("seuil le plus élevé atteint (20g→3g): got=%.2f want=3", got) } // Produit non listé dans cette config : aucun cadeau. if got := db.ResolveFreeGift(settings, 222, "fleurs", 25); got != 0 { t.Errorf("produit non couvert: got=%.2f want=0", got) } } // ── Intégration AddToBasket : la quantité livrée inclut le cadeau, au même prix ── func TestAddToBasket_AppliesFreeGiftQuantityAtSamePrice(t *testing.T) { cleanupStockTestData(t) resetSettingsAfterTest(t) username := newTestClient(t, "freegift_basket_applies") productID := newTestProduct(t, "FreeGiftBasketApplies", 50) // newTestProduct crée un palier quantity=1 à 10.00€ dans la catégorie "test". s := db.DefaultSettings() s.FreeGiftsEnabled = true s.FreeGifts = []models.CategoryFreeGiftConfig{ {Category: "test", AllProducts: true, Tiers: []models.FreeGiftTier{ {BuyQuantity: 10, FreeQuantity: 1}, }}, } if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings: %v", err) } basket, err := testDB.AddToBasket(username, productID, 10) if err != nil { t.Fatalf("AddToBasket: %v", err) } if basket.Quantity != 11 { t.Errorf("quantité livrée attendue = 10 + 1 offert = 11: got=%.2f", basket.Quantity) } if basket.Price != 10.0 { t.Errorf("le prix ne doit pas changer (facturé sur les 10g demandés): got=%.2f want=10.00", basket.Price) } } func TestAddToBasket_NoFreeGiftBelowThreshold(t *testing.T) { cleanupStockTestData(t) resetSettingsAfterTest(t) username := newTestClient(t, "freegift_basket_below") productID := newTestProduct(t, "FreeGiftBasketBelow", 50) s := db.DefaultSettings() s.FreeGiftsEnabled = true s.FreeGifts = []models.CategoryFreeGiftConfig{ {Category: "test", AllProducts: true, Tiers: []models.FreeGiftTier{ {BuyQuantity: 10, FreeQuantity: 1}, }}, } if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings: %v", err) } basket, err := testDB.AddToBasket(username, productID, 5) if err != nil { t.Fatalf("AddToBasket: %v", err) } if basket.Quantity != 5 { t.Errorf("sous le seuil, aucune quantité offerte: got=%.2f want=5", basket.Quantity) } } // La quantité réellement décomptée du stock doit inclure le cadeau : un stock // suffisant pour la quantité demandée mais pas pour demandée+offerte doit // faire échouer l'ajout, pas livrer un cadeau partiel. func TestAddToBasket_FreeGiftRejectedWhenStockInsufficientForBonus(t *testing.T) { cleanupStockTestData(t) resetSettingsAfterTest(t) username := newTestClient(t, "freegift_basket_stock") productID := newTestProduct(t, "FreeGiftBasketStock", 10) // stock = 10, pile la quantité demandée s := db.DefaultSettings() s.FreeGiftsEnabled = true s.FreeGifts = []models.CategoryFreeGiftConfig{ {Category: "test", AllProducts: true, Tiers: []models.FreeGiftTier{ {BuyQuantity: 10, FreeQuantity: 1}, }}, } if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings: %v", err) } if _, err := testDB.AddToBasket(username, productID, 10); err == nil { t.Fatal("stock=10 ne doit pas suffire pour livrer 10g + 1g offert") } }