package tests import ( "testing" "time" ) // ResetAdminStat déplace le point de coupure temporel utilisé par toutes les // requêtes de stats (TotalOrders, TotalRevenue, ActiveDaysLast30, ...) : les // commandes créées AVANT le reset doivent disparaître des totaux, celles // créées APRÈS doivent rester visibles. C'est le mécanisme central derrière // le bouton "reset stats" de l'admin — jamais testé jusqu'ici. const statsResetTestKey = "stats_reset_commandes_at" func cleanupStatsResetKey(t *testing.T, key string) { t.Helper() t.Cleanup(func() { testDB.GDB.Exec(`DELETE FROM app_settings WHERE key = ?`, key) }) } func TestResetAdminStat_TotalOrdersExcludesOrdersBeforeReset(t *testing.T) { cleanupStockTestData(t) cleanupStatsResetKey(t, statsResetTestKey) username := newTestClient(t, "stat_reset_orders") productID := newTestProduct(t, "StatResetOrders", 10) newTestCommandWithItem(t, username, "pending", "", productID, 1, 10) time.Sleep(1100 * time.Millisecond) // RFC3339 stocké sans fraction de seconde : marge nécessaire if err := testDB.ResetAdminStat(statsResetTestKey); err != nil { t.Fatalf("ResetAdminStat: %v", err) } time.Sleep(1100 * time.Millisecond) newTestCommandWithItem(t, username, "pending", "", productID, 1, 10) totalWithoutFilter, err := testDB.TotalOrders(time.Time{}) if err != nil { t.Fatalf("TotalOrders(zero): %v", err) } if totalWithoutFilter != 2 { t.Fatalf("précondition: 2 commandes doivent exister sans filtre: got=%d", totalWithoutFilter) } resetAt := testDB.ReadResetAt(statsResetTestKey) if resetAt.IsZero() { t.Fatal("ReadResetAt ne doit pas être zero après ResetAdminStat") } totalAfterReset, err := testDB.TotalOrders(resetAt) if err != nil { t.Fatalf("TotalOrders(resetAt): %v", err) } if totalAfterReset != 1 { t.Errorf("après reset, seule la commande créée après doit être comptée: got=%d want=1", totalAfterReset) } } // Chaque section de stats a sa propre clé de reset (commandes, revenus, // produits, heures, jours, doses) — réinitialiser l'une ne doit jamais // affecter les autres. func TestResetAdminStat_DifferentSectionsAreIndependent(t *testing.T) { cleanupStatsResetKey(t, "stats_reset_commandes_at_test_indep") cleanupStatsResetKey(t, "stats_reset_revenus_at_test_indep") if err := testDB.ResetAdminStat("stats_reset_commandes_at_test_indep"); err != nil { t.Fatalf("ResetAdminStat (commandes): %v", err) } if got := testDB.ReadResetAt("stats_reset_revenus_at_test_indep"); !got.IsZero() { t.Errorf("réinitialiser la section commandes ne doit pas créer de reset pour revenus: got=%v", got) } if got := testDB.ReadResetAt("stats_reset_commandes_at_test_indep"); got.IsZero() { t.Error("la section commandes doit bien avoir une date de reset") } } // ActiveDaysLast30 (stat secondaire) doit respecter le même filtre de reset // que TotalOrders : un jour dont l'unique commande a été passée avant le // reset ne doit plus compter comme jour actif. func TestActiveDaysLast30_RespectsResetFilter(t *testing.T) { cleanupStockTestData(t) cleanupStatsResetKey(t, statsResetTestKey) username := newTestClient(t, "stat_reset_active_days") productID := newTestProduct(t, "StatResetActiveDays", 10) newTestCommandWithItem(t, username, "pending", "", productID, 1, 10) time.Sleep(1100 * time.Millisecond) if err := testDB.ResetAdminStat(statsResetTestKey); err != nil { t.Fatalf("ResetAdminStat: %v", err) } resetAt := testDB.ReadResetAt(statsResetTestKey) activeDays, err := testDB.ActiveDaysLast30(resetAt) if err != nil { t.Fatalf("ActiveDaysLast30: %v", err) } if activeDays != 0 { t.Errorf("aucun jour actif ne doit être compté (seule commande antérieure au reset): got=%d want=0", activeDays) } }