--- type: testing project: Metamorph milestone: M1 date: "2026-05-10" --- # Comment tester M1 (schéma DB & migrations) > Procédure de validation manuelle + automatisée pour M1 : SQLAlchemy 2 + Alembic + 26 tables. Toutes les commandes se lancent depuis la racine du repo. ## 0. Prérequis Voir `tasks/testing-m0.md §0` (Docker ou Podman, Make, Node 20+, etc.). Aucune dépendance Python locale n'est requise — pytest tourne dans un container éphémère bâti depuis le stage `test` du Dockerfile backend. ## 1. Bootstrap (si la stack n'est pas déjà up) ```bash make env # crée .env si absent make up # build + start de la stack (api / db / front) make inspect-health # attends que les 3 soient healthy ``` ## 2. Appliquer la migration ```bash make migrate # alembic upgrade head dans le container api make migrate-status # confirme la revision courante = head ``` **Attendu** : ``` INFO [alembic.runtime.migration] Will assume transactional DDL. INFO [alembic.runtime.migration] Running upgrade -> 24765a5014b6, initial schema ``` et `make migrate-status` : ``` 24765a5014b6 (head) --- 24765a5014b6 (head) ``` ## 3. Tests fonctionnels manuels ### 3.1 — Liste des tables ```bash make psql # ouvre psql dans le container db \dt # une fois dans psql ``` **Attendu** : **27 lignes** (26 tables métier + `alembic_version`) : ``` detection_levels, evidence_files, group_permissions, groups, invitation_groups, invitations, mission_categories, mission_members, mission_scenarios, mission_test_mitre_tags, mission_tests, missions, mitre_subtechniques, mitre_tactics, mitre_technique_tactics, mitre_techniques, notifications, permissions, refresh_tokens, scenario_template_tests, scenario_templates, settings, test_template_mitre_tags, test_templates, user_groups, users, alembic_version ``` Compter via SQL : ```bash podman exec metamorph-db psql -U metamorph -d metamorph -tAc \ "SELECT count(*) FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'" # 27 ``` ### 3.2 — Contraintes au niveau Postgres ```bash podman exec metamorph-db psql -U metamorph -d metamorph -tAc \ "SELECT contype, count(*) FROM pg_constraint WHERE connamespace = 'public'::regnamespace GROUP BY contype ORDER BY contype" ``` **Attendu** : - `c|9` — CHECK constraints (status valid, opsec_level valid, mitre_kind valid, exactly_one_mitre_fk uniquement sur `test_template_mitre_tags`, …) - `f|32` — Foreign keys *(snapshot `mission_test_mitre_tags` n'a volontairement pas de FK MITRE)* - `p|27` — Primary keys (1 par table) - `u|14` — UNIQUE constraints ### 3.3 — Index partiels (soft delete + unread notifications) ```bash podman exec metamorph-db psql -U metamorph -d metamorph -c \ "SELECT indexname FROM pg_indexes WHERE schemaname='public' AND indexdef ILIKE '%WHERE%' ORDER BY 1" ``` **Attendu** (12 indexes) : - `ix_evidence_files_active`, `ix_groups_active`, `ix_missions_active`, `ix_mission_categories_active`, `ix_mission_scenarios_active`, `ix_mission_tests_active`, `ix_scenario_templates_active`, `ix_test_templates_active`, `ix_users_active` — soft-delete partiels (9) - `ix_notifications_user_unread` — `WHERE read_at IS NULL` - `uq_users_email_active`, `uq_groups_name_active` — uniques scopés aux lignes actives ### 3.4 — Test négatif d'un CHECK constraint (`exactly_one_mitre_fk`) ```sql -- Dans psql, doit échouer : INSERT INTO test_templates (id, name, opsec_level) VALUES (gen_random_uuid(), 'tmp', 'low'); INSERT INTO mitre_tactics (id, external_id, short_name, name) VALUES (gen_random_uuid(), 'TA0099', 'tmp', 'tmp'); -- (puis tente une insertion avec deux FK MITRE non null — bloqué par CHECK) ``` Couvert automatiquement par `tests/test_schema.py::test_exactly_one_mitre_fk_check_enforced`. ### 3.5 — Migration depuis DB totalement vide ```bash make clean # DESTRUCTEUR — supprime aussi les volumes make up # re-spawn db vierge # attends que db soit healthy make migrate # applique 0001_initial sur DB vide podman exec metamorph-db psql -U metamorph -d metamorph -tAc "SELECT count(*) FROM information_schema.tables WHERE table_schema='public'" # attendu : 27 ``` ## 4. Tests automatisés ### 4.1 — Backend pytest (M1 schema integration) ```bash make test-api ``` **Attendu** : `9 passed in <1s` (1 health M0 + 8 schema M1). Détail des 8 tests M1 : | # | Test | Couvre | |---|---|---| | 1 | `test_all_expected_tables_exist` | Liste exhaustive des 26 tables métier | | 2 | `test_soft_delete_columns_present` | `deleted_at` sur 6 tables | | 3 | `test_standard_timestamp_columns_present` | `created_at`+`updated_at` sur 5 tables | | 4 | `test_partial_index_for_soft_delete` | Index `ix_