Files
Metamorph/tasks/testing-m5.md
Knacky a559823386 test(m5): playwright spec + docs (CHANGELOG, README, lessons, testing-m5)
- 4 Playwright tests: API CRUD round-trip, scenario reorder via PUT, SPA
  list + opsec filter, SPA scenario list rendering with ordered tests.
- afterAll restores the stable admin (admin@metamorph.local) per the
  test_admin memory rule.
- CHANGELOG M5 section + Fixed subsections for the LogRecord 'name'
  collision and the React `currentTarget` vs `target` quirk.
- README status bumps to M0-M5.
- tasks/lessons.md captures the new patterns (sentinel pattern for
  partial-update, FK ordering in /diag/reset, dnd-kit stable IDs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:57:51 +02:00

5.8 KiB

type, milestone, date, project
type milestone date project
testing M5 2026-05-12 Metamorph

Testing M5 — Templates : tests unitaires & scénarios

1. Lancement de la stack

make clean
make up
make migrate
make seed-mitre   # tag picker needs the catalogue

L'admin stable admin@metamorph.local / AdminPass1234! est restauré automatiquement par le hook afterAll du spec e2e M5 — mais la 1ʳᵉ fois, bootstrappe-le via /setup ou laisse les tests faire le travail.

2. Tests automatisés

make test-api    # 77 tests pytest dont 19 M5 (CRUD, perm, mitre tags, reorder)
make e2e         # 38 tests Playwright dont 4 M5 (API CRUD + scenario reorder + SPA list/filter)

Rapport HTML : e2e/playwright-report/. JUnit : e2e/playwright-report/junit.xml.

3. Smoke navigateur

Pré-requis

  • Stack make up + admin loggé.
  • MITRE seedé (vérifier via /mitre).

3.1 Catalogue de tests (/admin/tests)

  1. Cliquer Tests dans la nav admin → page chargée.
  2. Cliquer + New test → modal s'ouvre avec :
    • Champs : Name, Description, Objective, Procedure (markdown), Prerequisites, Red expected, Blue expected, OPSEC, Free tags, Expected IOCs.
    • Sous-section MITRE ATT&CK tags : matrice complète, mêmes interactions que /mitre.
  3. Remplir au minimum Name=phish-link, OPSEC=low, ajouter 2 tags MITRE (ex. TA0001 + T1566) → Create → carte apparaît dans la liste avec chips OPSEC + MITRE.
  4. Cliquer Edit sur la carte → modal pré-remplie, modifier OPSEC à highSave → la card est repeinte avec l'accent rouge OPSEC.
  5. Filtres en haut :
    • Search (full-text q sur nom/description)
    • Tactic external_id (ex. TA0001)
    • OPSEC (select : —all— / low / medium / high)
    • Free tag (mot-clé libre)
  6. Cliquer Delete sur une carte → confirm popup → la card disparaît (soft-delete : visible via ?include_deleted=true côté API).

3.2 Catalogue de scénarios (/admin/scenarios)

  1. Cliquer Scenarios dans la nav admin.
  2. + New scenario → modal.
    • Champs Name + Description.
    • Catalogue picker en bas : champ de recherche + liste des tests dispos (max 50).
  3. Cliquer 3 tests dans le catalogue → ils s'empilent dans la liste ordonnée avec leurs indices 01/02/03.
  4. Drag-and-drop : empoigner la poignée à gauche d'une ligne et glisser vers le haut/bas → la liste se réordonne. La grille met à jour les indices au relâchement.
  5. Save → carte apparaît avec un Tag « N tests » + l'aperçu des 4 premiers tests dans l'ordre choisi.
  6. Re-ouvrir Edit → l'ordre est persisté côté serveur (vérifie le numéro 01, 02, 03 dans la modal).
  7. Supprimer un test_template dont un scénario dépend (via /admin/tests) → la card scénario marque le test en rose dans le résumé (test_template_deleted: true).

3.3 Permissions

  1. Inviter Bob via Admin > Invitations sans groupe → Bob peut se logger mais reçoit 403 sur /api/v1/test-templates.
  2. Lui attacher un groupe avec seulement test_template.read → Bob voit /admin/tests... non, l'UI gate sur is_admin. La perm seule donne l'accès API ; l'UI ne l'expose pas pour les non-admins (par design M5).
  3. Bob tente POST /api/v1/test-templates403 (manque test_template.create).

4. Smoke API

4.1 Login admin

ACCESS=$(curl -sX POST http://localhost:8080/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@metamorph.local","password":"AdminPass1234!"}' | jq -r .access_token)

4.2 Créer un test taggué MITRE

curl -sX POST http://localhost:8080/api/v1/test-templates \
  -H "Authorization: Bearer $ACCESS" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "lsass-dump",
    "opsec_level": "high",
    "tags": ["creds"],
    "mitre_tags": [
      {"kind":"technique","external_id":"T1003"},
      {"kind":"subtechnique","external_id":"T1003.001"}
    ]
  }' | jq

4.3 Créer un scénario ordonné

# Suppose 3 ids: $A $B $C
curl -sX POST http://localhost:8080/api/v1/scenario-templates \
  -H "Authorization: Bearer $ACCESS" \
  -H 'Content-Type: application/json' \
  -d "{\"name\":\"chained\",\"test_template_ids\":[\"$A\",\"$B\",\"$C\"]}" | jq

# Reorder (full replace)
curl -sX PUT http://localhost:8080/api/v1/scenario-templates/<scn_id>/tests \
  -H "Authorization: Bearer $ACCESS" \
  -H 'Content-Type: application/json' \
  -d "{\"test_template_ids\":[\"$C\",\"$A\",\"$B\"]}" | jq

4.4 Filtre par tactic

curl -s "http://localhost:8080/api/v1/test-templates?tactic=TA0006" \
  -H "Authorization: Bearer $ACCESS" | jq '.items[].name'

5. Points de contrôle critiques

  • POST /test-templates rejette MITRE inconnu avec 400 unknown_mitre_tag.
  • POST /test-templates rejette opsec hors low/medium/high.
  • PUT /test-templates/{id} partial keeps unset fields.
  • PUT /test-templates/{id} avec mitre_tags remplace la collection (pas d'append).
  • DELETE /test-templates/{id} soft-delete (visible avec ?include_deleted=true).
  • POST /scenario-templates rejette test_template inconnu ou soft-deleted.
  • PUT /scenario-templates/{id}/tests rewrite atomique (delete + re-insert, contrainte UNIQUE(position) honorée).
  • Un test soft-deleted après linking reste référencé : test_template_deleted: true sur le scénario.
  • Filtres list: q, tactic, technique, subtechnique, opsec, tag cumulatifs.
  • Perm gating : test_template.{read,create,update,delete} + scenario_template.{read,create,update,delete}.
  • /diag/reset truncate les 4 nouvelles tables (scenario_template_tests, scenario_templates, test_template_mitre_tags, test_templates) avant les tables MITRE.
  • UI : drag-and-drop @dnd-kit/sortable réordonne la liste, save persistant.