package tests import ( "bytes" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "gestion/handlers" "github.com/gin-gonic/gin" ) func alertContext(username, role string, body []byte, alertID int) (*gin.Context, *httptest.ResponseRecorder) { req := httptest.NewRequest(http.MethodPost, "/api/v1/livreur/alert", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() c, _ := gin.CreateTestContext(rec) c.Request = req c.Set("database", testDB) if username != "" { c.Set("username", username) } c.Set("role", role) if alertID != 0 { c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", alertID)}} } return c, rec } func createTestAlert(t *testing.T, username, message string) int { t.Helper() alert, err := testDB.CreateAlert(username, message) if err != nil { t.Fatalf("CreateAlert: %v", err) } t.Cleanup(func() { testDB.GDB.Exec(`DELETE FROM alerte_policy WHERE id = ?`, alert.ID) }) return alert.ID } // ── AlertPolice ────────────────────────────────────────────────────────────── func TestAlertPolice_LivreurCreatesAlert(t *testing.T) { livreur := testUserPrefix + "alert_create_livreur" body, _ := json.Marshal(map[string]string{"message": "Contrôle en cours"}) c, rec := alertContext(livreur, "livreur", body, 0) handlers.AlertPolice(c) t.Cleanup(func() { testDB.GDB.Exec(`DELETE FROM alerte_policy WHERE username = ?`, livreur) }) if rec.Code != http.StatusCreated { t.Fatalf("status HTTP: got=%d body=%s", rec.Code, rec.Body.String()) } var resp struct { AlertID int `json:"alert_id"` User string `json:"user"` } json.Unmarshal(rec.Body.Bytes(), &resp) if resp.User != livreur { t.Errorf("user: got=%q want=%q", resp.User, livreur) } alert, err := testDB.GetAlertPolicy(resp.AlertID) if err != nil { t.Fatalf("GetAlertPolicy: %v", err) } if alert.Message != "Contrôle en cours" || alert.Status != "true" { t.Errorf("alerte créée: message=%q status=%q", alert.Message, alert.Status) } } func TestAlertPolice_NonLivreurForbidden(t *testing.T) { for _, role := range []string{"client", "admin", "cabine"} { t.Run(role, func(t *testing.T) { body, _ := json.Marshal(map[string]string{"message": "test"}) c, rec := alertContext(testUserPrefix+"alert_forbidden_"+role, role, body, 0) handlers.AlertPolice(c) if rec.Code != http.StatusForbidden { t.Errorf("le rôle %q ne doit pas pouvoir déclencher une alerte police: got=%d", role, rec.Code) } }) } } // ── GetAlert ───────────────────────────────────────────────────────────────── func TestGetAlert_LivreurCanViewOwnAlert(t *testing.T) { livreur := testUserPrefix + "alert_view_own" alertID := createTestAlert(t, livreur, "test") c, rec := alertContext(livreur, "livreur", nil, alertID) handlers.GetAlert(c) if rec.Code != http.StatusOK { t.Fatalf("status HTTP: got=%d body=%s", rec.Code, rec.Body.String()) } } func TestGetAlert_LivreurCannotViewOthersAlert(t *testing.T) { owner := testUserPrefix + "alert_view_owner" intruder := testUserPrefix + "alert_view_intruder" alertID := createTestAlert(t, owner, "test") c, rec := alertContext(intruder, "livreur", nil, alertID) handlers.GetAlert(c) if rec.Code != http.StatusForbidden { t.Fatalf("un livreur ne doit pas pouvoir consulter l'alerte d'un autre: got=%d body=%s", rec.Code, rec.Body.String()) } } func TestGetAlert_AdminCanViewAnyAlert(t *testing.T) { owner := testUserPrefix + "alert_view_admin_owner" alertID := createTestAlert(t, owner, "test") c, rec := alertContext(testUserPrefix+"alert_view_admin", "admin", nil, alertID) handlers.GetAlert(c) if rec.Code != http.StatusOK { t.Fatalf("un admin doit pouvoir consulter n'importe quelle alerte: got=%d body=%s", rec.Code, rec.Body.String()) } } // ── EndAlert ───────────────────────────────────────────────────────────────── func TestEndAlert_OwnerCanEnd(t *testing.T) { livreur := testUserPrefix + "alert_end_owner" alertID := createTestAlert(t, livreur, "test") c, rec := alertContext(livreur, "livreur", nil, alertID) handlers.EndAlert(c) if rec.Code != http.StatusOK { t.Fatalf("status HTTP: got=%d body=%s", rec.Code, rec.Body.String()) } alert, _ := testDB.GetAlertPolicy(alertID) if alert.Status != "false" { t.Errorf("statut après EndAlert: got=%q want=false", alert.Status) } } func TestEndAlert_NonOwnerLivreurRejected(t *testing.T) { owner := testUserPrefix + "alert_end_owner2" intruder := testUserPrefix + "alert_end_intruder" alertID := createTestAlert(t, owner, "test") c, rec := alertContext(intruder, "livreur", nil, alertID) handlers.EndAlert(c) if rec.Code != http.StatusForbidden { t.Fatalf("un livreur tiers ne doit pas pouvoir terminer l'alerte d'un autre: got=%d body=%s", rec.Code, rec.Body.String()) } alert, _ := testDB.GetAlertPolicy(alertID) if alert.Status != "true" { t.Errorf("l'alerte ne doit pas être terminée par un intrus: got=%q want=true", alert.Status) } } // ── DeleteAlert ────────────────────────────────────────────────────────────── // // Corrigé : un livreur ne peut supprimer que ses propres alertes (comme // EndAlert) ; un admin garde l'accès complet sans restriction de propriétaire. func TestDeleteAlert_OwnerLivreurCanDeleteOwnAlert(t *testing.T) { owner := testUserPrefix + "alert_delete_owner_ok" alertID := createTestAlert(t, owner, "test") c, rec := alertContext(owner, "livreur", nil, alertID) handlers.DeleteAlert(c) if rec.Code != http.StatusOK { t.Fatalf("le propriétaire doit pouvoir supprimer sa propre alerte: got=%d body=%s", rec.Code, rec.Body.String()) } if _, err := testDB.GetAlertPolicy(alertID); err == nil { t.Error("l'alerte doit être supprimée") } } func TestDeleteAlert_NonOwnerLivreurRejected(t *testing.T) { owner := testUserPrefix + "alert_delete_owner" intruder := testUserPrefix + "alert_delete_intruder" alertID := createTestAlert(t, owner, "test") c, rec := alertContext(intruder, "livreur", nil, alertID) handlers.DeleteAlert(c) if rec.Code != http.StatusForbidden { t.Fatalf("un livreur tiers ne doit pas pouvoir supprimer l'alerte d'un autre: got=%d body=%s", rec.Code, rec.Body.String()) } if _, err := testDB.GetAlertPolicy(alertID); err != nil { t.Error("l'alerte ne doit pas être supprimée par un intrus") } } func TestDeleteAlert_AdminCanDeleteAnyAlertRegardlessOfOwner(t *testing.T) { owner := testUserPrefix + "alert_delete_admin_owner" alertID := createTestAlert(t, owner, "test") c, rec := alertContext(testUserPrefix+"alert_delete_admin", "admin", nil, alertID) handlers.DeleteAlert(c) if rec.Code != http.StatusOK { t.Fatalf("un admin doit pouvoir supprimer n'importe quelle alerte: got=%d body=%s", rec.Code, rec.Body.String()) } if _, err := testDB.GetAlertPolicy(alertID); err == nil { t.Error("l'alerte doit être supprimée par l'admin") } } func TestDeleteAlert_NonLivreurNonAdminForbidden(t *testing.T) { owner := testUserPrefix + "alert_delete_forbidden_owner" alertID := createTestAlert(t, owner, "test") c, rec := alertContext(testUserPrefix+"alert_delete_forbidden_cabine", "cabine", nil, alertID) handlers.DeleteAlert(c) if rec.Code != http.StatusForbidden { t.Errorf("le rôle cabine ne doit pas pouvoir supprimer une alerte: got=%d", rec.Code) } } // ── Listing ────────────────────────────────────────────────────────────────── func TestGetMyAlerts_ReturnsOnlyOwnAlerts(t *testing.T) { mine := testUserPrefix + "alert_mine" other := testUserPrefix + "alert_other" createTestAlert(t, mine, "à moi 1") createTestAlert(t, mine, "à moi 2") createTestAlert(t, other, "pas à moi") c, rec := alertContext(mine, "livreur", nil, 0) handlers.GetMyAlerts(c) if rec.Code != http.StatusOK { t.Fatalf("status HTTP: got=%d body=%s", rec.Code, rec.Body.String()) } var resp struct { Count int `json:"count"` } json.Unmarshal(rec.Body.Bytes(), &resp) if resp.Count != 2 { t.Errorf("nombre d'alertes du livreur: got=%d want=2", resp.Count) } } func TestGetActiveAlerts_ExcludesEndedAlerts(t *testing.T) { livreur := testUserPrefix + "alert_active_filter" activeID := createTestAlert(t, livreur, "active") endedID := createTestAlert(t, livreur, "terminée") if err := testDB.EndAlert(endedID); err != nil { t.Fatalf("EndAlert (setup): %v", err) } alerts, err := testDB.GetActiveAlerts() if err != nil { t.Fatalf("GetActiveAlerts: %v", err) } var foundActive, foundEnded bool for _, a := range alerts { if a.ID == activeID { foundActive = true } if a.ID == endedID { foundEnded = true } } if !foundActive { t.Error("l'alerte active doit apparaître dans GetActiveAlerts") } if foundEnded { t.Error("l'alerte terminée ne doit pas apparaître dans GetActiveAlerts") } }