Files
projet_gestion_commande/backend/gestion/tests/stats_advanced_test.go
T
Nuxgrid 734493363f
Backend - Build & Lint / build (push) Failing after 29m5s
Frontend Client - EAS Build / build (push) Failing after 2h25m33s
chore: build
2026-07-14 18:34:58 +02:00

163 lines
5.5 KiB
Go

package tests
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"gestion/handlers"
"gestion/models"
"github.com/gin-gonic/gin"
)
func adminStatsContext() (*gin.Context, *httptest.ResponseRecorder) {
req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/stats", nil)
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
c.Request = req
c.Set("database", testDB)
return c, rec
}
// insertStatsCommand insère une commande directement avec un created_at
// choisi, pour contrôler précisément le jour de semaine/heure agrégés.
func insertStatsCommand(t *testing.T, username, status, createdAt string) {
t.Helper()
testDB.GDB.Exec(
`INSERT INTO commandes (username, status, adresse, total_prix, created_at, updated_at)
VALUES (?, ?, 'Adresse stats test', 10, ?::timestamp, ?::timestamp)`,
username, status, createdAt, createdAt,
)
}
func TestGetAdminStats_ZeroOrdersAvgPerDayIsZeroNoPanic(t *testing.T) {
cleanupStockTestData(t)
// Table commandes peut contenir des données d'autres tests, mais aucune
// n'utilise ce username isolé — on vérifie juste l'absence de panic/NaN
// et que la structure de réponse est bien formée avec les vraies
// données actuelles de la base de test (qui peut être non-vide).
c, rec := adminStatsContext()
handlers.GetAdminStats(c)
if rec.Code != http.StatusOK {
t.Fatalf("GetAdminStats doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
}
var resp struct {
Summary struct {
AvgPerDay float64 `json:"avg_per_day"`
} `json:"summary"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
t.Fatalf("réponse JSON invalide: %v", err)
}
if resp.Summary.AvgPerDay < 0 {
t.Errorf("avg_per_day ne doit jamais être négatif: got=%f", resp.Summary.AvgPerDay)
}
}
func TestGetAdminStats_PeakWeekdayMatchesBusiestDay(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "stats_peak")
// 2024-01-08 est un lundi, 2024-01-07 un dimanche (DOW Postgres: 0=dimanche).
insertStatsCommand(t, username, "pending", "2024-01-08 10:00:00")
insertStatsCommand(t, username, "pending", "2024-01-08 11:00:00")
insertStatsCommand(t, username, "pending", "2024-01-08 12:00:00")
insertStatsCommand(t, username, "pending", "2024-01-07 10:00:00")
c, rec := adminStatsContext()
handlers.GetAdminStats(c)
if rec.Code != http.StatusOK {
t.Fatalf("GetAdminStats doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
}
var resp struct {
Summary struct {
PeakWeekday string `json:"peak_weekday"`
} `json:"summary"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
t.Fatalf("réponse JSON invalide: %v", err)
}
if resp.Summary.PeakWeekday != "Lundi" {
t.Errorf("peak_weekday doit être le jour avec le plus de commandes: got=%q want=Lundi", resp.Summary.PeakWeekday)
}
}
func TestGetAdminStats_ByQuantitySortedDescendingByTotalOrders(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "stats_byqty")
prodLow := newTestProduct(t, "ByQtyLow", 100)
prodHigh := newTestProduct(t, "ByQtyHigh", 100)
// prodHigh: 3 commandes ; prodLow: 1 commande.
for range 3 {
newTestCommandWithItem(t, username, "pending", "", prodHigh, 1, 10)
}
newTestCommandWithItem(t, username, "pending", "", prodLow, 1, 10)
c, rec := adminStatsContext()
handlers.GetAdminStats(c)
if rec.Code != http.StatusOK {
t.Fatalf("GetAdminStats doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
}
var resp struct {
ByQuantity []struct {
ProductID int `json:"product_id"`
TotalOrders int `json:"total_orders"`
} `json:"by_quantity"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
t.Fatalf("réponse JSON invalide: %v", err)
}
// Vérifie l'ordre décroissant global (pas seulement nos deux produits,
// d'autres tests peuvent avoir laissé des données) et que prodHigh
// apparaît avant prodLow.
highIdx, lowIdx := -1, -1
for i, r := range resp.ByQuantity {
if r.ProductID == prodHigh {
highIdx = i
}
if r.ProductID == prodLow {
lowIdx = i
}
if i > 0 && r.TotalOrders > resp.ByQuantity[i-1].TotalOrders {
t.Errorf("by_quantity doit être trié par total_orders décroissant: rupture à l'index %d", i)
}
}
if highIdx == -1 || lowIdx == -1 {
t.Fatalf("les deux produits de test doivent apparaître dans by_quantity: highIdx=%d lowIdx=%d", highIdx, lowIdx)
}
if highIdx >= lowIdx {
t.Errorf("le produit avec le plus de commandes doit apparaître avant: highIdx=%d lowIdx=%d", highIdx, lowIdx)
}
}
func TestOrdersAndRevenueByHour_CountsNonCancelledRevenueOnlyApproved(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "stats_hour")
insertStatsCommand(t, username, "approved", "2024-01-08 14:00:00")
insertStatsCommand(t, username, "pending", "2024-01-08 14:30:00")
insertStatsCommand(t, username, "cancelled", "2024-01-08 14:45:00")
testDB.GDB.Exec(`UPDATE commandes SET total_prix = 25 WHERE username = ? AND status = 'approved'`, username)
var hourRows []models.HourRow
if err := testDB.OrdersAndRevenueByHour(&hourRows, testDB.ReadResetAt("stats_reset_heures_at")); err != nil {
t.Fatalf("OrdersAndRevenueByHour: %v", err)
}
var found bool
for _, r := range hourRows {
if r.Hour == 14 {
found = true
if r.Count != 2 {
t.Errorf("count à 14h doit exclure la commande annulée: got=%d want=2", r.Count)
}
if r.Revenue != 25 {
t.Errorf("revenue à 14h ne doit compter que les commandes approuvées: got=%.2f want=25", r.Revenue)
}
}
}
if !found {
t.Fatal("aucune ligne pour l'heure 14 trouvée")
}
}