Files
Xor290 22a8d5026c
Frontend Admin - EAS Build / build (push) Canceled after 0s
Frontend Client - EAS Build / build (push) Canceled after 0s
Backend - Build & Lint / build (push) Failing after 25m18s
Frontend Web - Build & Lint / build (push) Failing after 9m58s
chore: build
2026-08-06 12:06:05 +02:00

257 lines
9.9 KiB
Go

package tests
import (
"bytes"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"gestion/handlers"
"github.com/gin-gonic/gin"
)
func cmdContext(method, username, role string, body []byte, commandID int) (*gin.Context, *httptest.ResponseRecorder) {
var reader *bytes.Reader
if body != nil {
reader = bytes.NewReader(body)
} else {
reader = bytes.NewReader([]byte{})
}
req := httptest.NewRequest(method, "/api/v1/commands", reader)
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 commandID != 0 {
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", commandID)}}
}
return c, rec
}
// ── CancelCommandByClient (HTTP layer) ───────────────────────────────────
func TestCancelCommandByClient_RejectsNonClientRole(t *testing.T) {
cleanupStockTestData(t)
c, rec := cmdContext(http.MethodPost, testUserPrefix+"cancel_role", "admin", nil, 1)
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusForbidden {
t.Errorf("role admin doit être refusé: got=%d want=403", rec.Code)
}
}
func TestCancelCommandByClient_InvalidCommandIDReturns400(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "cancel_badid")
c, rec := cmdContext(http.MethodPost, username, "client", nil, 0)
c.Params = gin.Params{{Key: "id", Value: "not-a-number"}}
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusBadRequest {
t.Errorf("ID invalide doit retourner 400: got=%d", rec.Code)
}
}
func TestCancelCommandByClient_UnknownCommandReturns404(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "cancel_404")
c, rec := cmdContext(http.MethodPost, username, "client", nil, 99999999)
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusNotFound {
t.Errorf("commande inconnue doit retourner 404: got=%d body=%s", rec.Code, rec.Body.String())
}
}
func TestCancelCommandByClient_WrongOwnerReturns403(t *testing.T) {
cleanupStockTestData(t)
owner := newTestClient(t, "cancel_owner")
intruder := newTestClient(t, "cancel_intruder")
productID := newTestProduct(t, "CancelWrongOwner", 10)
cmdID := newTestCommandWithItem(t, owner, "pending", "", productID, 1, 10)
c, rec := cmdContext(http.MethodPost, intruder, "client", nil, cmdID)
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusForbidden {
t.Errorf("un autre client ne doit pas pouvoir annuler: got=%d body=%s", rec.Code, rec.Body.String())
}
}
func TestCancelCommandByClient_SuccessNoPenaltyWhenNoLivreur(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "cancel_ok_nopenalty")
productID := newTestProduct(t, "CancelOkNoPenalty", 10)
cmdID := newTestCommandWithItem(t, username, "pending", "", productID, 1, 10)
c, rec := cmdContext(http.MethodPost, username, "client", nil, cmdID)
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusOK {
t.Fatalf("annulation sans livreur doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
}
if got := commandStatus(t, cmdID); got != "cancelled" {
t.Errorf("statut après annulation: got=%s want=cancelled", got)
}
}
func TestCancelCommandByClient_TerminalStatusReturns400(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "cancel_terminal")
productID := newTestProduct(t, "CancelTerminal", 10)
cmdID := newTestCommandWithItem(t, username, "livre", "", productID, 1, 10)
c, rec := cmdContext(http.MethodPost, username, "client", nil, cmdID)
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusBadRequest {
t.Errorf("annulation d'une commande livrée doit être rejetée: got=%d body=%s", rec.Code, rec.Body.String())
}
}
func TestCancelCommandByClient_ConfirmationRequiredReturns409WithPenaltyWarning(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "cancel_confirm")
livreur := "cancel_confirm_livreur"
productID := newTestProduct(t, "CancelConfirm", 10)
cmdID := newTestCommandWithItem(t, username, "en_route", livreur, productID, 1, 10)
if err := testDB.SetCommandETA(cmdID, 14); err != nil {
t.Fatalf("SetCommandETA: %v", err)
}
c, rec := cmdContext(http.MethodPost, username, "client", nil, cmdID)
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusConflict {
t.Fatalf("annulation tardive sans force doit demander confirmation: got=%d body=%s", rec.Code, rec.Body.String())
}
if !bytes.Contains(rec.Body.Bytes(), []byte(`"will_apply":true`)) {
t.Errorf("la réponse doit avertir d'une pénalité à venir: body=%s", rec.Body.String())
}
// Statut inchangé tant que non confirmé.
if got := commandStatus(t, cmdID); got != "en_route" {
t.Errorf("statut ne doit pas changer avant confirmation: got=%s want=en_route", got)
}
}
// ── GetMyCancellationHistory ──────────────────────────────────────────────
func TestGetMyCancellationHistory_RejectsNonClientRole(t *testing.T) {
cleanupStockTestData(t)
c, rec := cmdContext(http.MethodGet, testUserPrefix+"hist_role", "livreur", nil, 0)
handlers.GetMyCancellationHistory(c)
if rec.Code != http.StatusForbidden {
t.Errorf("role livreur doit être refusé: got=%d want=403", rec.Code)
}
}
func TestGetMyCancellationHistory_ReturnsHistoryAndTotalPenalties(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "hist_ok")
c, rec := cmdContext(http.MethodGet, username, "client", nil, 0)
handlers.GetMyCancellationHistory(c)
if rec.Code != http.StatusOK {
t.Fatalf("historique doit réussir pour un client: got=%d body=%s", rec.Code, rec.Body.String())
}
if !bytes.Contains(rec.Body.Bytes(), []byte(`"total_penalties"`)) {
t.Errorf("la réponse doit inclure total_penalties: body=%s", rec.Body.String())
}
}
// ── GetAllCancelledOrders ─────────────────────────────────────────────────
func TestGetAllCancelledOrders_RejectsNonAdminNonCabine(t *testing.T) {
cleanupStockTestData(t)
c, rec := cmdContext(http.MethodGet, testUserPrefix+"allcancel_role", "livreur", nil, 0)
handlers.GetAllCancelledOrders(c)
if rec.Code != http.StatusForbidden {
t.Errorf("role livreur doit être refusé: got=%d want=403", rec.Code)
}
}
func TestGetAllCancelledOrders_InvalidLimitFallsBackToDefault(t *testing.T) {
cleanupStockTestData(t)
c, rec := cmdContext(http.MethodGet, testUserPrefix+"allcancel_limit", "admin", nil, 0)
req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/cancelled?limit=not-a-number", nil)
c.Request = req
c.Set("database", testDB)
c.Set("username", testUserPrefix+"allcancel_limit")
c.Set("role", "admin")
handlers.GetAllCancelledOrders(c)
if rec.Code != http.StatusOK {
t.Fatalf("limit invalide doit quand même réussir avec un défaut: got=%d body=%s", rec.Code, rec.Body.String())
}
}
func TestGetAllCancelledOrders_LimitCapsAt500(t *testing.T) {
cleanupStockTestData(t)
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
c.Request = httptest.NewRequest(http.MethodGet, "/api/v1/admin/cancelled?limit=99999", nil)
c.Set("database", testDB)
c.Set("username", testUserPrefix+"allcancel_cap")
c.Set("role", "admin")
handlers.GetAllCancelledOrders(c)
if rec.Code != http.StatusOK {
t.Fatalf("limit énorme doit quand même réussir (plafonné): got=%d body=%s", rec.Code, rec.Body.String())
}
}
// ── DeleteCommandByCabine ─────────────────────────────────────────────────
func TestDeleteCommandByCabine_RejectsNonCabineNonAdmin(t *testing.T) {
cleanupStockTestData(t)
c, rec := cmdContext(http.MethodDelete, testUserPrefix+"delcab_role", "client", nil, 1)
handlers.DeleteCommandByCabine(c)
if rec.Code != http.StatusForbidden {
t.Errorf("role client doit être refusé: got=%d want=403", rec.Code)
}
}
func TestDeleteCommandByCabine_UnknownCommandReturns404(t *testing.T) {
cleanupStockTestData(t)
c, rec := cmdContext(http.MethodDelete, testUserPrefix+"delcab_404", "cabine", nil, 99999999)
handlers.DeleteCommandByCabine(c)
if rec.Code != http.StatusNotFound {
t.Errorf("commande inconnue doit retourner 404: got=%d body=%s", rec.Code, rec.Body.String())
}
}
func TestDeleteCommandByCabine_SuccessDeletesCommand(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "delcab_ok")
productID := newTestProduct(t, "DelCabOk", 10)
cmdID := newTestCommandWithItem(t, username, "pending", "", productID, 1, 10)
c, rec := cmdContext(http.MethodDelete, testUserPrefix+"delcab_ok_actor", "cabine", nil, cmdID)
handlers.DeleteCommandByCabine(c)
if rec.Code != http.StatusOK {
t.Fatalf("suppression par cabine doit réussir: got=%d body=%s", rec.Code, rec.Body.String())
}
var count int64
testDB.GDB.Raw(`SELECT COUNT(*) FROM commandes WHERE id = ?`, cmdID).Scan(&count)
if count != 0 {
t.Errorf("la commande doit être supprimée: got=%d lignes restantes", count)
}
}
// ── validateReason (testé indirectement via CancelCommandByClient) ───────
func TestCancelCommandByClient_BlankReasonDefaultsToStandardMessage(t *testing.T) {
cleanupStockTestData(t)
username := newTestClient(t, "reason_blank")
productID := newTestProduct(t, "ReasonBlank", 10)
cmdID := newTestCommandWithItem(t, username, "pending", "", productID, 1, 10)
body := []byte(`{"reason":""}`)
c, rec := cmdContext(http.MethodPost, username, "client", body, cmdID)
handlers.CancelCommandByClient(c)
if rec.Code != http.StatusOK {
t.Fatalf("annulation avec reason vide doit réussir (validateReason doit fournir un défaut, pas rejeter): got=%d body=%s", rec.Code, rec.Body.String())
}
if got := commandStatus(t, cmdID); got != "cancelled" {
t.Errorf("statut après annulation avec raison vide: got=%s want=cancelled", got)
}
}