# Guide de Test avec Postman - Xpeditis API

## 📦 Importer la Collection Postman

### Option 1 : Importer le fichier JSON

1. Ouvrez Postman
2. Cliquez sur **"Import"** (en haut à gauche)
3. Sélectionnez le fichier : `postman/Xpeditis_API.postman_collection.json`
4. Cliquez sur **"Import"**

### Option 2 : Collection créée manuellement

La collection contient **13 requêtes** organisées en 3 dossiers :
- **Rates API** (4 requêtes)
- **Bookings API** (6 requêtes)
- **Health & Status** (1 requête)

---

## 🚀 Avant de Commencer

### 1. Démarrer les Services

```bash
# Terminal 1 : PostgreSQL
# Assurez-vous que PostgreSQL est démarré

# Terminal 2 : Redis
redis-server

# Terminal 3 : Backend API
cd apps/backend
npm run dev
```

L'API sera disponible sur : **http://localhost:4000**

### 2. Configurer les Variables d'Environnement

La collection utilise les variables suivantes :

| Variable | Valeur par défaut | Description |
|----------|-------------------|-------------|
| `baseUrl` | `http://localhost:4000` | URL de base de l'API |
| `rateQuoteId` | (auto) | ID du tarif (sauvegardé automatiquement) |
| `bookingId` | (auto) | ID de la réservation (auto) |
| `bookingNumber` | (auto) | Numéro de réservation (auto) |

**Note :** Les variables `rateQuoteId`, `bookingId` et `bookingNumber` sont automatiquement sauvegardées après les requêtes correspondantes.

---

## 📋 Scénario de Test Complet

### Étape 1 : Rechercher des Tarifs Maritimes

**Requête :** `POST /api/v1/rates/search`

**Dossier :** Rates API → Search Rates - Rotterdam to Shanghai

**Corps de la requête :**
```json
{
  "origin": "NLRTM",
  "destination": "CNSHA",
  "containerType": "40HC",
  "mode": "FCL",
  "departureDate": "2025-02-15",
  "quantity": 2,
  "weight": 20000,
  "isHazmat": false
}
```

**Codes de port courants :**
- `NLRTM` - Rotterdam, Pays-Bas
- `CNSHA` - Shanghai, Chine
- `DEHAM` - Hamburg, Allemagne
- `USLAX` - Los Angeles, États-Unis
- `SGSIN` - Singapore
- `USNYC` - New York, États-Unis
- `GBSOU` - Southampton, Royaume-Uni

**Types de conteneurs :**
- `20DRY` - Conteneur 20 pieds standard
- `20HC` - Conteneur 20 pieds High Cube
- `40DRY` - Conteneur 40 pieds standard
- `40HC` - Conteneur 40 pieds High Cube (le plus courant)
- `40REEFER` - Conteneur 40 pieds réfrigéré
- `45HC` - Conteneur 45 pieds High Cube

**Réponse attendue (200 OK) :**
```json
{
  "quotes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "carrierId": "...",
      "carrierName": "Maersk Line",
      "carrierCode": "MAERSK",
      "origin": {
        "code": "NLRTM",
        "name": "Rotterdam",
        "country": "Netherlands"
      },
      "destination": {
        "code": "CNSHA",
        "name": "Shanghai",
        "country": "China"
      },
      "pricing": {
        "baseFreight": 1500.0,
        "surcharges": [
          {
            "type": "BAF",
            "description": "Bunker Adjustment Factor",
            "amount": 150.0,
            "currency": "USD"
          }
        ],
        "totalAmount": 1700.0,
        "currency": "USD"
      },
      "containerType": "40HC",
      "mode": "FCL",
      "etd": "2025-02-15T10:00:00Z",
      "eta": "2025-03-17T14:00:00Z",
      "transitDays": 30,
      "route": [...],
      "availability": 85,
      "frequency": "Weekly"
    }
  ],
  "count": 5,
  "fromCache": false,
  "responseTimeMs": 234
}
```

