MdW v1.1.2

Mock MdW v2.2.1 — Guide

Présentation

Simulateur REST du Middleware MA, conforme au CI AppMNG↔MdW v2.0.0. Deux modes : mock (logique locale + envoi auto vers Mock MK6) ou recette (proxy transparent vers le vrai MdW Ignition/Operametrix).


Démarrage rapide

pip install fastapi uvicorn httpx markdown

# HTTPS mode recette (dĂ©faut — pointe vers Ignition interne)
python mock_mdw.py --port 9000 --ssl

# HTTP développement local (mode mock)
python mock_mdw.py --port 9000 --mode mock

# Surcharge URL Ignition via CLI
python mock_mdw.py --port 9000 --ssl --mode recette \
  --mdw-url https://cot-scada-frc.inxops.com/system/webdev/Middleware

Configuration

Cascade de priorités

Le mock résout chaque paramÚtre selon cette cascade (priorité décroissante) :

CLI args  →  ENV vars  →  Fichier config "runtime"  →  Defaults hardcodĂ©s
(priorité max)                                          (priorité min)

ParamĂštres configurables

ParamĂštre CLI ENV Config runtime Default
Chemin config --config /path MOCK_MDW_CONFIG_PATH — ./DEFAULT_CONFIG.json
Mode --mode recette MOCK_MDW_MODE runtime.mode recette
URL MdW Ignition (interne) --mdw-url https://... MOCK_MDW_URL runtime.mdw_url https://cot-scada-frc.inxops.com/.../Middleware
URL publique (navigateur) — — runtime.public_base_url https://manege.inxops.com
URL MK6 fallback — MOCK_MK6_URL — https://localhost:9001

Architecture des URLs — interne vs publique

Le mock gĂšre deux circuits d'URL distincts :

Circuit Usage Résolution Exemple
Interne (proxy) Appels httpx serveur → Ignition DNS interne Azure / Docker https://cot-scada-frc.inxops.com/system/webdev/Middleware
Publique (navigateur) Liens <a href>, messages confirm, ancres 🔗 DNS public / IP accessible au navigateur https://manege.inxops.com/system/webdev/Middleware

RĂšgle : tout appel qui passe par le serveur (proxy recette, mk6-proxy, execute) utilise l'URL interne. Les URLs publiques ne sont jamais envoyĂ©es Ă  un proxy — elles peuplent uniquement le DOM HTML.

L'URL publique est résolue selon cette cascade : 1. Champ IHM "URL publique" (si rempli) 2. runtime.public_base_url dans DEFAULT_CONFIG.json 3. window.location.hostname du navigateur (fallback auto)

Fichier DEFAULT_CONFIG.json

Trois rÎles dans un seul fichier : - Topologie (obligatoire) : raccordements[], mk6_url - Runtime (optionnel, section "runtime") : mode, mdw_url, public_base_url, proxy_whitelist - Whitelist SSRF (optionnel) : URLs supplémentaires pour les proxys recette

{
  "runtime": {
    "mode": "recette",
    "mdw_url": "https://cot-scada-frc.inxops.com/system/webdev/Middleware",
    "public_base_url": "https://manege.inxops.com",
    "proxy_whitelist": ["https://20.19.89.53:8088", "http://mock-mdw:9000"]
  },
  "mk6_url": "https://localhost:9001",
  "raccordements": [
    {
      "raccordement_id": "ROU1.1",
      "mk6_id": "MK6_ROU1.1",
      "mk6_url": "https://localhost:9001",
      "prm_status": [
        { "prm_id": "50048589698480", "alias": "ROU1 PDL1", "pmax_kw": 8340, "sycode": "8FV898VMwWX0", "role": "thissite" }
      ]
    }
  ]
}

Note : le fichier DEFAULT_CONFIG_HTTPS.json a ete supprime en v1.0.2. Toutes les URLs MK6 sont en HTTPS par defaut dans DEFAULT_CONFIG.json (la VM recette tourne integralement en HTTPS).

Exemple Docker Compose

services:
  mock-mdw:
    image: python:3.12-slim
    command: python mock_mdw.py --port 9000 --ssl
    environment:
      - MOCK_MDW_MODE=recette
      - MOCK_MDW_URL=https://ignition.dev.operalink.fr/system/webdev/Middleware
      - MOCK_MK6_URL=https://mock-mk6:9001
    volumes:
      - ./my_config.json:/app/DEFAULT_CONFIG.json
    ports:
      - "9000:9000"

