package tests import ( "gestion/db" "gestion/models" "testing" ) // resetSettingsAfterTest restaure les settings par défaut à la fin du test — // AppSettings est un état global partagé (une seule ligne par clé dans // app_settings), donc un test qui le modifie ne doit pas laisser de résidu // pour les tests suivants (ex: DeliveryMode utilisé par d'autres suites). func resetSettingsAfterTest(t *testing.T) { t.Helper() t.Cleanup(func() { if err := testDB.UpdateSettings(db.DefaultSettings()); err != nil { t.Logf("⚠️ resetSettingsAfterTest: restauration des settings par défaut échouée: %v", err) } }) } // ── Bascules booléennes (activer/désactiver une option) ───────────────────── // // Régression visée : chaque option doit persister à sa valeur exacte après un // cycle save→reload, dans les deux sens (activation ET désactivation) — voir // TestUpdateSettings_DisablingPointsRewardPersistsAsNil pour un cas où la // désactivation ne persistait pas correctement. func TestUpdateSettings_DisablingBooleanTogglesPersists(t *testing.T) { resetSettingsAfterTest(t) set := func(v bool) models.AppSettings { s := db.DefaultSettings() s.PenaltiesEnabled = v s.ShowAmendeScore = v s.PointsEnabled = v s.ReferralEnabled = v s.CryptoPaymentEnabled = v s.CryptoOnly = v s.TelegramNotificationsEnabled = v s.Telegram2FAEnabled = v return s } assertAll := func(t *testing.T, want bool) { t.Helper() loaded, err := testDB.GetSettings() if err != nil { t.Fatalf("GetSettings: %v", err) } checks := map[string]bool{ "penalties_enabled": loaded.PenaltiesEnabled, "show_amende_score": loaded.ShowAmendeScore, "points_enabled": loaded.PointsEnabled, "referral_enabled": loaded.ReferralEnabled, "crypto_payment_enabled": loaded.CryptoPaymentEnabled, "crypto_only": loaded.CryptoOnly, "telegram_notifications_enabled": loaded.TelegramNotificationsEnabled, "telegram_2fa_enabled": loaded.Telegram2FAEnabled, } for key, got := range checks { if got != want { t.Errorf("%s: got=%v want=%v", key, got, want) } } } if err := testDB.UpdateSettings(set(true)); err != nil { t.Fatalf("UpdateSettings (activation): %v", err) } assertAll(t, true) if err := testDB.UpdateSettings(set(false)); err != nil { t.Fatalf("UpdateSettings (désactivation): %v", err) } assertAll(t, false) } // ── Options non-booléennes (hors NowPayments) ─────────────────────────────── // Le barème des amendes (penalty_tiers) est éditable dans l'admin // ("Barème des amendes") mais aucune clé "penalty_tiers" n'existe dans les // pairs persistées par UpdateSettings ni dans le switch de GetSettings — la // configuration saisie par l'admin est donc silencieusement perdue au // prochain rechargement, et retombe toujours sur le barème par défaut. func TestUpdateSettings_PenaltyTiersRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.PenaltyTiers = []models.PenaltyTier{ {MinCancel: 0, Amount: 10}, {MinCancel: 5, Amount: 999}, } 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 len(loaded.PenaltyTiers) != 2 || loaded.PenaltyTiers[1].Amount != 999 { t.Errorf("le barème des amendes personnalisé n'a pas été persisté: got=%+v", loaded.PenaltyTiers) } } func TestUpdateSettings_ReferralAmountRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.ReferralAmount = 12.5 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.ReferralAmount != 12.5 { t.Errorf("referral_amount: got=%.2f want=12.50", loaded.ReferralAmount) } s.ReferralAmount = 0 if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings (remise à zéro): %v", err) } loaded, err = testDB.GetSettings() if err != nil { t.Fatalf("GetSettings (remise à zéro): %v", err) } if loaded.ReferralAmount != 0 { t.Errorf("referral_amount remis à 0: got=%.2f want=0.00", loaded.ReferralAmount) } } func TestUpdateSettings_PointsPoolsRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.PointsPools = []models.PointsPool{ { Key: "pool_custom", Name: "Pool Custom", Categories: []string{"catA", "catB"}, Tiers: []models.PointsTier{{Min: 10, Max: 20, Points: 7}}, }, } 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 len(loaded.PointsPools) != 1 || loaded.PointsPools[0].Key != "pool_custom" || len(loaded.PointsPools[0].Categories) != 2 || loaded.PointsPools[0].Tiers[0].Points != 7 { t.Errorf("points_pools personnalisé mal persisté: got=%+v", loaded.PointsPools) } } func TestUpdateSettings_DeliveryScheduleRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.DeliverySchedule.Monday = models.DaySchedule{Enabled: false, OpenTime: "10:00", CloseTime: "18:00"} 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.DeliverySchedule.Monday.Enabled != false || loaded.DeliverySchedule.Monday.OpenTime != "10:00" || loaded.DeliverySchedule.Monday.CloseTime != "18:00" { t.Errorf("delivery_schedule.monday mal persisté: got=%+v", loaded.DeliverySchedule.Monday) } } func TestUpdateSettings_PostalZonesRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.PostalZones = []models.PostalZone{ {Name: "Zone Test", MinAmount: 42, Codes: []string{"11111", "22222"}}, } 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 len(loaded.PostalZones) != 1 || loaded.PostalZones[0].MinAmount != 42 || len(loaded.PostalZones[0].Codes) != 2 { t.Errorf("postal_zones mal persisté: got=%+v", loaded.PostalZones) } } func TestUpdateSettings_DeliveryModeRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.DeliveryMode = models.DeliveryModeConfig{ Mode: "category_based", CategoryRoutes: []models.CategoryRoute{ {DeliverymanUsername: "livreur_test", Categories: []string{"catA"}}, }, } 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.DeliveryMode.Mode != "category_based" || len(loaded.DeliveryMode.CategoryRoutes) != 1 || loaded.DeliveryMode.CategoryRoutes[0].DeliverymanUsername != "livreur_test" { t.Errorf("delivery_mode mal persisté: got=%+v", loaded.DeliveryMode) } // Repasser en mode "single" avec une liste vide doit aussi persister // correctement (pas de résidu de l'ancienne liste category_routes). s.DeliveryMode = models.DeliveryModeConfig{Mode: "single", CategoryRoutes: []models.CategoryRoute{}} if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings (retour single): %v", err) } loaded, err = testDB.GetSettings() if err != nil { t.Fatalf("GetSettings (retour single): %v", err) } if loaded.DeliveryMode.Mode != "single" || len(loaded.DeliveryMode.CategoryRoutes) != 0 { t.Errorf("delivery_mode retour à single mal persisté: got=%+v", loaded.DeliveryMode) } } func TestUpdateSettings_ShopAndTelegramTextFieldsRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.ShopName = "Ma Boutique Test" s.TelegramBotToken = "123456:ABC-test-token" s.TelegramBotUsername = "mon_bot_test" 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.ShopName != "Ma Boutique Test" { t.Errorf("shop_name: got=%q want=%q", loaded.ShopName, "Ma Boutique Test") } if loaded.TelegramBotToken != "123456:ABC-test-token" { t.Errorf("telegram_bot_token: got=%q", loaded.TelegramBotToken) } if loaded.TelegramBotUsername != "mon_bot_test" { t.Errorf("telegram_bot_username: got=%q", loaded.TelegramBotUsername) } // Effacer le token/username (chaîne vide) doit aussi persister tel quel — // contrairement à contact_telegram qui a un repli explicite non-vide. s.TelegramBotToken = "" s.TelegramBotUsername = "" if err := testDB.UpdateSettings(s); err != nil { t.Fatalf("UpdateSettings (effacement): %v", err) } loaded, err = testDB.GetSettings() if err != nil { t.Fatalf("GetSettings (effacement): %v", err) } if loaded.TelegramBotToken != "" || loaded.TelegramBotUsername != "" { t.Errorf("token/username effacés devraient rester vides: got token=%q username=%q", loaded.TelegramBotToken, loaded.TelegramBotUsername) } } func TestUpdateSettings_ColorAndGradientFieldsRoundTrip(t *testing.T) { resetSettingsAfterTest(t) s := db.DefaultSettings() s.AdminColorPrimary = "#111111" s.ClientColorDanger = "#222222" s.ClientTitleGradientFrom = "#333333" s.ClientTitleGradientTo = "#444444" 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.AdminColorPrimary != "#111111" { t.Errorf("admin_color_primary: got=%q", loaded.AdminColorPrimary) } if loaded.ClientColorDanger != "#222222" { t.Errorf("client_color_danger: got=%q", loaded.ClientColorDanger) } if loaded.ClientTitleGradientFrom != "#333333" || loaded.ClientTitleGradientTo != "#444444" { t.Errorf("client_title_gradient: got from=%q to=%q", loaded.ClientTitleGradientFrom, loaded.ClientTitleGradientTo) } }