MdW v0.9.15

Mock MdW v0.9.15 – Scénarios de test

BASE=http://localhost:9000
MK6=http://localhost:9001
# HTTPS : BASE=https://20.19.89.53:9000 et ajouter -k à chaque curl

Scénario 0 – Initialisation et horloge (v0.9.6)

# Reset + vérifier auto-provision ROUVAI
curl -X POST $BASE/mock/reset
# → {"status": "reset_ok", "prm_count": 12}

# Sans horloge : heure système (pas d'erreur 400)
curl $BASE/mock/time
# → {"now": "2026-02-25T09:00:00Z", "simulated": false}

# Figer l'horloge
curl -X POST $BASE/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T10:00:00Z"}'
# → {"status": "time_set", "now": "2026-03-01T10:00:00+00:00", "simulated": true}

# Libérer l'horloge → retour au temps réel (v0.9.6)
curl -X POST $BASE/mock/time/release
# → {"message": "Horloge liberee — temps reel actif", "now": null, "simulated": false}

Scénario 1 – Commande psetpoint simple (cas passant)

curl -X POST $BASE/mock/reset
curl -X POST $BASE/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T10:00:00Z"}'

# Provisionner Mock MK6 (dans l'autre terminal, port 9001)
curl -X POST $MK6/mock/reset
curl -X POST $MK6/mock/mk6/ROU1.1/provision -H "Content-Type: application/json" \
  -d '{"sites": [
    {"sycode": "8FV898VMwWX0", "name": "ROU1 PDL1", "role": "thissite", "pmax_kw": 8340, "prm_id": "50048589698480"},
    {"sycode": "8FV898VPwW20", "name": "ROU1 PDL2", "role": "nearsite", "pmax_kw": 8340, "prm_id": "50035999249967"}
  ]}'

# Commande psetpoint 5MW sur ROU1 PDL1
curl -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -H "X-Request-ID: req-001" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "ORDER-001",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "preset": false,
    "commands": [
      {"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000, "preset": false}
    ]
  }'
# → 200 {"status": "accepted", "prm_id": "50048589698480", "order_id": "ORDER-001", ...}

# Vérifier que la consigne est arrivée sur le MK6
curl $MK6/api/v1/mk6/ROU1.1/monitoring/sites/8FV898VMwWX0
# → psetpoint_recu: 5000 (après start_time)

Scénario 2 – Commande stop

# (suite du scénario 1)

# Stop sur ROU1 PDL2
curl -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50035999249967",
    "order_id": "ORDER-002",
    "source": "MA",
    "start_time": "2026-03-01T14:00:00Z",
    "commands": [
      {"command_type": "stop", "end_time": "2026-03-01T16:00:00Z"}
    ]
  }'
# → 200 accepted

# Avancer l'horloge
curl -X POST $BASE/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T15:00:00Z"}'

# Vérifier MK6 PDL2 : stop actif
curl $MK6/api/v1/mk6/ROU1.1/monitoring/sites/8FV898VPwW20
# → psetpoint_recu: 0, stop_wf_active: true

Scénario 3 – Multi-segments avec preset

curl -X POST $BASE/mock/reset
curl -X POST $BASE/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T10:00:00Z"}'

# 3 segments : psetpoint → stop → psetpoint, preset sur sortie
curl -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "ORDER-003",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "preset": true,
    "commands": [
      {"command_type": "psetpoint", "end_time": "2026-03-01T14:00:00Z", "value_kw": 6000, "preset": false},
      {"command_type": "stop",      "end_time": "2026-03-01T16:00:00Z", "preset": false},
      {"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 4000, "preset": true}
    ]
  }'
# → 200 accepted, segments_count: 3
# Le preset final génère une 4e requête MK6 : retour Pmax à 18h

Scénario 4 – Erreurs de validation (DEC-047)

curl -X POST $BASE/mock/reset
curl -X POST $BASE/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T10:00:00Z"}'

# 400 FORMAT_ERROR — champ obligatoire manquant
curl -v -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{"prm_id": "50048589698480"}'
# → 400 {"status": "error", "error_code": "FORMAT_ERROR", ...}

# 404 PRM_NOT_FOUND — PRM inconnu
curl -v -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "00000000000000",
    "order_id": "ORDER-ERR-404",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000}]
  }'
# → 404 {"error_code": "PRM_NOT_FOUND"}

# 422 TIME_RANGE_INVALID — end_time <= start_time
curl -v -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "ORDER-ERR-422",
    "source": "MA",
    "start_time": "2026-03-01T18:00:00Z",
    "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T14:00:00Z", "value_kw": 5000}]
  }'
# → 422 {"error_code": "TIME_RANGE_INVALID"}

# 409 DUPLICATE_ORDER — order_id déjà utilisé
curl -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "ORDER-DUP",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000}]
  }'
# → 200 accepted

curl -v -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "ORDER-DUP",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "commands": [{"command_type": "stop", "end_time": "2026-03-01T18:00:00Z"}]
  }'
# → 409 {"error_code": "DUPLICATE_ORDER"}

Scénario 5 – PRM fault (503)

curl -X POST $BASE/mock/reset
curl -X POST $BASE/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T10:00:00Z"}'

# Mettre un PRM en fault
curl -X POST $BASE/mock/prm/50048589698480/fault

# Commande → 503
curl -v -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "ORDER-503",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000}]
  }'
# → 503 {"status": "rejected", "error_code": "PRM_UNAVAILABLE",
#         "available_prm": [...PRM non-faultés...]}

# Rétablir le PRM
curl -X DELETE $BASE/mock/prm/50048589698480/fault

