# ✅ P1-002 — VALIDATION INPUTS SYSTÉMATIQUE (DTO + HELPER)

**Date**: 2025-12-12  
**Objectif**: Rendre la validation homogène sur les endpoints critiques avec DTOs + helper centralisé

---

## 📋 RÉSUMÉ

✅ **Helper centralisé créé** : `common.BindAndValidateJSON()` utilisable par tous les handlers  
✅ **DTOs complétés** : Tags `validate` ajoutés aux DTOs existants (en plus de `binding`)  
✅ **Handlers mis à jour** : `TrackHandler` utilise maintenant le helper centralisé  
✅ **Tests ajoutés** : Tests unitaires pour cas invalides, limites, UUID invalides  
✅ **Format d'erreur standardisé** : HTTP 400 avec détails structurés

---

## 📁 FICHIERS MODIFIÉS

### 1. `internal/common/validation.go`
- ✅ Amélioration de `BindAndValidateJSON()` pour utiliser le validator centralisé
- ✅ Gestion d'erreurs améliorée (JSON malformé, body trop gros, types incorrects)
- ✅ Validation avec `go-playground/validator` après binding Gin
- ✅ Format d'erreur standardisé via `response.ValidationError()`

### 2. `internal/core/track/handler.go`
- ✅ Ajout import `common` pour utiliser `BindAndValidateJSON()`
- ✅ Remplacement de tous les appels `ShouldBindJSON()` par `BindAndValidateJSON()`
- ✅ Ajout tags `validate` aux DTOs :
  - `InitiateChunkedUploadRequest`
  - `UpdateTrackRequest`
  - `CompleteChunkedUploadRequest`
  - `BatchDeleteRequest`
  - `BatchUpdateRequest`
  - `CreateShareRequest`
  - `StreamCallbackRequest`

### 3. `internal/handlers/profile_handler.go`
- ✅ Ajout tags `validate` à `UpdateProfileRequest`
- ✅ Utilise déjà `BindAndValidateJSON()` de `CommonHandler` (pas de changement)

### 4. `internal/common/validation_test.go` (nouveau)
- ✅ Tests unitaires complets :
  - Requête valide
  - Champ requis manquant
  - UUID invalide
  - String trop longue
  - Nombre hors limites
  - String vide
  - JSON malformé
  - Body trop gros

---

## 🎯 ENDPOINTS COUVERTS

### TrackHandler (`/api/v1/tracks/*`)

| Endpoint | Méthode | DTO | Validation |
|----------|---------|-----|------------|
| `/tracks/initiate` | POST | `InitiateChunkedUploadRequest` | ✅ Tags `validate` + helper |
| `/tracks/{id}` | PUT | `UpdateTrackRequest` | ✅ Tags `validate` + helper |
| `/tracks/complete` | POST | `CompleteChunkedUploadRequest` | ✅ Tags `validate` + helper |
| `/tracks/batch/delete` | POST | `BatchDeleteRequest` | ✅ Tags `validate` + helper |
| `/tracks/batch/update` | POST | `BatchUpdateRequest` | ✅ Tags `validate` + helper |
| `/tracks/{id}/share` | POST | `CreateShareRequest` | ✅ Tags `validate` + helper |
| `/tracks/stream/callback` | POST | `StreamCallbackRequest` | ✅ Tags `validate` + helper |

### ProfileHandler (`/api/v1/users/*`)

| Endpoint | Méthode | DTO | Validation |
|----------|---------|-----|------------|
| `/users/{id}` | PUT | `UpdateProfileRequest` | ✅ Tags `validate` + helper (déjà utilisé) |

### PlaylistHandler (`/api/v1/playlists/*`)

| Endpoint | Méthode | DTO | Validation |
|----------|---------|-----|------------|
| `/playlists` | POST | `CreatePlaylistRequest` | ✅ Déjà utilise `BindAndValidateJSON()` |
| `/playlists/{id}` | PUT | `UpdatePlaylistRequest` | ✅ Déjà utilise `BindAndValidateJSON()` |
| `/playlists/{id}/reorder` | POST | `ReorderTracksRequest` | ✅ Déjà utilise `BindAndValidateJSON()` |

---

## 🚫 ENDPOINTS HORS SCOPE (justifiés)

### 1. Endpoints avec `ShouldBind()` (multipart/form-data)
- **`/tracks/chunk`** (POST) : `UploadChunkRequest` utilise `form` tags (multipart)
  - **Justification** : Validation différente (fichiers, pas JSON)
  - **Action** : Hors scope P1-002 (validation JSON uniquement)

### 2. Endpoints avec `ShouldBind()` (query params)
- Endpoints avec query params uniquement
  - **Justification** : Pas de body JSON, validation différente
  - **Action** : Hors scope P1-002

### 3. Handlers dans `.backup-pre-uuid-migration/`
- **Justification** : Fichiers de backup, non utilisés
- **Action** : Ignorés

---

## 📊 EXEMPLES D'ERREURS JSON RETOURNÉES

### Cas 1 : Champ requis manquant

**Requête** :
```json
{
  "year": 2020
  // title manquant
}
```

**Réponse** (HTTP 400) :
```json
{
  "success": false,
  "error": {
    "code": 400,
    "message": "Validation failed",
    "details": {
      "title": "title is required"
    }
  }
}
```

### Cas 2 : UUID invalide

**Requête** :
```json
{
  "title": "Test Track",
  "track_id": "invalid-uuid"
}
```

