11 KiB
type, project, milestone, date
| type | project | milestone | date |
|---|---|---|---|
| testing | Metamorph | M0 | 2026-05-10 |
Comment tester M0 (bootstrap)
Procédure de validation manuelle + automatisée pour le milestone M0. Toutes les commandes se lancent depuis la racine du repo.
0. Prérequis
Au choix entre Docker ou Podman — le Makefile détecte automatiquement (override ENGINE=docker ou ENGINE=podman si les deux sont installés).
| Outil | Version min | Vérifier |
|---|---|---|
| Docker Engine (option A) | 24+ | docker --version + docker compose version |
| Podman (option B) | 4.0+ avec plugin compose, ou podman-compose 1.0.6+ | podman --version + podman compose version (ou podman-compose --version) |
| GNU make | 4+ | make --version |
| curl, jq | n'importe | curl --version |
| Node.js (pour les e2e) | 20+ | node --version |
Vérifier le moteur que le Makefile utilisera :
make engine
# ENGINE=podman
# COMPOSE=podman compose
Override possible : make up ENGINE=docker COMPOSE="docker compose".
1. Bootstrap de l'environnement
make env # crée .env depuis .env.example
$EDITOR .env # vérifie : APP_ENV=dev OK pour la machine, sinon set des secrets forts
Variables critiques de .env :
APP_ENV—devautorise les placeholders ;prodoustagingexigentJWT_SECRET >=32 chars+POSTGRES_PASSWORDnon-default (sinon l'API refuse de booter).JWT_SECRET— pour M0 le démon ne signe rien, mais autant le mettre propre tout de suite :python3 -c "import secrets; print(secrets.token_urlsafe(64))".HOST_FRONT_PORT/HOST_API_PORT— modifie si 8080/8000 sont déjà occupés.
2. Build & démarrage
make up # build des 3 images + démarrage
make ps # vérifie que les 3 services tournent
Attendu :
metamorph-db postgres:16-alpine Up (healthy)
metamorph-api metamorph-api Up
metamorph-front metamorph-front Up (healthy)
Si db reste en starting au-delà de 30 s : make logs pour voir l'erreur (généralement un mismatch de credentials dans .env).
3. Tests fonctionnels manuels
3.1 — Health API direct (port 8000)
curl -s http://localhost:8000/api/v1/health | jq
Attendu :
{ "status": "ok", "version": "0.1.0" }
3.2 — Health API via le proxy nginx (port 8080)
curl -s http://localhost:8080/api/v1/health | jq
Doit renvoyer le même JSON. Cela valide la conf nginx qui proxifie /api/* → api:8000.
3.3 — SPA dans le navigateur
Ouvrir http://localhost:8080. Vérifications visuelles :
- Header centré, fond
#0a0e1a, titre « Metamorph Purple Team Platform » avec « Meta » en rouge et « Purple Team Platform » en violet. - Section
// System Healthavec une card bordée vert affichantversion 0.1.0etstatus: ok. - Section
// Design Tokensmontrant les tags colorés (EVASION, C2, LATERAL…), la flow chainrecon → phish → c2 → lateral → impact, et 3 boutons. - Footer en mono dim avec la mention M0 bootstrap.
- Toutes les polices sont chargées (titres en JetBrains Mono, body en IBM Plex Sans). Onglet Network : aucune requête vers
fonts.googleapis.comoufonts.gstatic.com. - Console JS sans aucune erreur.
3.4 — Logs structurés JSON
make logs-api # tail uniquement le container api (engine-agnostic)
Attendu : chaque ligne est un objet JSON avec au minimum ts, level, logger, message. Exemple :
{"ts":"2026-05-10 14:21:33,012","level":"INFO","logger":"metamorph.boot","message":"metamorph.api.boot","cors_origins":["http://localhost:8080"],"log_level":"INFO","evidence_dir":"/data/evidence"}
Si tu vois du texte non-JSON, c'est gunicorn qui parle ; vérifier que l'app est bien chargée via app.main:app (le formatter doit s'appliquer).
3.5 — Healthchecks containers
make inspect-health
Attendu : healthy pour les trois containers (à 30 s près après le boot).
metamorph-db healthy
metamorph-api healthy
metamorph-front healthy
3.6 — Garde APP_ENV (sécurité)
Test négatif : on prouve que l'API refuse de booter en non-dev avec un secret faible.
make down
APP_ENV=prod JWT_SECRET=trop-court $(make engine | sed -n 's/^COMPOSE=//p') up api 2>&1 | head -30
# ou plus simplement, en explicitant ton moteur :
# APP_ENV=prod JWT_SECRET=trop-court docker compose up api
# APP_ENV=prod JWT_SECRET=trop-court podman compose up api
Attendu : trace d'erreur Pydantic mentionnant "JWT_SECRET is missing, default, or shorter than 32 chars". L'API doit s'arrêter, pas démarrer.
Reset :
make down && make up
3.7 — CORS
curl -is -H 'Origin: http://localhost:8080' http://localhost:8080/api/v1/health \
| grep -i access-control-allow-origin
Attendu : un header Access-Control-Allow-Origin: http://localhost:8080.
curl -is -H 'Origin: http://evil.example' http://localhost:8080/api/v1/health \
| grep -i access-control-allow-origin || echo "no CORS allow header (expected)"
Attendu : pas de header (origine non-allowée).
3.8 — Volumes persistants
make volumes
Attendu : deux volumes nommés (le préfixe peut varier selon le moteur) :
metamorph_db
metamorph_evidence
Test de persistance basique : make down && make up ne doit pas effacer les volumes ; seul make clean le fait (destructeur, demande explicite).
4. Tests automatisés (Playwright)
make e2e-install # à faire une seule fois (download chromium + deps OS)
make up # si la stack n'est pas déjà up
make e2e # lance la suite
make e2e-report # ouvre le rapport HTML
Suite M0 (e2e/tests/m0-smoke.spec.ts) — 8 tests :
| # | Test | Couvre |
|---|---|---|
| 1 | home page loads and renders the RTOps header | Front + nginx + assets statiques |
| 2 | API health card eventually shows OK | Front → API via proxy /api/* |
| 3 | design system primitives render with the expected accents | Card / Tag / FlowNode / Button |
| 4 | body uses self-hosted IBM Plex Sans, no Google Fonts | Spec §7 « pas de CDN runtime » |
| 5 | background uses the RTOps deep navy token | Token --bg = #0a0e1a appliqué |
| 6 | no JS console errors on first load | Pas de regression silencieuse côté SPA |
| 7 | API health endpoint returns the expected JSON shape | Contrat API direct |
| 8 | CORS headers are set when the SPA origin asks for them | flask-cors configuré sur FRONT_ORIGIN |
Le rapport HTML (e2e/playwright-report/index.html) inclut, pour chaque test : steps, screenshots sur échec, vidéo sur retry, trace Playwright (timeline réseau + DOM).
Le rapport JUnit XML (e2e/playwright-report/junit.xml) est consommable directement par GitLab CI / GitHub Actions / Jenkins.
5. Tests unitaires backend
make test-api
Attendu : tests/test_health.py::test_health_returns_ok PASSED.
6. Lint & typecheck
make lint
Lance ruff (back), eslint + tsc --noEmit (front). Tout doit passer.
7. Critères de DoD M0 (extraits de tasks/todo.md)
make updémarre les 3 conteneurscurl http://localhost:8080/api/v1/health→{"status":"ok","version":"…"}- Front affiche la home RTOps (manuel + e2e #1, #3, #5)
- Logs JSON sur stdout (manuel #3.4)
- Volumes nommés présents (manuel #3.8)
- Suite Playwright M0 verte
- Rapport HTML disponible dans
e2e/playwright-report/
8. Si quelque chose casse
| Symptôme | Diagnostic |
|---|---|
make up plante en build du back |
Probablement un download uv lent ; relancer ou make rebuild |
| API réponse 502 via le front | api pas encore healthy ; make logs api |
Page blanche, console : Failed to load module … |
Le bundle Vite n'a pas été produit ; make rebuild |
| Polices custom non chargées (fallback sans-serif visible) | Vérifier que @fontsource/* est bien dans node_modules du build context |
Tests Playwright Timeout … API health card |
API pas joignable depuis le navigateur ; tester curl d'abord |
make volumes ne montre rien |
Vérifier que la stack est make up. Sous Podman rootless, les volumes vivent dans ~/.local/share/containers/storage/volumes/. |
make engine annonce le mauvais moteur |
Override : make up ENGINE=docker COMPOSE="docker compose" ou inverse pour podman. |
podman compose indisponible mais podman-compose oui |
Le Makefile fallback automatiquement, ou force-le : COMPOSE=podman-compose make up. |
9. Pièges connus (validés sur podman 5.x / Fedora 43)
- Short-name resolution sous Podman : si tu remplaces une image par son nom court (
postgres:16-alpine), Podman échoue avecshort-name resolution enforced but cannot prompt without a TTY. Toujours utiliserdocker.io/library/<image>:<tag>(Docker accepte le préfixe transparente). - Premier
make up: compte ~3 min pour téléchargerpostgres:16-alpine+ builder les images custom. Les builds suivants sont quasi instantanés grâce au cache. make inspect-healthmontre(no-healthcheck)malgré le Dockerfile : podman-compose 1.x ne propage pas les healthchecks du Dockerfile. Le projet redéclare les healthchecks dansdocker-compose.ymlpour cette raison.apireste enstarting~15 s avant de basculer healthy : c'est lestart_period: 10sdu healthcheck + 1 round de polling. Normal.- Volumes Podman rootless :
~/.local/share/containers/storage/volumes/au lieu de/var/lib/docker/volumes/.make volumesliste les bons volumes peu importe l'engine.
10. Teardown
make down # garde les volumes
make clean # supprime aussi les volumes (DESTRUCTEUR)