Docker et URLs publiques : dans my_config.json, renseigner runtime.public_base_url avec l'URL accessible au navigateur de l'opĂ©rateur (ex: https://manege.inxops.com). Les URLs internes (mock-mk6:9001, ignition.dev.operalink.fr) ne sont pas accessibles au navigateur — elles ne servent qu'aux proxys serveur.


Modes de fonctionnement

Mode mock (développement local)

Le simulateur valide la commande localement (DEC-047), la stocke, la traduit en appels MK6 (CI GW↔MK6) et les exĂ©cute vers le Mock MK6. Utile pour dĂ©velopper sans Ignition. Activable via --mode mock ou dans l'IHM.

Mode recette (dĂ©faut — proxy transparent)

La commande est envoyĂ©e telle quelle au vrai MdW Ignition via state.mdw_url (URL interne). Aucune validation locale — c'est Ignition qui valide. Cela permet de tester des cas invalides (value_kw nĂ©gatif, > Pmax) et d'observer la rĂ©action du vrai MdW.

Le simulateur récupÚre ensuite les logs /health MdW et GW pour les afficher. Les liens cliquables dans les résultats utilisent l'URL publique (public_base_url).


API Reference

Endpoints mĂ©tier — CI AppMNG↔MdW v2.0.0

Méthode Endpoint Description Référence CI
POST /api/v1/commands Soumettre une commande de consigne §4.1
GET /api/v1/health Health check (degraded/error + details R3) §4.4
GET /api/v1/status Supervision PRM (état, disponibilité, streem_connected) §4.2
GET /api/v1/planning Planning consolidé (filtrable par prm_id) §4.3
GET /api/v1/gw/health ConnectivitĂ© GW→MK6 par raccordement (R3-E6-S03) DEC-071

Validation POST /commands — ordre DEC-047

Le mock applique les validations dans cet ordre strict (court-circuit Ă  la premiĂšre erreur) :

  1. 400 — Structure : champs requis, format ISO 8601
  2. 404 — PRM inexistant
  3. 503 — PRM indisponible (supervision > 20 min)
  4. 422 — SĂ©mantique : type inconnu, value_kw hors bornes, chronologie
  5. 409 — Anti-replay : order_id dĂ©jĂ  acceptĂ©

En mode recette, cette validation est bypassée.

Endpoints mock (pilotage)

Méthode Endpoint Description
POST /mock/reset Purge + re-provision depuis le fichier config
GET/POST /mock/config Configuration raccordements/PRM
GET/POST /mock/time Horloge simulée (figer/libérer)
GET/POST /mock/mode Basculer mock ↔ recette
GET/POST /mock/auth Configurer Basic Auth + scope (DEC-155, R1)
POST /mock/prm/{id}/fault Marquer un PRM en dĂ©faut matĂ©riel (→ 503)
DELETE /mock/prm/{id}/fault Rétablir un PRM faulté
POST /mock/prm/{id}/expire Expirer supervision PRM (→ 503)
POST /mock/prm/{id}/restore Restaurer supervision PRM
POST /mock/prm/bulk-supervision Expire/restore en masse (tout ou liste)
POST /mock/sync-mk6 Sync supervision + monitoring depuis MK6 réels
GET/POST /mock/proxy-whitelist Whitelist URLs proxy recette (anti-SSRF)
GET/POST /mock/error-config Configuration des erreurs simulées
POST /mock/error-config/reset Désactiver toutes les erreurs + purge R3
POST/DELETE/GET /mock/bdd-fault Simuler BDD inaccessible (R3-E6-S02)
POST/DELETE /mock/pmax-override Override Pmax par source (R3-E6-S01)
POST /mock/silence-alert-min Configurer seuil silence Streem (R3-E2-S02)
GET /mock/state État complet (PRM, supervision, planning, SIEM, R3)
GET /mock/tls État TLS de l'instance

Endpoints simulation

Méthode Endpoint Description
GET /simulation Page HTML de simulation
GET /simulation/api/config Config PRM pour la page
POST /simulation/api/execute Exécution E2E (mock ou recette)
POST /simulation/api/recette-health Proxy health (évite CORS, anti-SSRF)
POST /simulation/api/recette-proxy Proxy generique vers MdW/GW (anti-SSRF)
POST /simulation/api/passthrough Proxy direct avec headers custom (v1.1.0 — cyber C01-C06)
POST /simulation/api/mk6-proxy Proxy actions MK6 (Docker-safe)
GET /simulation/api/mk6-tree Etat agrege de tous les MK6
POST /simulation/api/mk6-reset-all Reset tous les MK6
GET /simulation/api/state Etat simulation complet
POST /simulation/api/run-scenario Scenario temporise synchrone (C3)
GET /simulation/api/export-results Export JSON/CSV (C4 — deprecie, utiliser campagne)
POST /mock/test/campaign Lancer campagne de test async (CI + IHM) — v1.1.0
GET /mock/test/campaign/{campaign_id} Poll resultat campagne (rapport complet)
GET /mock/test/campaigns Liste des campagnes
GET /static/{filename} JS modules (sim_core, sim_queue, sim_assert, sim_execute)
GET /scenarios Page scénarios curl (rendu Markdown)
GET /simulation/help Manuel utilisateur simulateur (rendu Markdown)

Interface de simulation

File de requĂȘtes

L'interface permet de construire des commandes graphiquement et de les empiler dans une file d'exécution. Fonctionnalités :

Injection d'erreurs

Le panneau "Erreurs simulées" permet d'activer des scénarios d'erreur cÎté MdW (health dégradé, 503, timeouts) via /mock/error-config.

Horloge simulée

Figer l'horloge permet de tester des scénarios temporels sans attendre. L'horloge est synchronisée avec les Mock MK6 (broadcast automatique).

Actions MK6 (panneau Init / Erreurs)

Le simulateur permet de piloter les Mock MK6 sans quitter la page :

Mode d'exécution MK6

Deux modes pour l'envoi des consignes au MK6 aprĂšs validation MdW :

Compatibilité Docker

Toutes les actions MK6 (init, reset, fault, network, state, contrainte externe) passent par un proxy cÎté serveur (/simulation/api/mk6-proxy). Le serveur MdW résout l'URL MK6 via sa configuration interne. Le browser n'appelle jamais directement le mock MK6.

Implication : en Docker Compose, les URLs MK6 peuvent ĂȘtre des noms de service Docker (ex: http://mock-mk6:9001) dans DEFAULT_CONFIG.json — le proxy cĂŽtĂ© serveur rĂ©sout le DNS Docker correctement.

Endpoints proxy disponibles : - POST /simulation/api/mk6-proxy — forward une requĂȘte vers un MK6 - GET /simulation/api/mk6-tree — agrĂšge l'Ă©tat de tous les MK6 - POST /simulation/api/mk6-reset-all — reset tous les MK6

Raccourcis clavier (M2)

Raccourci Action
Ctrl+Enter Ajouter la commande Ă  la file
Ctrl+Shift+Enter Exécuter toute la file (ou reprendre si en pause)
Ctrl+Shift+Delete Vider la file
Escape Fermer les panneaux ouverts (erreurs, console)

Authentification (DEC-155, R1)

Le mock simule la Basic Auth Ignition natif. Par défaut désactivée.

Scope par endpoint

Le scope détermine quels endpoints sont protégés. Deux profils prédéfinis :

Profil Scope Contexte
R2 (défaut MVP) ["commands", "health"] Health = seul endpoint de supervision avant R3
R3 (post GO LIVE) ["commands", "status", "planning"] Status/planning prennent le relais, health redevient libre

Activer

# Profil R2 (défaut)
curl -X POST http://localhost:9000/mock/auth \
  -H 'Content-Type: application/json' \
  -d '{"enabled": true, "username": "admin", "password": "mock_mdw_2026"}'

# Profil R3
curl -X POST http://localhost:9000/mock/auth \
  -H 'Content-Type: application/json' \
  -d '{"enabled": true, "scope": ["commands", "status", "planning"]}'

# Scope custom (tout protéger)
curl -X POST http://localhost:9000/mock/auth \
  -H 'Content-Type: application/json' \
  -d '{"enabled": true, "scope": ["commands", "health", "status", "planning"]}'

Dans le simulateur, le bloc 🔐 dans ⚙ Configuration permet de basculer entre profils R2/R3 en un clic.

Comportement

Situation Réponse Log SIEM
Auth dĂ©sactivĂ©e Transparent (pas de check) —
Endpoint hors scope Transparent (pas de check) —
Pas de header Authorization 401 UNAUTHORIZED AUTH_FAILURE
Schéma non-Basic (ex: Bearer) 401 UNAUTHORIZED AUTH_FAILURE
Credentials invalides 403 FORBIDDEN AUTH_FAILURE
Credentials valides Traitement normal AUTH_SUCCESS

Credentials par défaut : admin / mock_mdw_2026 (configurables via /mock/auth).


Protection SSRF (proxy whitelist)

Les endpoints proxy recette (/simulation/api/recette-health, /simulation/api/recette-proxy) valident l'URL cible contre une whitelist dynamique. URLs non autorisĂ©es → HTTP 403.

La whitelist ne contient que des URLs internes (atteignables par le serveur Python). Les URLs publiques (navigateur) n'y figurent jamais — elles ne sont pas utilisĂ©es par les proxys.

Sources de la whitelist

  1. state.mdw_url — vrai MdW Ignition (configurĂ© via mode/CLI/ENV)
  2. state.mk6_url + state.mk6_urls — MK6 cibles (topologie)
  3. state.proxy_whitelist — URLs additionnelles (Docker, ports alternatifs)
  4. localhost / 127.0.0.1 / ::1 — fallback dev local

Configurer des URLs supplémentaires

Via DEFAULT_CONFIG.json section runtime.proxy_whitelist (au démarrage) :

"runtime": {
  "proxy_whitelist": ["https://20.19.89.53:8088", "http://mock-mdw:9000", "http://mock-mk6:9001"]
}

Ou Ă  chaud :

curl -X POST http://localhost:9000/mock/proxy-whitelist \
  -H 'Content-Type: application/json' \
  -d '{"urls": ["https://new-env:8088"]}'

Supervision PRM (disponibilité)

Le mock MdW maintient un état de supervision par PRM qui détermine la réponse à POST /commands : 200 (disponible) ou 503 PRM_UNAVAILABLE (expiré/faulté).

ModĂšle

Par défaut, tous les PRM sont disponibles aprÚs provisioning (Reset+Init). Deux mécanismes pour rendre un PRM indisponible :

Mécanisme Endpoint Simule Réversible via
Fault POST /mock/prm/{id}/fault Défaut matériel DELETE /mock/prm/{id}/fault
Expire POST /mock/prm/{id}/expire Timeout supervision (20 min sans signal) POST /mock/prm/{id}/restore

Synchronisation MK6→MdW

Le bouton 🔄 Sync MK6→MdW (ou POST /mock/sync-mk6) interroge chaque mock MK6 :

  1. GET /mock/mk6/{id}/state → met Ă  jour la disponibilitĂ© PRM (MK6 offline, site en fault, state=0 → PRM expirĂ©)
  2. GET /api/v1/mk6/{id}/monitoring/sites → cache les donnĂ©es opĂ©rationnelles (vent, puissance, contraintes) pour GET /status

AprĂšs un sync, la supervision PRM dans GET /status reflĂšte l'Ă©tat rĂ©el des MK6 (PRM offline → status KO + anomalies, PRM online → status OK).

ContrĂŽle en masse

# Tout expirer (simule perte de communication généralisée)
curl -X POST http://localhost:9000/mock/prm/bulk-supervision \
  -H 'Content-Type: application/json' -d '{"available": false}'

# Tout restaurer
curl -X POST http://localhost:9000/mock/prm/bulk-supervision \
  -H 'Content-Type: application/json' -d '{"available": true}'

# Expirer certains PRM uniquement
curl -X POST http://localhost:9000/mock/prm/bulk-supervision \
  -H 'Content-Type: application/json' \
  -d '{"prm_ids": ["50048589698480"], "available": false}'

Dans le simulateur

Le panneau 📊 Supervision PRM affiche l'Ă©tat de chaque PRM avec :

Docker / recette

POST /mock/sync-mk6 résout les URLs MK6 via state.get_mk6_url() cÎté serveur. Les noms Docker (http://mock-mk6:9001) fonctionnent. Les endpoints expire/restore/bulk sont purement internes au MdW, aucun appel externe.


Translation MK6

Le mock traduit les commandes MdW en appels MK6 conformément aux décisions :

MdW MK6 Décision
command_type: "psetpoint" set: "Psetpoint" (P majuscule) CI GW↔MK6
command_type: "stop" set: "stop" CI GW↔MK6
command_type: "clear" DELETE /planning/{id} DEC-108/117
preset: true/false preset: "auto"/"none" DEC-120
prm_id → lookup site: sycode DonnĂ©es de rĂ©fĂ©rence

Spécifications API (Swagger / OpenAPI)

Swagger de production MdW (Ă  venir)

Le fichier swagger_mdw_production.yaml.placeholder est un emplacement réservé. Il sera remplacé par le swagger officiel du MdW Ignition une fois le développement Operametrix (Benjamin Freeman) terminé.

Ce swagger de production dĂ©crira les endpoints exposĂ©s par le module WebDev Ignition : - POST /api/v1/commands (CI AppMNG↔MdW §4.1) - GET /api/v1/health (CI AppMNG↔MdW §4.4) - GET /api/v1/status (CI AppMNG↔MdW §4.2) - GET /api/v1/planning (CI AppMNG↔MdW §4.3)

Pour l'intégrer : renommer en swagger_mdw_production.yaml et supprimer le .placeholder.

# Générer un client depuis le swagger de production (quand disponible)
openapi-generator-cli generate -i swagger_mdw_production.yaml -g python -o client_mdw/

Swagger du mock (en ligne)

Le mock expose un Swagger UI interactif sur /docs et la spec JSON brute sur /openapi.json (auto-générés par FastAPI).

Pas de fichier statique dans l'archive — la spec est toujours Ă  jour sur le mock en cours d'exĂ©cution.


Environnement de développement

VS Code

  1. Installer Python 3.11+ et les dépendances (pip install -r requirements.txt)
  2. Ouvrir le dossier dans VS Code
  3. Créer .vscode/launch.json :
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Mock MdW (HTTP :9000)",
      "type": "debugpy",
      "request": "launch",
      "program": "mock_mdw.py",
      "args": ["--port", "9000"]
    },
    {
      "name": "Mock MdW (HTTPS :9000)",
      "type": "debugpy",
      "request": "launch",
      "program": "mock_mdw.py",
      "args": ["--port", "9000", "--ssl"]
    }
  ]
}
  1. F5 pour lancer, ouvrir http://localhost:9000/simulation

Docker

docker build -t mock-mdw .
docker run -p 9000:9000 mock-mdw

Docker Compose avec mock MK6 :

services:
  mock-mdw:
    build: ./mock-mdw
    ports: ["9000:9000"]
  mock-mk6:
    build: ./mock-mk6
    ports: ["9001:9001","9002:9002","9003:9003","9004:9004","9005:9005"]

Mode développement (rechargement à chaud)

Par dĂ©faut, simulation.html est mis en cache au premier chargement. Pour recharger Ă  chaque requĂȘte (utile quand on modifie le HTML) :

MOCK_MDW_DEV=1 python mock_mdw.py --port 9000

Tests

# Tests de régression Python (208 tests, TestClient, rapide)
python test_mock_mdw_regression.py

# Tests d'intégration Python (164 tests, serveur uvicorn réel, HTTPS/TLS)
python test_mock_mdw_integration.py

# Tests JS de simulation.html (94 tests)
npm install jsdom && node test_simulation_js.js

# Structure documentaire (19 tests)
python test_doc_structure.py

Dépannage

SymptĂŽme Cause probable Solution
Connection refused port 9001 Mock MK6 non lancé Lancer python mock_mk6.py --port 9001
URL MdW reset aprĂšs rechargement sessionStorage vide Configurer via ENV ou section runtime
422 en mode recette state.mode pas "recette" Vérifier GET /mock/mode
401 sur POST /commands Auth activée POST /mock/auth {"enabled": false}
403 sur recette-proxy URL hors whitelist POST /mock/proxy-whitelist {"urls": [...]}
HTML inchangé aprÚs modif Cache statique Relancer avec MOCK_MDW_DEV=1

Latence simulée (C2)

Permet de simuler une latence réseau réaliste (100-500ms vers Azure, 200-1000ms MK6 via VPN).

# Activer 200ms de latence
curl -X POST http://localhost:9000/mock/error-config \
  -H 'Content-Type: application/json' \
  -d '{"simulated_latency_ms": 200}'

# Désactiver
curl -X POST http://localhost:9000/mock/error-config \
  -H 'Content-Type: application/json' \
  -d '{"simulated_latency_ms": 0}'

La latence s'ajoute avant chaque rĂ©ponse POST /commands et avant chaque appel MK6 dans execute_mk6_actions(). Configurable aussi dans le simulateur (⚙ Configuration → ⏱ Latence).

Un badge rouge ⚠ > 2s apparaĂźt automatiquement dans les rĂ©sultats si la latence totale dĂ©passe 2 secondes (CI §4.1 / DEC-038).



Campagne de test (CI)

Pour integration continue et campagnes de recette formelles. Meme endpoint que l'IHM (bouton "Campagne").

Lancer une campagne (v1.1.0 — format handoff)

# Envoyer un fichier de campagne JSON directement
curl -s -X POST http://localhost:9000/mock/test/campaign \
  -H "Content-Type: application/json" \
  -d '{"name": "recette_P1", "items": [
    {"_method": "COMMENT", "_label": "Setup"},
    {"_method": "ACTION", "_target": "mk6", "_http_method": "POST",
     "_endpoint": "/mock/mk6/MK6_ROU1.1/provision",
     "_body": {"sites": [{"sycode": "8FV898VMwWX0", "name": "ROU1", "role": "thissite", "pmax_kw": 8340, "prm_id": "50048589698480"}]},
     "_expected_status": 200},
    {"prm_id": "50048589698480", "order_id": "CAMP-001", "source": "MA",
     "start_time": "2026-03-20T14:00:00Z", "preset": false,
     "commands": [{"command_type": "psetpoint", "end_time": "2026-03-20T18:00:00Z", "preset": false, "value_kw": 5000}],
     "_expected_status": 200, "_x_request_id": "camp-001"}
  ], "delay_between_items_ms": 300}'
