test(catalog) : M7 — tests RG-7.01→7.08 + contrat de sérialisation (ERP-215) #167

Merged
tristan merged 2 commits from feat/erp-215-tests-storage into develop 2026-06-30 06:00:08 +00:00
Owner

M7 · ERP-215 (1.6) — Tests RG-7.01→7.08 + capture du contrat JSON

⚠️ MR empilée sur feat/erp-214-storage-export-xlsx (#166) → #165#164#163#162. Merge dans l'ordre 210 → … → 215.

Couvre les règles métier du stockage en PHPUnit et fige le contrat de sérialisation réel (DoD). Structure miroir M6 (tests dédiés par RG).

Reclassement

StorageApiTest (smoke test bundlé introduit en ERP-213) est supprimé et remplacé par les classes canoniques par RG — mirroir exact de la structure produit M6 (ProductCodeUniquenessTest, ProductStatesValidationTest, ProductRBACMatrixTest, ProductSerializationContractTest…). Net sur develop après merge de la pile : pas de doublon.

Tests livrés

  • StorageSerializationContractTest — POST réel puis GET liste + détail ; site/storageType en objets embarqués (pas IRI), states tableau, displayName présent et correct. + RG-7.07 (soft-deleted jamais exposé). DoD régénérable STORAGE_DOD_DUMP=1.
  • StorageUniquenessTest (RG-7.01 / RG-7.08) — 409 doublon triplet actif ; même numéro OK sur autre site ou autre type ; réutilisation OK après soft-delete ; PATCH vers triplet existant → 409.
  • StorageStatesValidationTest (RG-7.04 / RG-7.08) — ≥1 état requis (422), valeur hors enum (422), 1 état valide (201), PATCH états vides → 422.
  • StorageDisplayNameTest (RG-7.05) — displayName = « » sur le JSON réel.
  • StorageRBACMatrixTest — Admin 200/201 ; Bureau/Compta/Commerciale/Usine → 403 (view+manage) ; anonyme → 401.
  • StorageTypeBySiteTest (RG-7.03) — markTestSkipped avec justification.

RG-7.03 — test skippé (décision ERP-213 validée avec toi)

Le concept type↔site a été retiré du modèle en M6 (jointure storage_type_site droppée, StorageType rendu plat). Aucun référentiel à interroger pour lever une 422 → RG-7.03 inimplémentable telle quelle. Le test est conservé skippé pour la traçabilité DoD (à réactiver si la spec réintroduit le lien). À reclarifier côté spec.

Contrat de sérialisation réel (spec § 4.0.bis)

Le fichier docs/specs/M7-stockage/spec-back.md n'existe pas sur le repo — je colle ici le détail réel capturé (STORAGE_DOD_DUMP=1) à reporter dans la spec :

{
    "@context": "/api/contexts/Storage",
    "@id": "/api/storages/103",
    "@type": "Storage",
    "id": 103,
    "site": {
        "@id": "/api/sites/39",
        "@type": "Site",
        "id": 39,
        "name": "Chatellerault",
        "code": "86",
        "street": "14 All. d'Argenson",
        "postalCode": "86100",
        "city": "Châtellerault",
        "color": "#056CF2",
        "createdAt": "2026-06-29T16:42:20+02:00",
        "updatedAt": "2026-06-29T16:42:20+02:00",
        "fullAddress": "14 All. d'Argenson\n86100 Châtellerault"
    },
    "storageType": {
        "@id": "/api/storage_types/353",
        "@type": "StorageType",
        "id": 353,
        "code": "TESTSTO_4188A3677F",
        "label": "Cellule"
    },
    "numero": "NUM_9B1310D131",
    "states": [
        "RECEPTION",
        "TRIAGE"
    ],
    "createdAt": "2026-06-29T17:08:05+02:00",
    "updatedAt": "2026-06-29T17:08:05+02:00",
    "createdBy": "/api/me",
    "updatedBy": "/api/me",
    "displayName": "Cellule NUM_9B1310D131"
}

Vérifications

  • 17 tests stockage (95 assertions, 1 skippé = RG-7.03) verts ; module Catalog complet 139/139 (1 skippé).
  • make test : les échecs résiduels (Commercial/Technique) = flaky JWT connu (différents à chaque run, tous verts en isolation 7/7) ; 1 skippé (RG-7.03).
  • make php-cs-fixer-allow-risky : clean.
  • AuditableEntitiesHaveI18nLabelTest : déjà vert — la clé catalog_storage a été posée dès ERP-212 (le prompt la situait en ERP-219, mais elle était nécessaire au make test de ERP-212).
## M7 · ERP-215 (1.6) — Tests RG-7.01→7.08 + capture du contrat JSON > ⚠️ **MR empilée** sur `feat/erp-214-storage-export-xlsx` (#166) → #165 → #164 → #163 → #162. Merge dans l'ordre 210 → … → 215. Couvre les règles métier du stockage en PHPUnit et fige le contrat de sérialisation réel (DoD). Structure miroir M6 (tests dédiés **par RG**). ### Reclassement `StorageApiTest` (smoke test bundlé introduit en ERP-213) est **supprimé** et remplacé par les classes canoniques par RG — mirroir exact de la structure produit M6 (`ProductCodeUniquenessTest`, `ProductStatesValidationTest`, `ProductRBACMatrixTest`, `ProductSerializationContractTest`…). Net sur `develop` après merge de la pile : pas de doublon. ### Tests livrés - **`StorageSerializationContractTest`** — POST réel puis GET liste + détail ; `site`/`storageType` en **objets embarqués** (pas IRI), `states` tableau, **`displayName`** présent et correct. + RG-7.07 (soft-deleted jamais exposé). DoD régénérable `STORAGE_DOD_DUMP=1`. - **`StorageUniquenessTest`** (RG-7.01 / RG-7.08) — 409 doublon triplet actif ; même numéro OK sur autre site **ou** autre type ; réutilisation OK après soft-delete ; **PATCH** vers triplet existant → 409. - **`StorageStatesValidationTest`** (RG-7.04 / RG-7.08) — ≥1 état requis (422), valeur hors enum (422), 1 état valide (201), **PATCH** états vides → 422. - **`StorageDisplayNameTest`** (RG-7.05) — `displayName` = « <label> <numero> » sur le JSON réel. - **`StorageRBACMatrixTest`** — Admin 200/201 ; Bureau/Compta/Commerciale/Usine → 403 (view+manage) ; anonyme → 401. - **`StorageTypeBySiteTest`** (RG-7.03) — **`markTestSkipped`** avec justification. ### RG-7.03 — test **skippé** (décision ERP-213 validée avec toi) Le concept type↔site a été retiré du modèle en M6 (jointure `storage_type_site` droppée, `StorageType` rendu plat). Aucun référentiel à interroger pour lever une 422 → RG-7.03 inimplémentable telle quelle. Le test est conservé **skippé** pour la traçabilité DoD (à réactiver si la spec réintroduit le lien). **À reclarifier côté spec.** ### Contrat de sérialisation réel (spec § 4.0.bis) Le fichier `docs/specs/M7-stockage/spec-back.md` n'existe pas sur le repo — je colle ici le **détail** réel capturé (`STORAGE_DOD_DUMP=1`) à reporter dans la spec : ```json { "@context": "/api/contexts/Storage", "@id": "/api/storages/103", "@type": "Storage", "id": 103, "site": { "@id": "/api/sites/39", "@type": "Site", "id": 39, "name": "Chatellerault", "code": "86", "street": "14 All. d'Argenson", "postalCode": "86100", "city": "Châtellerault", "color": "#056CF2", "createdAt": "2026-06-29T16:42:20+02:00", "updatedAt": "2026-06-29T16:42:20+02:00", "fullAddress": "14 All. d'Argenson\n86100 Châtellerault" }, "storageType": { "@id": "/api/storage_types/353", "@type": "StorageType", "id": 353, "code": "TESTSTO_4188A3677F", "label": "Cellule" }, "numero": "NUM_9B1310D131", "states": [ "RECEPTION", "TRIAGE" ], "createdAt": "2026-06-29T17:08:05+02:00", "updatedAt": "2026-06-29T17:08:05+02:00", "createdBy": "/api/me", "updatedBy": "/api/me", "displayName": "Cellule NUM_9B1310D131" } ``` ### Vérifications - 17 tests stockage (95 assertions, 1 skippé = RG-7.03) verts ; module Catalog complet **139/139** (1 skippé). - `make test` : les échecs résiduels (Commercial/Technique) = **flaky JWT** connu (différents à chaque run, tous verts en isolation 7/7) ; 1 skippé (RG-7.03). - `make php-cs-fixer-allow-risky` : clean. - `AuditableEntitiesHaveI18nLabelTest` : déjà vert — la clé `catalog_storage` a été posée dès ERP-212 (le prompt la situait en ERP-219, mais elle était nécessaire au `make test` de ERP-212).
tristan added the type/testbackM7-Stockage labels 2026-06-29 15:11:29 +00:00
Author
Owner

Revue de code — M7 Stockages (tests)

🟡 Contrat de sérialisation incompletStorageSerializationContractTest

Le test n'assère que des clés choisies (site.name/code, storageType.label, states, displayName), jamais l'absence des champs non exposés (deletedAt, createdBy, updatedBy) ni le set complet de clés. Une régression qui ajouterait deletedAt/createdBy à storage:read passerait au vert — alors que c'est précisément ce qu'un test « contrat » doit attraper. Ajouter l'assertion d'absence + le set de clés attendu.

🟡 Test soft-delete liste quasi-vacuousStorageSerializationContractTest

testSoftDeletedIsNotExposed ne seede qu'un stockage soft-deleté puis assère null sur une collection devenue vide : impossible de distinguer « le provider exclut le soft-deleté » de « la collection est vide ». RG-7.07 n'est pas réellement exercée côté liste (seul le 404 sur l'item l'est). → Seeder 1 actif + 1 supprimé, asserter présence de l'actif ET absence du supprimé dans la même réponse.

🟡 Couverture validation manquanteStorageStatesValidationTest

Pas de test pour numero (NotBlank / Length 50), ni NotNull sur site/storageType, ni surtout le chemin collectDenormalizationErrors:true (IRI site/storageType invalide → doit remonter 422 + propertyPath, pas 400) — pourtant signalé comme piège RETEX-M1 dans l'entité. La normalisation RG-7.06 (POST " A1 " → stocké "A1") n'est exercée nulle part (tous les fixtures fournissent des numéros déjà propres) → un trim cassé passerait inaperçu et contournerait le contrôle d'unicité.

🟡 RG-7.03 (type dispo sur le site) — markTestSkipped permanent

Choix documenté (référentiel storage_type aplati en M6), mais la matrice DoD liste RG-7.03 comme couverte alors que rien ne l'exerce et que le test reste vert pour toujours. → Reclarifier côté spec : règle abandonnée ou à réintroduire (avec un lien type↔site) ?

### Revue de code — M7 Stockages (tests) **🟡 Contrat de sérialisation incomplet** — `StorageSerializationContractTest` Le test n'assère **que** des clés choisies (site.name/code, storageType.label, states, displayName), jamais l'**absence** des champs non exposés (`deletedAt`, `createdBy`, `updatedBy`) ni le set complet de clés. Une régression qui ajouterait `deletedAt`/`createdBy` à `storage:read` passerait au vert — alors que c'est précisément ce qu'un test « contrat » doit attraper. Ajouter l'assertion d'absence + le set de clés attendu. **🟡 Test soft-delete liste quasi-vacuous** — `StorageSerializationContractTest` `testSoftDeletedIsNotExposed` ne seede qu'un stockage soft-deleté puis assère `null` sur une collection devenue vide : impossible de distinguer « le provider exclut le soft-deleté » de « la collection est vide ». RG-7.07 n'est pas réellement exercée côté liste (seul le 404 sur l'item l'est). → Seeder 1 actif + 1 supprimé, asserter présence de l'actif ET absence du supprimé dans la même réponse. **🟡 Couverture validation manquante** — `StorageStatesValidationTest` Pas de test pour `numero` (NotBlank / Length 50), ni `NotNull` sur `site`/`storageType`, ni surtout le chemin `collectDenormalizationErrors:true` (IRI `site`/`storageType` invalide → doit remonter 422 + propertyPath, pas 400) — pourtant signalé comme piège RETEX-M1 dans l'entité. La normalisation RG-7.06 (POST `" A1 "` → stocké `"A1"`) n'est exercée nulle part (tous les fixtures fournissent des numéros déjà propres) → un trim cassé passerait inaperçu et contournerait le contrôle d'unicité. **🟡 RG-7.03 (type dispo sur le site) — `markTestSkipped` permanent** Choix documenté (référentiel `storage_type` aplati en M6), mais la matrice DoD liste RG-7.03 comme couverte alors que rien ne l'exerce et que le test reste vert pour toujours. → Reclarifier côté spec : règle abandonnée ou à réintroduire (avec un lien type↔site) ?
tristan changed target branch from feat/erp-214-storage-export-xlsx to develop 2026-06-30 06:00:06 +00:00
tristan added 2 commits 2026-06-30 06:00:06 +00:00
- Storage.setStates() renormalise en liste séquentielle (array_values) : un states posté en objet JSON ne peut plus être persisté en JSONB objet (jsonb_array_length → 500). Doublons rejetés en 422 via Assert\Unique.
- PhpSpreadsheetExporter écrit les cellules chaîne en TYPE_STRING explicite : neutralise l'injection de formules/DDE sur toutes les valeurs saisies (corrige aussi Produit/Client/Logistique/Supplier/Provider/Carrier).
- StorageListFilters : source unique de parsing des filtres (?search, ?siteId[], ?storageTypeId, ?state), consommée par le provider ET l'export → fin des divergences (numéro « 0 » coercé à null, param tableau en 400, id non positif).
- Export en streaming (toIterable + clear par lot) au lieu de getResult() : mémoire bornée.
- Tests : doublon/objet states, normalisation trim RG-7.06, 422 relations nulles, absence de deletedAt, soft-delete liste discriminant, neutralisation formule, parité ?search=0, robustesse param tableau ; garde-fou Assert\Unique enregistré.
tristan merged commit fcb6715c1f into develop 2026-06-30 06:00:08 +00:00
Sign in to join this conversation.