package tests import ( "bytes" "fmt" "net/http" "net/http/httptest" "testing" "gestion/db" "gestion/handlers" "gestion/services" "github.com/gin-gonic/gin" ) // db.Redis n'est initialisé qu'après TestMain (db.InitRedis) — un var // package-level serait construit trop tôt, d'où cette init paresseuse. var testGeoService *services.GeoService // ensureTestGeoService initialise paresseusement le GeoService partagé // (db.Redis n'existe qu'après TestMain) — réutilisé par les autres fichiers // de tests qui ont besoin de "geoService" dans le contexte gin. func ensureTestGeoService() *services.GeoService { if testGeoService == nil { testGeoService = services.NewGeoService(db.Redis, db.RedisCtx) } return testGeoService } func etaContext(username, role string, commandID int, setUsername bool) (*gin.Context, *httptest.ResponseRecorder) { req := httptest.NewRequest(http.MethodGet, "/api/v1/eta", bytes.NewReader(nil)) rec := httptest.NewRecorder() c, _ := gin.CreateTestContext(rec) c.Request = req c.Set("database", testDB) c.Set("geoService", ensureTestGeoService()) if setUsername { c.Set("username", username) } c.Set("role", role) c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", commandID)}} return c, rec } func TestGetOrderETA_UnauthenticatedReturns401(t *testing.T) { cleanupStockTestData(t) c, rec := etaContext("", "client", 1, false) handlers.GetOrderETA(c) if rec.Code != http.StatusUnauthorized { t.Errorf("non authentifié doit retourner 401: got=%d", rec.Code) } } func TestGetOrderETA_InvalidCommandIDReturns400(t *testing.T) { cleanupStockTestData(t) c, rec := etaContext(testUserPrefix+"eta_badid", "client", 0, true) c.Params = gin.Params{{Key: "id", Value: "not-a-number"}} handlers.GetOrderETA(c) if rec.Code != http.StatusBadRequest { t.Errorf("ID invalide doit retourner 400: got=%d", rec.Code) } } func TestGetOrderETA_UnknownCommandReturns404(t *testing.T) { cleanupStockTestData(t) c, rec := etaContext(testUserPrefix+"eta_404", "client", 99999999, true) handlers.GetOrderETA(c) if rec.Code != http.StatusNotFound { t.Errorf("commande inconnue doit retourner 404: got=%d", rec.Code) } } func TestGetOrderETA_ClientAccessingAnotherClientCommandReturns403(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_owner") intruder := newTestClient(t, "eta_intruder") productID := newTestProduct(t, "EtaOwner", 10) cmdID := newTestCommandWithItem(t, owner, "en_route", "eta_livreur", productID, 1, 10) c, rec := etaContext(intruder, "client", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusForbidden { t.Errorf("un autre client ne doit pas accéder à l'ETA: got=%d body=%s", rec.Code, rec.Body.String()) } } func TestGetOrderETA_LivreurNotAssignedReturns403(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_lv_owner") productID := newTestProduct(t, "EtaLvOwner", 10) cmdID := newTestCommandWithItem(t, owner, "en_route", "eta_real_livreur", productID, 1, 10) c, rec := etaContext("eta_other_livreur", "livreur", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusForbidden { t.Errorf("un livreur non assigné ne doit pas accéder à l'ETA: got=%d body=%s", rec.Code, rec.Body.String()) } } func TestGetOrderETA_AdminBypassesOwnershipChecks(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_admin_owner") productID := newTestProduct(t, "EtaAdminOwner", 10) cmdID := newTestCommandWithItem(t, owner, "pending", "", productID, 1, 10) c, rec := etaContext(testUserPrefix+"eta_admin", "admin", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusOK { t.Errorf("admin doit pouvoir accéder à l'ETA de n'importe quelle commande: got=%d body=%s", rec.Code, rec.Body.String()) } } func TestGetOrderETA_DeliveredStatusReturnsEtaUnavailable(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_delivered") productID := newTestProduct(t, "EtaDelivered", 10) cmdID := newTestCommandWithItem(t, owner, "livre", "eta_livreur_d", productID, 1, 10) c, rec := etaContext(owner, "client", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusOK { t.Fatalf("statut livre doit retourner 200: got=%d body=%s", rec.Code, rec.Body.String()) } if !bytes.Contains(rec.Body.Bytes(), []byte(`"eta_available":false`)) { t.Errorf("eta_available doit être false pour une commande livrée: body=%s", rec.Body.String()) } } func TestGetOrderETA_PendingStatusReturnsEtaUnavailable(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_pending") productID := newTestProduct(t, "EtaPending", 10) cmdID := newTestCommandWithItem(t, owner, "pending", "", productID, 1, 10) c, rec := etaContext(owner, "client", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusOK { t.Fatalf("statut pending doit retourner 200: got=%d body=%s", rec.Code, rec.Body.String()) } if !bytes.Contains(rec.Body.Bytes(), []byte(`"eta_available":false`)) { t.Errorf("eta_available doit être false pour une commande pending: body=%s", rec.Body.String()) } } func TestGetOrderETA_ArrivedStatusReturnsEtaUnavailable(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_arrived") productID := newTestProduct(t, "EtaArrived", 10) cmdID := newTestCommandWithItem(t, owner, "arrived", "eta_livreur_a", productID, 1, 10) c, rec := etaContext(owner, "client", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusOK { t.Fatalf("statut arrived doit retourner 200: got=%d body=%s", rec.Code, rec.Body.String()) } if !bytes.Contains(rec.Body.Bytes(), []byte(`"eta_available":false`)) { t.Errorf("eta_available doit être false quand le livreur est arrivé: body=%s", rec.Body.String()) } } func TestGetOrderETA_NoLivreurAssignedReturnsEtaUnavailable(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_nolivreur") productID := newTestProduct(t, "EtaNoLivreur", 10) cmdID := newTestCommandWithItem(t, owner, "en_route", "", productID, 1, 10) c, rec := etaContext(owner, "client", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusOK { t.Fatalf("sans livreur assigné doit retourner 200: got=%d body=%s", rec.Code, rec.Body.String()) } if !bytes.Contains(rec.Body.Bytes(), []byte(`"eta_available":false`)) { t.Errorf("eta_available doit être false sans livreur assigné: body=%s", rec.Body.String()) } } // Sans coordonnées de destination et sans cache Redis préalable, le // handler doit tomber sur returnStaleOrUnavailable plutôt que planter. func TestGetOrderETA_MissingDestinationCoordsFallsBackGracefully(t *testing.T) { cleanupStockTestData(t) owner := newTestClient(t, "eta_nodest") productID := newTestProduct(t, "EtaNoDest", 10) cmdID := newTestCommandWithItem(t, owner, "en_route", "eta_livreur_nodest", productID, 1, 10) c, rec := etaContext(owner, "client", cmdID, true) handlers.GetOrderETA(c) if rec.Code != http.StatusOK { t.Fatalf("sans coordonnées destination doit quand même retourner 200: got=%d body=%s", rec.Code, rec.Body.String()) } if !bytes.Contains(rec.Body.Bytes(), []byte(`"eta_available":false`)) { t.Errorf("eta_available doit être false sans coordonnées destination: body=%s", rec.Body.String()) } }