**✅ Tests automatiques :**
- Vérifie le status code 200
- Vérifie la présence du tableau `quotes`
- Vérifie le temps de réponse < 3s
- **Sauvegarde automatiquement le premier `rateQuoteId`** pour l'étape suivante

**💡 Note :** Le `rateQuoteId` est **indispensable** pour créer une réservation !

---

### Étape 2 : Créer une Réservation

**Requête :** `POST /api/v1/bookings`

**Dossier :** Bookings API → Create Booking

**Prérequis :** Avoir exécuté l'étape 1 pour obtenir un `rateQuoteId`

**Corps de la requête :**
```json
{
  "rateQuoteId": "{{rateQuoteId}}",
  "shipper": {
    "name": "Acme Corporation",
    "address": {
      "street": "123 Main Street",
      "city": "Rotterdam",
      "postalCode": "3000 AB",
      "country": "NL"
    },
    "contactName": "John Doe",
    "contactEmail": "john.doe@acme.com",
    "contactPhone": "+31612345678"
  },
  "consignee": {
    "name": "Shanghai Imports Ltd",
    "address": {
      "street": "456 Trade Avenue",
      "city": "Shanghai",
      "postalCode": "200000",
      "country": "CN"
    },
    "contactName": "Jane Smith",
    "contactEmail": "jane.smith@shanghai-imports.cn",
    "contactPhone": "+8613812345678"
  },
  "cargoDescription": "Electronics and consumer goods for retail distribution",
  "containers": [
    {
      "type": "40HC",
      "containerNumber": "ABCU1234567",
      "vgm": 22000,
      "sealNumber": "SEAL123456"
    }
  ],
  "specialInstructions": "Please handle with care. Delivery before 5 PM."
}
```

