mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 12:29:36 +01:00
docs: manuelle Testfälle für Inventory und Production BC ergänzen
Testfälle aus allen GitHub-Issues mit Status 'Done' abgeleitet: - TC-SL (1-15): Lagerorte - TC-STK (1-5): Bestandspositionen - TC-BATCH (1-10): Chargen - TC-REC (1-22): Rezepte - TC-CYCLE (1-3): Zyklus-Erkennung - TC-BAT (1-4): Chargenplanung - TC-INV-CROSS/TC-PROD-CROSS: Übergreifende Tests
This commit is contained in:
parent
e8cbb948b7
commit
8c042925eb
1 changed files with 349 additions and 1 deletions
|
|
@ -305,6 +305,346 @@ POST /api/customers/{b2c-id}/frame-contract
|
|||
|
||||
---
|
||||
|
||||
# Manuelle Testfälle – Inventory BC
|
||||
|
||||
## Kontext
|
||||
Der Inventory Bounded Context umfasst zwei Aggregate:
|
||||
- **StorageLocation** – Lagerorte mit Typ und optionalem Temperaturbereich
|
||||
- **Stock** – Bestandspositionen mit chargengenauer Führung (Batches)
|
||||
|
||||
Alle Schreiboperationen erfordern `INVENTORY_WRITE`-Permission. API-Basis: `/api/inventory/`
|
||||
|
||||
---
|
||||
|
||||
## TC-SL: Lagerorte (GitHub #1, #2, #3)
|
||||
|
||||
### TC-SL-01: Lagerort erstellen – Pflichtfelder (Happy Path)
|
||||
1. `POST /api/inventory/storage-locations`
|
||||
2. Body: `name: "Kühlraum 1"`, `storageType: "COLD_ROOM"` → ohne Temperaturbereich
|
||||
- [ ] **Erwartung:** 201 Created; Lagerort hat ID, Status aktiv
|
||||
|
||||
### TC-SL-02: Lagerort erstellen – mit Temperaturbereich
|
||||
1. `POST /api/inventory/storage-locations`
|
||||
2. Body: `name: "Tiefkühllager"`, `storageType: "FREEZER"`, `minTemperature: -25`, `maxTemperature: -18`
|
||||
- [ ] **Erwartung:** 201 Created; Temperaturbereich korrekt gespeichert
|
||||
|
||||
### TC-SL-03: Lagerort erstellen – ungültiger Temperaturbereich (min >= max)
|
||||
1. Body: `name: "Fehler-Lager"`, `storageType: "COLD_ROOM"`, `minTemperature: 10`, `maxTemperature: 5`
|
||||
- [ ] **Erwartung:** 400 Bad Request; Validierungsfehler
|
||||
|
||||
### TC-SL-04: Lagerort erstellen – Temperatur außerhalb Grenzen
|
||||
1. Body: `minTemperature: -60`, `maxTemperature: 90` (Grenzen: -50 bis +80)
|
||||
- [ ] **Erwartung:** 400 Bad Request
|
||||
|
||||
### TC-SL-05: Doppelter Name wird abgelehnt
|
||||
1. Lagerort `Kühlraum 1` erneut anlegen (Name existiert bereits)
|
||||
- [ ] **Erwartung:** 409 Conflict; spezifische Fehlermeldung
|
||||
|
||||
### TC-SL-06: Leerer Name wird abgelehnt
|
||||
1. Body: `name: ""`, `storageType: "COLD_ROOM"`
|
||||
- [ ] **Erwartung:** 400 Bad Request
|
||||
|
||||
### TC-SL-07: Lagerort bearbeiten – Name und Temperatur ändern
|
||||
1. `PUT /api/inventory/storage-locations/{id}`
|
||||
2. Name auf `Kühlraum A` ändern, Temperaturbereich anpassen
|
||||
- [ ] **Erwartung:** 200 OK; Änderungen gespeichert
|
||||
- [ ] **Erwartung:** StorageType ist unverändert (immutable)
|
||||
|
||||
### TC-SL-08: Lagerort bearbeiten – Unique-Check bei Namensänderung
|
||||
1. Lagerort umbenennen auf einen bereits existierenden Namen
|
||||
- [ ] **Erwartung:** 409 Conflict
|
||||
|
||||
### TC-SL-09: Lagerort deaktivieren (ohne Bestand)
|
||||
1. `PATCH /api/inventory/storage-locations/{id}/deactivate` (Lagerort ohne Stock)
|
||||
- [ ] **Erwartung:** 200 OK; Status inaktiv
|
||||
|
||||
### TC-SL-10: Lagerort deaktivieren (mit Bestand) schlägt fehl
|
||||
1. Lagerort hat zugeordneten Stock → Deaktivieren versuchen
|
||||
- [ ] **Erwartung:** 400/409; Fehlermeldung – Bestand existiert
|
||||
|
||||
### TC-SL-11: Lagerort aktivieren
|
||||
1. Deaktivierten Lagerort reaktivieren
|
||||
- [ ] **Erwartung:** 200 OK; Status aktiv
|
||||
|
||||
### TC-SL-12: Doppelte Deaktivierung/Aktivierung
|
||||
1. Bereits inaktiven Lagerort erneut deaktivieren
|
||||
- [ ] **Erwartung:** Spezifischer Fehler (bereits inaktiv)
|
||||
2. Bereits aktiven Lagerort erneut aktivieren
|
||||
- [ ] **Erwartung:** Spezifischer Fehler (bereits aktiv)
|
||||
|
||||
### TC-SL-13: Lagerorte auflisten
|
||||
1. `GET /api/inventory/storage-locations`
|
||||
- [ ] **Erwartung:** Liste aller Lagerorte mit id, name, storageType, temperatureRange, active
|
||||
|
||||
### TC-SL-14: Lagerorte filtern nach Typ
|
||||
1. `GET /api/inventory/storage-locations?storageType=COLD_ROOM`
|
||||
- [ ] **Erwartung:** Nur Kühlraum-Lagerorte
|
||||
|
||||
### TC-SL-15: Lagerorte filtern nach Status
|
||||
1. `GET /api/inventory/storage-locations?active=true`
|
||||
- [ ] **Erwartung:** Nur aktive Lagerorte
|
||||
|
||||
---
|
||||
|
||||
## TC-STK: Bestandspositionen (GitHub #4)
|
||||
|
||||
### TC-STK-01: Bestandsposition erstellen (Happy Path)
|
||||
1. `POST /api/inventory/stocks`
|
||||
2. Body: `articleId: "{existierendeId}"`, `storageLocationId: "{existierendeId}"`
|
||||
- [ ] **Erwartung:** 201 Created; Stock angelegt
|
||||
|
||||
### TC-STK-02: Bestandsposition erstellen – mit MinimumLevel und MinimumShelfLife
|
||||
1. Body: `articleId`, `storageLocationId`, `minimumLevel: 10`, `minimumShelfLife: 5`
|
||||
- [ ] **Erwartung:** 201 Created; Werte korrekt gespeichert
|
||||
|
||||
### TC-STK-03: Doppelte Bestandsposition (gleicher Artikel + Lagerort)
|
||||
1. Gleiche Kombination `articleId + storageLocationId` erneut anlegen
|
||||
- [ ] **Erwartung:** 409 Conflict
|
||||
|
||||
### TC-STK-04: Bestandsposition – MinimumLevel negativ
|
||||
1. Body: `minimumLevel: -1`
|
||||
- [ ] **Erwartung:** 400 Bad Request
|
||||
|
||||
### TC-STK-05: Bestandsposition – MinimumShelfLife 0 oder negativ
|
||||
1. Body: `minimumShelfLife: 0`
|
||||
- [ ] **Erwartung:** 400 Bad Request
|
||||
|
||||
---
|
||||
|
||||
## TC-BATCH: Chargen einbuchen/entnehmen/sperren (GitHub #5, #6, #7)
|
||||
|
||||
### TC-BATCH-01: Charge einbuchen (Happy Path)
|
||||
1. `POST /api/inventory/stocks/{stockId}/batches`
|
||||
2. Body: `batchId: "CH-001"`, `batchType: "PURCHASED"`, `quantity: 50`, `expiryDate: "2026-06-01"`
|
||||
- [ ] **Erwartung:** 201 Created; Status AVAILABLE; receivedAt automatisch gesetzt
|
||||
|
||||
### TC-BATCH-02: Charge einbuchen – Menge 0 oder negativ
|
||||
1. Body: `quantity: 0`
|
||||
- [ ] **Erwartung:** 400 Bad Request
|
||||
2. Body: `quantity: -5`
|
||||
- [ ] **Erwartung:** 400 Bad Request
|
||||
|
||||
### TC-BATCH-03: Doppelte BatchReference im selben Stock
|
||||
1. Erneut `batchId: "CH-001"`, `batchType: "PURCHASED"` einbuchen
|
||||
- [ ] **Erwartung:** 400/409; Duplikat abgelehnt
|
||||
|
||||
### TC-BATCH-04: Charge teilweise entnehmen
|
||||
1. `POST /api/inventory/stocks/{stockId}/batches/{batchId}/remove`
|
||||
2. Body: `quantity: 20` (von 50 verfügbar)
|
||||
- [ ] **Erwartung:** 200 OK; Restmenge = 30
|
||||
|
||||
### TC-BATCH-05: Charge vollständig entnehmen
|
||||
1. Restmenge (30) komplett entnehmen
|
||||
- [ ] **Erwartung:** 200 OK; Charge wird entfernt
|
||||
|
||||
### TC-BATCH-06: Entnahme übersteigt Bestand
|
||||
1. Mehr entnehmen als verfügbar (z.B. 100 von 50)
|
||||
- [ ] **Erwartung:** 400; NegativeStockNotAllowed
|
||||
|
||||
### TC-BATCH-07: Charge sperren
|
||||
1. `POST /api/inventory/stocks/{stockId}/batches/{batchId}/block`
|
||||
2. Body: `reason: "Qualitätsprüfung ausstehend"`
|
||||
- [ ] **Erwartung:** 200 OK; Status wechselt auf BLOCKED; Grund dokumentiert
|
||||
|
||||
### TC-BATCH-08: Gesperrte Charge kann nicht entnommen werden
|
||||
1. Entnahme aus BLOCKED-Charge versuchen
|
||||
- [ ] **Erwartung:** 400; Entnahme verweigert
|
||||
|
||||
### TC-BATCH-09: Charge entsperren
|
||||
1. `POST /api/inventory/stocks/{stockId}/batches/{batchId}/unblock`
|
||||
- [ ] **Erwartung:** 200 OK; Status zurück auf AVAILABLE (oder EXPIRING_SOON)
|
||||
|
||||
### TC-BATCH-10: Doppeltes Sperren/Entsperren
|
||||
1. Bereits gesperrte Charge erneut sperren
|
||||
- [ ] **Erwartung:** Spezifischer Fehler
|
||||
2. Bereits freigegebene Charge erneut entsperren
|
||||
- [ ] **Erwartung:** Spezifischer Fehler
|
||||
|
||||
---
|
||||
|
||||
# Manuelle Testfälle – Production BC
|
||||
|
||||
## Kontext
|
||||
Der Production Bounded Context umfasst:
|
||||
- **Recipe** – Rezeptverwaltung mit Zutaten, Produktionsschritten, Status-Lifecycle
|
||||
- **Batch** – Chargenplanung und -produktion
|
||||
- **Quantity** – Value Object mit Catch-Weight-Unterstützung
|
||||
|
||||
API-Basis: `/api/recipes` und `/api/batches`
|
||||
|
||||
---
|
||||
|
||||
## TC-REC: Rezepte (GitHub #26, #27, #28, #29, #30, #31)
|
||||
|
||||
### TC-REC-01: Rezept erstellen (Happy Path)
|
||||
1. `POST /api/recipes`
|
||||
2. Body: `name: "Bratwurst Classic"`, `version: 1`, `recipeType: "FINISHED_PRODUCT"`, `yieldPercentage: 85`, `shelfLifeDays: 14`, `outputQuantity: {amount: 10, uom: "KILOGRAM"}`
|
||||
- [ ] **Erwartung:** 201 Created; Status = DRAFT
|
||||
|
||||
### TC-REC-02: Rezept erstellen – YieldPercentage Grenzen
|
||||
1. Body: `yieldPercentage: 0`
|
||||
- [ ] **Erwartung:** 400; InvalidYieldPercentage
|
||||
2. Body: `yieldPercentage: 201`
|
||||
- [ ] **Erwartung:** 400; InvalidYieldPercentage
|
||||
|
||||
### TC-REC-03: Rezept erstellen – ShelfLifeDays 0 bei FINISHED_PRODUCT
|
||||
1. Body: `recipeType: "FINISHED_PRODUCT"`, `shelfLifeDays: 0`
|
||||
- [ ] **Erwartung:** 400; InvalidShelfLife
|
||||
|
||||
### TC-REC-04: Doppelter Name + Version wird abgelehnt
|
||||
1. Erneut `name: "Bratwurst Classic"`, `version: 1` anlegen
|
||||
- [ ] **Erwartung:** 409 Conflict
|
||||
|
||||
### TC-REC-05: Zutat zu DRAFT-Rezept hinzufügen (Happy Path)
|
||||
1. `POST /api/recipes/{id}/ingredients`
|
||||
2. Body: `position: 1`, `articleId: "{id}"`, `quantity: {amount: 5, uom: "KILOGRAM"}`, `substitutable: false`
|
||||
- [ ] **Erwartung:** 200 OK; Zutat im Rezept
|
||||
|
||||
### TC-REC-06: Zweite Zutat hinzufügen
|
||||
1. Body: `position: 2`, andere ArticleId, Menge
|
||||
- [ ] **Erwartung:** 200 OK; 2 Zutaten im Rezept
|
||||
|
||||
### TC-REC-07: Zutat mit doppelter Position
|
||||
1. Body: `position: 1` (bereits vergeben)
|
||||
- [ ] **Erwartung:** 400; DuplicatePosition
|
||||
|
||||
### TC-REC-08: Zutat zu ACTIVE-Rezept hinzufügen
|
||||
1. Rezept aktivieren, dann Zutat hinzufügen versuchen
|
||||
- [ ] **Erwartung:** 400; NotInDraftStatus
|
||||
|
||||
### TC-REC-09: Zutat mit SubRecipeId (Zwischen-Rezept)
|
||||
1. INTERMEDIATE-Rezept als SubRecipe anlegen
|
||||
2. Zutat mit `subRecipeId` zum Hauptrezept hinzufügen
|
||||
- [ ] **Erwartung:** 200 OK; Zutat referenziert Sub-Rezept
|
||||
|
||||
### TC-REC-10: Zutat entfernen
|
||||
1. `DELETE /api/recipes/{id}/ingredients/{ingredientId}`
|
||||
- [ ] **Erwartung:** 200 OK; Zutat entfernt, Position-Lücke erlaubt
|
||||
|
||||
### TC-REC-11: Produktionsschritt hinzufügen (Happy Path)
|
||||
1. `POST /api/recipes/{id}/steps`
|
||||
2. Body: `stepNumber: 1`, `description: "Fleisch wolfen"`, `durationMinutes: 15`, `temperatureCelsius: 4`
|
||||
- [ ] **Erwartung:** 200 OK; Schritt im Rezept
|
||||
|
||||
### TC-REC-12: Produktionsschritt – leere Beschreibung
|
||||
1. Body: `stepNumber: 2`, `description: ""`
|
||||
- [ ] **Erwartung:** 400; Validierungsfehler
|
||||
|
||||
### TC-REC-13: Produktionsschritt zu ACTIVE-Rezept
|
||||
1. Schritt zu aktiviertem Rezept hinzufügen versuchen
|
||||
- [ ] **Erwartung:** 400; NotInDraftStatus
|
||||
|
||||
### TC-REC-14: Produktionsschritt entfernen
|
||||
1. `DELETE /api/recipes/{id}/steps/{stepNumber}`
|
||||
- [ ] **Erwartung:** 200 OK; Schritt entfernt, keine automatische Umnummerierung
|
||||
|
||||
### TC-REC-15: Rezept aktivieren (Happy Path)
|
||||
1. Rezept mit mindestens einer Zutat → `POST /api/recipes/{id}/activate`
|
||||
- [ ] **Erwartung:** 200 OK; Status = ACTIVE
|
||||
|
||||
### TC-REC-16: Rezept aktivieren – ohne Zutaten
|
||||
1. Leeres DRAFT-Rezept aktivieren
|
||||
- [ ] **Erwartung:** 400; NoIngredients
|
||||
|
||||
### TC-REC-17: Rezept aktivieren – bereits ACTIVE
|
||||
1. Aktives Rezept erneut aktivieren
|
||||
- [ ] **Erwartung:** 400; InvalidStatusTransition
|
||||
|
||||
### TC-REC-18: Rezept archivieren (Happy Path)
|
||||
1. `POST /api/recipes/{id}/archive` (Rezept ist ACTIVE)
|
||||
- [ ] **Erwartung:** 200 OK; Status = ARCHIVED
|
||||
|
||||
### TC-REC-19: Rezept archivieren – aus DRAFT
|
||||
1. DRAFT-Rezept archivieren versuchen
|
||||
- [ ] **Erwartung:** 400; InvalidStatusTransition
|
||||
|
||||
### TC-REC-20: Rezept per ID abfragen
|
||||
1. `GET /api/recipes/{id}`
|
||||
- [ ] **Erwartung:** 200 OK; vollständiges Rezept mit Zutaten und Schritten
|
||||
|
||||
### TC-REC-21: Rezept nicht gefunden
|
||||
1. `GET /api/recipes/{nicht-existierende-id}`
|
||||
- [ ] **Erwartung:** 404 Not Found
|
||||
|
||||
### TC-REC-22: Rezepte nach Status filtern
|
||||
1. `GET /api/recipes?status=ACTIVE`
|
||||
- [ ] **Erwartung:** Nur aktive Rezepte; Liste enthält Zutaten-Count aber nicht volle Zutaten
|
||||
|
||||
---
|
||||
|
||||
## TC-CYCLE: Zyklus-Erkennung (GitHub #32)
|
||||
|
||||
### TC-CYCLE-01: Lineare Verschachtelung erlaubt (A → B → C)
|
||||
1. Rezept C (ACTIVE, mit Zutat)
|
||||
2. Rezept B (DRAFT) → Zutat mit `subRecipeId: C`
|
||||
3. Rezept A (DRAFT) → Zutat mit `subRecipeId: B`
|
||||
- [ ] **Erwartung:** Alle Zuweisungen erfolgreich; keine Fehler
|
||||
|
||||
### TC-CYCLE-02: Direkte Zirkularität (A → B → A)
|
||||
1. Rezept A enthält Zutat mit `subRecipeId: B`
|
||||
2. Rezept B: Zutat mit `subRecipeId: A` hinzufügen
|
||||
- [ ] **Erwartung:** 400; CyclicDependencyDetected mit Pfad-Angabe
|
||||
|
||||
### TC-CYCLE-03: Indirekte Zirkularität (A → B → C → A)
|
||||
1. A → B → C existiert
|
||||
2. Zutat mit `subRecipeId: A` zu Rezept C hinzufügen
|
||||
- [ ] **Erwartung:** 400; CyclicDependencyDetected mit Pfad-Angabe
|
||||
|
||||
---
|
||||
|
||||
## TC-BAT: Chargenplanung (GitHub #33)
|
||||
|
||||
### TC-BAT-01: Charge planen (Happy Path)
|
||||
1. `POST /api/batches`
|
||||
2. Body: `recipeId: "{id}"`, `plannedQuantity: {amount: 50, uom: "KILOGRAM"}`, `productionDate: "2026-03-01"`, `bestBeforeDate: "2026-03-15"`
|
||||
- [ ] **Erwartung:** 201 Created; Status = PLANNED; BatchNumber automatisch generiert (Format `P-YYYY-MM-DD-XXX`)
|
||||
|
||||
### TC-BAT-02: Charge planen – PlannedQuantity 0
|
||||
1. Body: `plannedQuantity: {amount: 0, uom: "KILOGRAM"}`
|
||||
- [ ] **Erwartung:** 400; Menge muss positiv sein
|
||||
|
||||
### TC-BAT-03: Charge planen – BestBeforeDate vor ProductionDate
|
||||
1. Body: `productionDate: "2026-03-15"`, `bestBeforeDate: "2026-03-01"`
|
||||
- [ ] **Erwartung:** 400; BestBeforeDate muss nach ProductionDate liegen
|
||||
|
||||
### TC-BAT-04: BatchNumber-Format prüfen
|
||||
1. Mehrere Chargen am selben Tag planen
|
||||
- [ ] **Erwartung:** BatchNumbers haben Format `P-YYYY-MM-DD-XXX` mit aufsteigender Sequenz
|
||||
|
||||
---
|
||||
|
||||
## TC-INV-CROSS: Übergreifende Tests – Inventory
|
||||
|
||||
### TC-INV-CROSS-01: Sequenz – Kompletter Lagerort-Workflow
|
||||
1. Lagerort erstellen → Name ändern → Temperatur anpassen → deaktivieren (ohne Stock) → reaktivieren
|
||||
- [ ] **Erwartung:** Alle Schritte funktionieren ohne Datenverlust
|
||||
|
||||
### TC-INV-CROSS-02: Sequenz – Kompletter Chargen-Workflow
|
||||
1. Lagerort erstellen → Stock anlegen → Charge einbuchen → Teilentnahme → Charge sperren → Charge entsperren → Rest entnehmen
|
||||
- [ ] **Erwartung:** Konsistenz über alle Schritte; Mengen korrekt
|
||||
|
||||
### TC-INV-CROSS-03: Lagerort mit Bestand kann nicht deaktiviert werden
|
||||
1. Lagerort mit Stock + Charge → Deaktivierung versuchen
|
||||
- [ ] **Erwartung:** Fehler; Bestand muss erst entfernt werden
|
||||
|
||||
---
|
||||
|
||||
## TC-PROD-CROSS: Übergreifende Tests – Production
|
||||
|
||||
### TC-PROD-CROSS-01: Sequenz – Rezept-Lifecycle komplett
|
||||
1. Rezept erstellen (DRAFT) → 2 Zutaten hinzufügen → 2 Schritte hinzufügen → 1 Zutat entfernen → aktivieren → archivieren
|
||||
- [ ] **Erwartung:** Konsistenz über alle Schritte
|
||||
|
||||
### TC-PROD-CROSS-02: Sequenz – Rezept + Chargenplanung
|
||||
1. Rezept erstellen → Zutaten → aktivieren → Charge planen mit diesem Rezept
|
||||
- [ ] **Erwartung:** Charge referenziert Rezept korrekt
|
||||
|
||||
### TC-PROD-CROSS-03: Verschachteltes Rezept vollständig
|
||||
1. INTERMEDIATE-Rezept erstellen + aktivieren → FINISHED_PRODUCT-Rezept mit SubRecipe-Zutat → aktivieren → Charge planen
|
||||
- [ ] **Erwartung:** Alle Abhängigkeiten korrekt aufgelöst
|
||||
|
||||
---
|
||||
|
||||
## Verifikation / Testdurchführung
|
||||
|
||||
```bash
|
||||
|
|
@ -315,7 +655,7 @@ POST /api/customers/{b2c-id}/frame-contract
|
|||
cd frontend/apps/cli && pnpm dev
|
||||
|
||||
# Backend-Logs beobachten (Fehler sichtbar machen)
|
||||
# Direkte API-Tests (für TC-B2B-02, TC-AUTH)
|
||||
# Direkte API-Tests (für TC-B2B-02, TC-AUTH, Inventory, Production)
|
||||
curl -X POST http://localhost:8080/api/... -H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
|
|
@ -327,3 +667,11 @@ curl -X POST http://localhost:8080/api/... -H "Authorization: Bearer <token>"
|
|||
- [ ] TC-B2B (1-2) durchgeführt
|
||||
- [ ] TC-AUTH (1-3) durchgeführt
|
||||
- [ ] TC-CROSS (1-5) durchgeführt
|
||||
- [ ] Alle TC-SL (1-15) durchgeführt
|
||||
- [ ] Alle TC-STK (1-5) durchgeführt
|
||||
- [ ] Alle TC-BATCH (1-10) durchgeführt
|
||||
- [ ] Alle TC-REC (1-22) durchgeführt
|
||||
- [ ] TC-CYCLE (1-3) durchgeführt
|
||||
- [ ] TC-BAT (1-4) durchgeführt
|
||||
- [ ] TC-INV-CROSS (1-3) durchgeführt
|
||||
- [ ] TC-PROD-CROSS (1-3) durchgeführt
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue