257 lines
9.9 KiB
Go
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)
|
|
}
|
|
}
|