**Réponse attendue (201 Created) :**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440001",
  "bookingNumber": "WCM-2025-ABC123",
  "status": "draft",
  "shipper": {...},
  "consignee": {...},
  "cargoDescription": "Electronics and consumer goods for retail distribution",
  "containers": [
    {
      "id": "...",
      "type": "40HC",
      "containerNumber": "ABCU1234567",
      "vgm": 22000,
      "sealNumber": "SEAL123456"
    }
  ],
  "specialInstructions": "Please handle with care. Delivery before 5 PM.",
  "rateQuote": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "carrierName": "Maersk Line",
    "origin": {...},
    "destination": {...},
    "pricing": {...}
  },
  "createdAt": "2025-02-15T10:00:00Z",
  "updatedAt": "2025-02-15T10:00:00Z"
}
```

**✅ Tests automatiques :**
- Vérifie le status code 201
- Vérifie la présence de `id` et `bookingNumber`
- Vérifie le format du numéro : `WCM-YYYY-XXXXXX`
- Vérifie que le statut initial est `draft`
- **Sauvegarde automatiquement `bookingId` et `bookingNumber`**

**Statuts de réservation possibles :**
- `draft` → Brouillon (modifiable)
- `pending_confirmation` → En attente de confirmation transporteur
- `confirmed` → Confirmé par le transporteur
- `in_transit` → En transit
- `delivered` → Livré (état final)
- `cancelled` → Annulé (état final)

---

### Étape 3 : Consulter une Réservation par ID

**Requête :** `GET /api/v1/bookings/{{bookingId}}`

**Dossier :** Bookings API → Get Booking by ID

**Prérequis :** Avoir exécuté l'étape 2

Aucun corps de requête nécessaire. Le `bookingId` est automatiquement utilisé depuis les variables d'environnement.

**Réponse attendue (200 OK) :** Même structure que la création

---

### Étape 4 : Consulter une Réservation par Numéro

**Requête :** `GET /api/v1/bookings/number/{{bookingNumber}}`

**Dossier :** Bookings API → Get Booking by Booking Number

**Prérequis :** Avoir exécuté l'étape 2

Exemple de numéro : `WCM-2025-ABC123`

**Avantage :** Format plus convivial que l'UUID pour les utilisateurs finaux.

---

### Étape 5 : Lister les Réservations avec Pagination

**Requête :** `GET /api/v1/bookings?page=1&pageSize=20`

**Dossier :** Bookings API → List Bookings (Paginated)

**Paramètres de requête :**
- `page` : Numéro de page (défaut : 1)
- `pageSize` : Nombre d'éléments par page (défaut : 20, max : 100)
- `status` : Filtrer par statut (optionnel)

**Exemples d'URLs :**
```
GET /api/v1/bookings?page=1&pageSize=20
GET /api/v1/bookings?page=2&pageSize=10
GET /api/v1/bookings?page=1&pageSize=20&status=draft
GET /api/v1/bookings?status=confirmed
```

**Réponse attendue (200 OK) :**
```json
{
  "bookings": [
    {
      "id": "...",
      "bookingNumber": "WCM-2025-ABC123",
      "status": "draft",
      "shipperName": "Acme Corporation",
      "consigneeName": "Shanghai Imports Ltd",
      "originPort": "NLRTM",
      "destinationPort": "CNSHA",
      "carrierName": "Maersk Line",
      "etd": "2025-02-15T10:00:00Z",
      "eta": "2025-03-17T14:00:00Z",
      "totalAmount": 1700.0,
      "currency": "USD",
      "createdAt": "2025-02-15T10:00:00Z"
    }
  ],
  "total": 25,
  "page": 1,
  "pageSize": 20,
  "totalPages": 2
}
```

---

## ❌ Tests d'Erreurs

### Test 1 : Code de Port Invalide

**Requête :** Rates API → Search Rates - Invalid Port Code (Error)

**Corps de la requête :**
```json
{
  "origin": "INVALID",
  "destination": "CNSHA",
  "containerType": "40HC",
  "mode": "FCL",
  "departureDate": "2025-02-15"
}
```

**Réponse attendue (400 Bad Request) :**
```json
{
  "statusCode": 400,
  "message": [
    "Origin must be a valid 5-character UN/LOCODE (e.g., NLRTM)"
  ],
  "error": "Bad Request"
}
```

---

### Test 2 : Validation de Réservation

**Requête :** Bookings API → Create Booking - Validation Error

**Corps de la requête :**
```json
{
  "rateQuoteId": "invalid-uuid",
  "shipper": {
    "name": "A",
    "address": {
      "street": "123",
      "city": "R",
      "postalCode": "3000",
      "country": "INVALID"
    },
    "contactName": "J",
    "contactEmail": "invalid-email",
    "contactPhone": "123"
  },
  "consignee": {...},
  "cargoDescription": "Short",
  "containers": []
}
```

**Réponse attendue (400 Bad Request) :**
```json
{
  "statusCode": 400,
  "message": [
    "Rate quote ID must be a valid UUID",
    "Name must be at least 2 characters",
    "Contact email must be a valid email address",
    "Contact phone must be a valid international phone number",
    "Country must be a valid 2-letter ISO country code",
    "Cargo description must be at least 10 characters"
  ],
  "error": "Bad Request"
}
```

---

## 📊 Variables d'Environnement Postman

### Configuration Recommandée

1. Créez un **Environment** nommé "Xpeditis Local"
2. Ajoutez les variables suivantes :

| Variable | Type | Valeur Initiale | Valeur Courante |
|----------|------|-----------------|-----------------|
| `baseUrl` | default | `http://localhost:4000` | `http://localhost:4000` |
| `rateQuoteId` | default | (vide) | (auto-rempli) |
| `bookingId` | default | (vide) | (auto-rempli) |
| `bookingNumber` | default | (vide) | (auto-rempli) |

3. Sélectionnez l'environnement "Xpeditis Local" dans Postman

---

## 🔍 Tests Automatiques Intégrés

Chaque requête contient des **tests automatiques** dans l'onglet "Tests" :

```javascript
// Exemple de tests intégrés
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Response has quotes array", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property('quotes');
    pm.expect(jsonData.quotes).to.be.an('array');
});

// Sauvegarde automatique de variables
pm.environment.set("rateQuoteId", pm.response.json().quotes[0].id);
```