**Réponse** (HTTP 400) :
```json
{
  "success": false,
  "error": {
    "code": 400,
    "message": "Validation failed",
    "details": {
      "track_id": "track_id must be a valid UUID"
    }
  }
}
```

### Cas 3 : String trop longue

**Requête** :
```json
{
  "title": "A" * 300,  // 300 caractères > max 255
  "track_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

**Réponse** (HTTP 400) :
```json
{
  "success": false,
  "error": {
    "code": 400,
    "message": "Validation failed",
    "details": {
      "title": "title must be at most 255 characters"
    }
  }
}
```

### Cas 4 : Nombre hors limites

**Requête** :
```json
{
  "title": "Test Track",
  "year": 1800,  // < min 1900
  "track_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

**Réponse** (HTTP 400) :
```json
{
  "success": false,
  "error": {
    "code": 400,
    "message": "Validation failed",
    "details": {
      "year": "year must be greater than or equal to 1900"
    }
  }
}
```

### Cas 5 : JSON malformé

**Requête** :
```json
{
  "title": "Test",
  "track_id": invalid
}
```

**Réponse** (HTTP 400) :
```json
{
  "error": "Invalid JSON syntax at offset 35: invalid character 'i' looking for beginning of value"
}
```

---

## 🧪 TESTS

### Tests unitaires

```bash
go test ./internal/common -run TestBindAndValidateJSON -v -count=1
```

**Résultat** : ✅ **Tous les tests passent**
- `TestBindAndValidateJSON_ValidRequest` — PASS
- `TestBindAndValidateJSON_MissingRequiredField` — PASS
- `TestBindAndValidateJSON_InvalidUUID` — PASS
- `TestBindAndValidateJSON_StringTooLong` — PASS
- `TestBindAndValidateJSON_NumberOutOfRange` — PASS
- `TestBindAndValidateJSON_EmptyString` — PASS
- `TestBindAndValidateJSON_InvalidJSON` — PASS
- `TestBindAndValidateJSON_BodyTooLarge` — PASS

### Tests complets

```bash
go test ./... -count=1
```

**Résultat** : Tests unitaires P1-002 passent. Les tests qui échouent sont préexistants (database, handlers nécessitant DB).

---

## 🔧 HELPER CENTRALISÉ

### `common.BindAndValidateJSON(c *gin.Context, obj interface{}) bool`

**Fonctionnalités** :
1. ✅ Vérifie la taille du body (max 10MB)
2. ✅ Parse le JSON avec `ShouldBindJSON` (Gin)
3. ✅ Valide avec `go-playground/validator` (tags `validate`)
4. ✅ Retourne `false` si erreur (erreur déjà envoyée au client avec HTTP 400)
5. ✅ Format d'erreur standardisé via `response.ValidationError()`

**Usage** :
```go
var req UpdateTrackRequest
if !common.BindAndValidateJSON(c, &req) {
    return // Erreur déjà envoyée au client
}
// req est validé et prêt à être utilisé
```

---

## 📝 TAGS VALIDATE AJOUTÉS

### Exemples de DTOs mis à jour

#### `UpdateTrackRequest`
```go
type UpdateTrackRequest struct {
    Title    *string `json:"title" binding:"omitempty,min=1,max=255" validate:"omitempty,min=1,max=255"`
    Artist   *string `json:"artist" binding:"omitempty,max=255" validate:"omitempty,max=255"`
    Year     *int    `json:"year" binding:"omitempty,min=1900,max=2100" validate:"omitempty,min=1900,max=2100"`
}
```

#### `InitiateChunkedUploadRequest`
```go
type InitiateChunkedUploadRequest struct {
    TotalChunks int    `json:"total_chunks" binding:"required,min=1" validate:"required,min=1"`
    TotalSize   int64  `json:"total_size" binding:"required,min=1" validate:"required,min=1"`
    Filename    string `json:"filename" binding:"required" validate:"required"`
}
```

#### `BatchDeleteRequest`
```go
type BatchDeleteRequest struct {
    TrackIDs []string `json:"track_ids" binding:"required" validate:"required,min=1,dive,uuid"`
}
```

---

## ✅ VALIDATION

### Compilation

```bash
go build ./...
```

**Résultat** : ✅ **Compilation réussie**

### Tests unitaires

```bash
go test ./internal/common -run TestBindAndValidateJSON -v
```

**Résultat** : ✅ **Tous les tests passent (8/8)**

### Tests complets

```bash
go test ./... -count=1
```

**Résultat** : Tests unitaires P1-002 passent. Les tests qui échouent sont préexistants.

---

## 🎯 OBJECTIFS ATTEINTS

- ✅ **Helper centralisé** : `common.BindAndValidateJSON()` utilisable par tous les handlers
- ✅ **DTOs avec tags validate** : Tous les DTOs critiques ont des tags `validate`
- ✅ **Pas de validation inline** : Tous les handlers utilisent le helper
- ✅ **Format d'erreur standardisé** : HTTP 400 avec détails structurés
- ✅ **Tests complets** : Cas invalides, limites, UUID invalides, body trop gros

---

## 📋 COMMANDES DE VALIDATION

### Tests unitaires
```bash
go test ./internal/common -run TestBindAndValidateJSON -v -count=1
```

### Tests complets
```bash
go test ./... -count=1
```

### Compilation
```bash
go build ./...
```

---

**Statut final** : ✅ **P1-002 IMPLÉMENTÉ ET VALIDÉ**