# Commande → 200 à nouveau
curl -X POST $BASE/api/v1/commands \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "ORDER-504",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000}]
  }'
# → 200 accepted

Scénario 6 – Health check

# Health simple (3 champs CI §4.2)
curl $BASE/api/v1/health
# → {"status": "healthy", "timestamp": "...", "version": "1.0.0"}

# Health avec logs SIEM
curl "$BASE/api/v1/health?include_logs=true"
# → {"status": "healthy", ..., "recent_logs": [...]}

Scénario 7 – E2E multi-PRM ROUVAI

# En multi-instance, chaque MK6 tourne sur son propre port :
# MK6_ROU1.1 → :9001, MK6_ROU1.2 → :9002, MK6_ROU1.3 → :9003, MK6_ROU2 → :9004, MK6_VAI → :9005
# Ici on montre le cas simplifié avec un seul mock MK6

# Provisionner les MK6 ROUVAI
curl -X POST $MK6/mock/reset
curl -X POST $MK6/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T10:00:00Z"}'

# MK6 ROU1.1
curl -X POST $MK6/mock/mk6/ROU1.1/provision -H "Content-Type: application/json" \
  -d '{"sites": [
    {"sycode": "8FV898VMwWX0", "name": "ROU1 PDL1", "role": "thissite", "pmax_kw": 8340, "prm_id": "50048589698480"},
    {"sycode": "8FV898VPwW20", "name": "ROU1 PDL2", "role": "nearsite", "pmax_kw": 8340, "prm_id": "50035999249967"}
  ]}'

# MK6 ROU2
curl -X POST $MK6/mock/mk6/ROU2/provision -H "Content-Type: application/json" \
  -d '{"sites": [
    {"sycode": "8FV8CCX2w7X0", "name": "ROU2 PDL10", "role": "thissite", "pmax_kw": 11120, "prm_id": "50048300262939"},
    {"sycode": "8FV8C9MWwHH0", "name": "ROU2 PDL5",  "role": "nearsite", "pmax_kw": 11120, "prm_id": "50049023851893"},
    {"sycode": "8FV8C9MWwHJ0", "name": "ROU2 PDL8",  "role": "nearsite", "pmax_kw": 11120, "prm_id": "50015159886785"},
    {"sycode": "8FV8CCX2w7W0", "name": "ROU2 PDL9",  "role": "nearsite", "pmax_kw": 11120, "prm_id": "50048155545173"}
  ]}'

# Commandes simultanées
curl -X POST $BASE/api/v1/commands -H "Content-Type: application/json" \
  -d '{"prm_id": "50048589698480", "order_id": "E2E-001", "source": "MA",
       "start_time": "2026-03-01T12:00:00Z",
       "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000}]}'

curl -X POST $BASE/api/v1/commands -H "Content-Type: application/json" \
  -d '{"prm_id": "50048300262939", "order_id": "E2E-002", "source": "MA",
       "start_time": "2026-03-01T12:00:00Z",
       "commands": [{"command_type": "stop", "end_time": "2026-03-01T18:00:00Z"}]}'

# Avancer l'horloge sur les deux mocks
curl -X POST $BASE/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T14:00:00Z"}'
curl -X POST $MK6/mock/time -H "Content-Type: application/json" \
  -d '{"now": "2026-03-01T14:00:00Z"}'

# Vérifier résultats MK6
curl $MK6/api/v1/mk6/ROU1.1/monitoring/sites/8FV898VMwWX0
# → psetpoint_recu: 5000

curl $MK6/api/v1/mk6/ROU2/monitoring/sites/8FV8CCX2w7X0
# → psetpoint_recu: 0, stop_wf_active: true

Scénario 8 – Mode recette (proxy vers vrai MdW)

# Configurer l'URL du vrai MdW
curl -X POST $BASE/mock/mode -H "Content-Type: application/json" \
  -d '{"mode": "recette", "mdw_url": "https://mdw.recette.innergex.fr"}'
# → {"status": "mode_set", "mode": "recette", ...}

# Commande proxifiée → vrai MdW
curl -X POST $BASE/api/v1/commands -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "RECETTE-001",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000}]
  }'
# → réponse du vrai MdW

# Retour en mock
curl -X POST $BASE/mock/mode -H "Content-Type: application/json" \
  -d '{"mode": "mock"}'

Scénario 9 – Mode Manuel simulation (v0.9.6)

Utiliser la page http://localhost:9000/simulation, sélectionner ☞ Manuel.

# En mode Manuel, le flag _skip_mk6 est envoyé automatiquement par la page.
# On peut le tester directement :

curl -X POST $BASE/simulation/api/execute \
  -H "Content-Type: application/json" \
  -d '{
    "prm_id": "50048589698480",
    "order_id": "MANUAL-001",
    "source": "MA",
    "start_time": "2026-03-01T12:00:00Z",
    "commands": [{"command_type": "psetpoint", "end_time": "2026-03-01T18:00:00Z", "value_kw": 5000}],
    "_skip_mk6": true
  }'
# → {"mdw_response": {"status_code": 200, ...},
#    "mk6_actions": [{"method": "POST", "url": "http://localhost:9001/...", "payload": {...}}],
#    "_pending": true}

# Les actions sont retournées mais PAS envoyées.
# Envoyer manuellement la 1re action :
curl -X POST http://localhost:9001/api/v1/mk6/MK6_ROU1.1/requests \
  -H "Content-Type: application/json" \
  -d '{"set": "Psetpoint", "site": "8FV898VMwWX0", "psetpoint": 5000,
       "start_time": "2026-03-01T12:00:00Z", "end_time": "2026-03-01T18:00:00Z", "preset": "none"}'