261 lines
7 KiB
Go
261 lines
7 KiB
Go
package common
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// TestGetMaxJSONBodySize vérifie la limite par défaut et l'override via env (TASK-SEC-005)
|
|
func TestGetMaxJSONBodySize(t *testing.T) {
|
|
defer os.Unsetenv("MAX_JSON_BODY_SIZE")
|
|
|
|
// Défaut 1MB
|
|
os.Unsetenv("MAX_JSON_BODY_SIZE")
|
|
assert.Equal(t, int64(1024*1024), GetMaxJSONBodySize())
|
|
|
|
// Override via env
|
|
os.Setenv("MAX_JSON_BODY_SIZE", "2097152") // 2MB
|
|
assert.Equal(t, int64(2097152), GetMaxJSONBodySize())
|
|
}
|
|
|
|
// TestRequest est un DTO de test avec validation
|
|
type TestRequest struct {
|
|
Title string `json:"title" binding:"required,min=1,max=255" validate:"required,min=1,max=255"`
|
|
Year int `json:"year" binding:"omitempty,min=1900,max=2100" validate:"omitempty,min=1900,max=2100"`
|
|
TrackID uuid.UUID `json:"track_id" binding:"required" validate:"required,uuid"`
|
|
IsPublic *bool `json:"is_public"`
|
|
}
|
|
|
|
// TestBindAndValidateJSON_ValidRequest vérifie qu'une requête valide passe la validation
|
|
func TestBindAndValidateJSON_ValidRequest(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "title": req.Title})
|
|
})
|
|
|
|
trackID := uuid.New()
|
|
reqBody := map[string]interface{}{
|
|
"title": "Test Track",
|
|
"year": 2020,
|
|
"track_id": trackID.String(),
|
|
"is_public": true,
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var response map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.Equal(t, true, response["success"])
|
|
}
|
|
|
|
// TestBindAndValidateJSON_MissingRequiredField vérifie qu'un champ requis manquant est rejeté
|
|
func TestBindAndValidateJSON_MissingRequiredField(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
})
|
|
|
|
reqBody := map[string]interface{}{
|
|
"year": 2020,
|
|
// title manquant
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// TestBindAndValidateJSON_InvalidUUID vérifie qu'un UUID invalide est rejeté
|
|
func TestBindAndValidateJSON_InvalidUUID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
})
|
|
|
|
reqBody := map[string]interface{}{
|
|
"title": "Test Track",
|
|
"track_id": "invalid-uuid",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// TestBindAndValidateJSON_StringTooLong vérifie qu'une string trop longue est rejetée
|
|
func TestBindAndValidateJSON_StringTooLong(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
})
|
|
|
|
trackID := uuid.New()
|
|
reqBody := map[string]interface{}{
|
|
"title": string(make([]byte, 300)), // 300 caractères > max 255
|
|
"track_id": trackID.String(),
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// TestBindAndValidateJSON_NumberOutOfRange vérifie qu'un nombre hors limites est rejeté
|
|
func TestBindAndValidateJSON_NumberOutOfRange(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
})
|
|
|
|
trackID := uuid.New()
|
|
reqBody := map[string]interface{}{
|
|
"title": "Test Track",
|
|
"year": 1800, // < min 1900
|
|
"track_id": trackID.String(),
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// TestBindAndValidateJSON_EmptyString vérifie qu'une string vide est rejetée pour un champ required
|
|
func TestBindAndValidateJSON_EmptyString(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
})
|
|
|
|
trackID := uuid.New()
|
|
reqBody := map[string]interface{}{
|
|
"title": "", // String vide
|
|
"track_id": trackID.String(),
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// TestBindAndValidateJSON_InvalidJSON vérifie qu'un JSON malformé est rejeté
|
|
func TestBindAndValidateJSON_InvalidJSON(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
})
|
|
|
|
invalidJSON := []byte(`{"title": "Test", "track_id": invalid}`)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(invalidJSON))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// TestBindAndValidateJSON_BodyTooLarge vérifie qu'un body trop gros est rejeté (TASK-SEC-005: 1MB défaut)
|
|
func TestBindAndValidateJSON_BodyTooLarge(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
router.POST("/test", func(c *gin.Context) {
|
|
var req TestRequest
|
|
if !BindAndValidateJSON(c, &req) {
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
})
|
|
|
|
// Créer un body > limite (1MB par défaut)
|
|
largeBody := make([]byte, GetMaxJSONBodySize()+1)
|
|
req := httptest.NewRequest("POST", "/test", bytes.NewBuffer(largeBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.ContentLength = int64(len(largeBody))
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|