# -> 202 {"campaign_id": "camp-abc12345", "poll_url": "/mock/test/campaign/camp-abc12345"}

Polling des resultats

# Boucle CI
while true; do
  RESULT=$(curl -s http://localhost:9000/mock/test/campaign/camp-abc12345)
  STATUS=$(echo $RESULT | python3 -c "import sys,json; print(json.load(sys.stdin).get('status'))")
  if [ "$STATUS" = "completed" ]; then break; fi
  sleep 1
done

# Lire le summary
echo $RESULT | python3 -c "
import sys,json
d = json.load(sys.stdin)
s = d['summary']
print(f'PASS:{s[\"pass\"]} FAIL:{s[\"fail\"]} SKIP:{s[\"skip\"]} success:{s[\"success\"]}')
for f in s.get('failures', []):
    print(f'  FAIL #{f[\"step\"]}: {f[\"label\"]}')
    a = f['assertion']
    for detail in a.get('details', []):
        if not detail['pass']:
            print(f'    {detail[\"field\"]}: attendu={detail[\"expected\"]} obtenu={detail[\"actual\"]}')
"

Types d'items supportes

Type Detection Description
COMMENT _method: "COMMENT" Separateur visuel, toujours SKIP
ACTION _method: "ACTION" Appel HTTP vers MK6 ou MdW (setup, teardown, injection faute)
GET _method: "GET" Lecture endpoint (health, planning, monitoring)
COMMAND Presence de prm_id POST /commands vers MdW (mock ou recette)

Chaque item peut porter des champs _expected_* pour produire un verdict PASS/FAIL.

Champs d'assertion (v1.2.0) :

Catégorie Champs
HTTP _expected_status (int), _expected_error_code (string)
SIEM _expected_siem_category, _expected_siem_fields ({key:val}), _expected_siem_count ({category, min}), _expected_log_order ([catégories])
Body _expected_body_contains ({key:val} récursif), _expected_body_not_contains, _expected_body_array_min_length (int, supporte dict+planning[]), _expected_body_field_present ([champs])
Timing _wait_seconds (pause avant exécution)
Méta _label, _breakpoint, _x_request_id, _passthrough, _passthrough_headers

Voir SIMULATION_HELP.md pour la référence complÚte avec exemples.

NouveautĂ©s v1.2.0 : - _expected_body_array_min_length supporte les rĂ©ponses MK6 ({sycode, planning: [...]}) - _expected_body_field_present vĂ©rifie l'existence de champs sans valeur exacte (v1.1.3) - Logs MdW+GW mergĂ©s en mode campagne (fetch GW health avec 3 retries × 1.5s) - Nommage rĂ©sultats EXEC_{source}-{PASS}PASS-{FAIL}FAIL-{timestamp}.json - run_tag ne suffixe plus les order_id/x_request_id vides (fix tests 400 MISSING_FIELD)

Filtrage logs SIEM (v1.1.1) : GET /health?include_logs=true&log_filter=xxx&log_count=50 filtre les logs par substring match sur tous les champs string. En mode recette, log_filter est passé avec le command_trace_id de chaque item pour ne récupérer que les logs liés à ce test. En mode autotest (mock standalone), le mock reproduit ce comportement.

Limitations connues (B1)


Décisions implémentées

DEC-045 (start passé accepté), DEC-047 (validation séquentielle), DEC-080 (consolidation légÚre), DEC-089 (ROUVAI = 5 MK6), DEC-108/117 (clear = DELETE), DEC-112/113 (preset boolean), DEC-120 (translation preset auto/none), DEC-155 (Basic Auth Ignition).


Architecture fichiers (M5/I4)

Structure v1.1.1

mock-mdw/
├── mock_mdw.py          # Serveur FastAPI — endpoints + rĂšgles mĂ©tier MA
├── mock_shared.py       # Utilitaires partagĂ©s (SSL, CSS, filtres uvicorn)
├── mock_framework.py    # Moteur rĂ©utilisable (AssertionEngine, HttpRunner, etc.)
├── sim_assert.py        # Shim rĂ©tro-compatible (re-exports depuis mock_framework)
├── simulation.html      # IHM — structure HTML + CSS
├── sim_core.js          # JS — globals, helpers, time, config, MK6, auth
├── sim_queue.js         # JS — form builder, file, import/export, date shift
├── sim_execute.js       # JS — exĂ©cution, rendu rĂ©sultats, console debug
├── sim_assert.js        # JS — rendu badges verdict, click-to-cycle
├── DEFAULT_CONFIG.json  # Config ROUVAI par dĂ©faut
├── Dockerfile
├── requirements.txt
├── test_mock_mdw_regression.py   # Tests rĂ©gression
├── test_mock_mdw_integration.py  # Tests intĂ©gration
├── test_e2e_mdw_mk6.py           # Tests cross-mock
├── test_sim_assert.py             # Tests assertion engine
├── test_simulation_js.js          # Tests JS (JSDOM)
├── test_doc_structure.py          # Tests structure docs
└── docs (GUIDE, SCENARIOS, README, CHANGELOG, HELP, DOC_GUIDELINES)

Séparation code métier vs utilitaire

Fichier RÎle Réutilisable ?
mock_shared.py SSL auto-signĂ©, CSS Markdown, filtres uvicorn, CORS ✅ Tout projet
mock_framework.py OrderTracker (anti-replay FIFO), TimeController, SupervisionTracker, AuthController, ProxyGuard ✅ Tout mock REST
mock_mdw.py Config ROUVAI, validation DEC-047, translation MK6, SIEM MA ❌ SpĂ©cifique MA

JS modules servis par /static/{filename}

En mode dev (MOCK_MDW_DEV=1), les fichiers JS sont rechargĂ©s Ă  chaque requĂȘte. En mode normal, ils sont cachĂ©s 1h cĂŽtĂ© navigateur.


Isolation des tests (D3)

Chaque test de régression appelle setup() qui remet le state global à zéro. Sans setup(), les tests peuvent collisionner (ex: un test qui change auth_scope impacte le suivant).

RÚgles : - Toujours appeler setup() en début de section de test - Remettre à l'état initial les champs modifiés si setup() n'est pas appelé - known_order_ids utilise OrderTracker du framework (D3) avec éviction FIFO correcte - Les tests ne sont pas parallélisables tant que le state est un singleton global

Parking lot v1.0.3 : rendre le state injectable (MdWState(config) au lieu du singleton state) pour permettre des tests isolés et parallÚles.