**Voir les résultats :**
- Onglet **"Test Results"** après chaque requête
- Indicateurs ✅ ou ❌ pour chaque test

---

## 🚨 Dépannage

### Erreur : "Cannot connect to server"

**Cause :** Le serveur backend n'est pas démarré

**Solution :**
```bash
cd apps/backend
npm run dev
```

Vérifiez que vous voyez : `[Nest] Application is running on: http://localhost:4000`

---

### Erreur : "rateQuoteId is not defined"

**Cause :** Vous essayez de créer une réservation sans avoir recherché de tarif

**Solution :** Exécutez d'abord **"Search Rates - Rotterdam to Shanghai"**

---

### Erreur 500 : "Internal Server Error"

**Cause possible :**
1. Base de données PostgreSQL non démarrée
2. Redis non démarré
3. Variables d'environnement manquantes

**Solution :**
```bash
# Vérifier PostgreSQL
psql -U postgres -h localhost

# Vérifier Redis
redis-cli ping
# Devrait retourner: PONG

# Vérifier les variables d'environnement
cat apps/backend/.env
```

---

### Erreur 404 : "Not Found"

**Cause :** L'ID ou le numéro de réservation n'existe pas

**Solution :** Vérifiez que vous avez créé une réservation avant de la consulter

---

## 📈 Utilisation Avancée

### Exécuter Toute la Collection

1. Cliquez sur les **"..."** à côté du nom de la collection
2. Sélectionnez **"Run collection"**
3. Sélectionnez les requêtes à exécuter
4. Cliquez sur **"Run Xpeditis API"**

**Ordre recommandé :**
1. Search Rates - Rotterdam to Shanghai
2. Create Booking
3. Get Booking by ID
4. Get Booking by Booking Number
5. List Bookings (Paginated)

---

### Newman (CLI Postman)

Pour automatiser les tests en ligne de commande :

```bash
# Installer Newman
npm install -g newman

# Exécuter la collection
newman run postman/Xpeditis_API.postman_collection.json \
  --environment postman/Xpeditis_Local.postman_environment.json

# Avec rapport HTML
newman run postman/Xpeditis_API.postman_collection.json \
  --reporters cli,html \
  --reporter-html-export newman-report.html
```

---

## 📚 Ressources Supplémentaires

### Documentation API Complète

Voir : `apps/backend/docs/API.md`

### Codes de Port UN/LOCODE

Liste complète : https://unece.org/trade/cefact/unlocode-code-list-country-and-territory

**Codes courants :**
- Europe : NLRTM (Rotterdam), DEHAM (Hamburg), GBSOU (Southampton)
- Asie : CNSHA (Shanghai), SGSIN (Singapore), HKHKG (Hong Kong)
- Amérique : USLAX (Los Angeles), USNYC (New York), USHOU (Houston)

### Classes IMO (Marchandises Dangereuses)

1. Explosifs
2. Gaz
3. Liquides inflammables
4. Solides inflammables
5. Substances comburantes
6. Substances toxiques
7. Matières radioactives
8. Substances corrosives
9. Matières dangereuses diverses

---

## ✅ Checklist de Test

- [ ] Recherche de tarifs Rotterdam → Shanghai
- [ ] Recherche de tarifs avec autres ports
- [ ] Recherche avec marchandises dangereuses
- [ ] Test de validation (code port invalide)
- [ ] Création de réservation complète
- [ ] Consultation par ID
- [ ] Consultation par numéro de réservation
- [ ] Liste paginée (page 1)
- [ ] Liste avec filtre de statut
- [ ] Test de validation (réservation invalide)
- [ ] Vérification des tests automatiques
- [ ] Temps de réponse acceptable (<3s pour recherche)

---

**Version :** 1.0
**Dernière mise à jour :** Février 2025
**Statut :** Phase 1 MVP - Tests Fonctionnels
