132 lines
5.4 KiB
Markdown
132 lines
5.4 KiB
Markdown
# EXPIRATION TRACKING — TESTS.md
|
|
|
|
## Unit Tests
|
|
|
|
### LotService
|
|
|
|
```
|
|
EXP-UT-001: createLot with expiry product and valid date → succeeds
|
|
EXP-UT-002: createLot with expiry product and missing date → throws MissingExpirationDateError (422)
|
|
EXP-UT-003: createLot with past date → throws PastExpirationDateError (422)
|
|
EXP-UT-004: createLot with non-expiry product and no date → succeeds (null expiry allowed)
|
|
EXP-UT-005: allocateLots FEFO order — earliest expiry first
|
|
EXP-UT-006: allocateLots partial — consumes only available quantity from earliest
|
|
EXP-UT-007: allocateLots insufficient → throws InsufficientStockError
|
|
EXP-UT-008: allocateLots across multiple lots (quantity exceeds first lot)
|
|
EXP-UT-009: getAvailableStock excludes expired lots
|
|
EXP-UT-010: getAvailableStock excludes lots with expiration_date = today (near_expiry status)
|
|
EXP-UT-011: updateLot quantity to 0 → still exists (not auto-deleted)
|
|
EXP-UT-012: deleteLot removes lot and logs audit
|
|
```
|
|
|
|
### InventoryService (extended)
|
|
|
|
```
|
|
EXP-UT-020: checkAvailability non-expiry product → existing variant-level sum
|
|
EXP-UT-021: checkAvailability expiry product with valid lots → sum of non-expired quantities
|
|
EXP-UT-022: checkAvailability expiry product with only expired lots → 0 available
|
|
EXP-UT-023: reserve expiry product → FEFO allocation from non-expired lots
|
|
EXP-UT-024: reserve non-expiry product → existing behavior unchanged
|
|
EXP-UT-025: release returns reserved quantity to FEFO lots
|
|
EXP-UT-026: feature flag OFF → delegates to existing variant-level path
|
|
```
|
|
|
|
### Product (expiration field)
|
|
|
|
```
|
|
EXP-UT-030: product.create sets expiration_tracking_enabled to false by default
|
|
EXP-UT-031: product.patch can update expiration_tracking_enabled
|
|
EXP-UT-032: product.findById returns expiration_tracking_enabled
|
|
```
|
|
|
|
## Integration Tests
|
|
|
|
### Database
|
|
|
|
```
|
|
EXP-IT-001: inventory_lots table enforces CHECK expiration_date >= CURRENT_DATE
|
|
EXP-IT-002: inventory_lots FK to catalog_variants ON DELETE CASCADE
|
|
EXP-IT-003: inventory_movements allows new operation types
|
|
EXP-IT-004: catalog_products expiration_tracking_enabled defaults to false
|
|
```
|
|
|
|
### API Routes
|
|
|
|
```
|
|
EXP-IT-010: POST /inventory/lots with valid body → 201 + lot object
|
|
EXP-IT-011: POST /inventory/lots missing expiration_date on expiry product → 422
|
|
EXP-IT-012: POST /inventory/lots with past expiration_date → 422
|
|
EXP-IT-013: PATCH /inventory/lots/:id updates quantity
|
|
EXP-IT-014: DELETE /inventory/lots/:id → 200 + lot removed
|
|
EXP-IT-015: GET /inventory/lots?variant_id=X → returns lots for variant
|
|
EXP-IT-016: GET /inventory/lots?filter=expiring → returns only near-expiry lots
|
|
EXP-IT-017: GET /inventory/lots?filter=expired → returns expired lots
|
|
EXP-IT-018: GET /inventory/lots?filter=no-expiry → returns null-expiry lots
|
|
EXP-IT-019: All lot endpoints require admin auth → 401 without session
|
|
```
|
|
|
|
### Full Flow
|
|
|
|
```
|
|
EXP-IT-030: Enable expiry on product → create lots → check availability → available
|
|
EXP-IT-031: Enable expiry on product → no lots created → availability = 0
|
|
EXP-IT-032: Reserve from FEFO lots → earliest expires first → correct lot decremented
|
|
EXP-IT-033: Expired lot never contributes to availability
|
|
EXP-IT-034: After feature flag OFF → reverts to variant-level behavior
|
|
```
|
|
|
|
## Admin Tests
|
|
|
|
```
|
|
EXP-ADM-UT-001: ProductEditor — enabling expiry toggle shows lot management UI
|
|
EXP-ADM-UT-002: ProductEditor — disabling expiry hides lot management UI
|
|
EXP-ADM-UT-003: Lot table — correct VALID/NEAR_EXPIRY/EXPIRED status badges
|
|
EXP-ADM-UT-004: Create lot form — expiry date required for tracking products
|
|
EXP-ADM-UT-005: Create lot form — expiry date optional for non-tracking products
|
|
EXP-ADM-UT-006: Inventory filter tabs — each shows correct subset
|
|
```
|
|
|
|
## Checkout Regression Tests
|
|
|
|
```
|
|
EXP-E2E-001: Checkout — normal non-expiry product → completes successfully
|
|
EXP-E2E-002: Checkout — expiry product with no valid lots → unavailable
|
|
EXP-E2E-003: Checkout — expiry product with valid lots → reserves correctly
|
|
EXP-E2E-004: Checkout — FEFO: oldest expiry lot decremented first
|
|
EXP-E2E-005: Checkout — cart with mixed expiry/non-expiry products → both work
|
|
EXP-E2E-006: Checkout — reserve then release → lots restored to correct quantities
|
|
```
|
|
|
|
## Given/When/Then Acceptance Criteria
|
|
|
|
```
|
|
GIVEN a product with expiration_tracking_enabled = false
|
|
WHEN inventory is received for a variant of that product
|
|
THEN expiration_date is NOT required
|
|
AND the variant uses existing variant-level stock
|
|
AND checkout works as before
|
|
|
|
GIVEN a product with expiration_tracking_enabled = true
|
|
WHEN inventory is received without an expiration date
|
|
THEN the backend returns 422 MISSING_EXPIRATION_DATE
|
|
|
|
GIVEN a product with expiration_tracking_enabled = true
|
|
WHEN a lot is received with an expiration date in the past
|
|
THEN the backend returns 422 PAST_EXPIRATION_DATE
|
|
|
|
GIVEN an inventory lot whose expiration_date is in the past
|
|
WHEN sellable inventory is calculated
|
|
THEN that lot contributes zero sellable units
|
|
AND the lot remains visible in admin
|
|
AND the lot can be manually adjusted/deleted by admin
|
|
|
|
GIVEN a product with expiration_tracking_enabled = true and multiple lots
|
|
WHEN a customer reserves units
|
|
THEN lots are allocated in FEFO order (earliest expiration first)
|
|
AND when the earliest lot is exhausted, allocation continues to the next
|
|
|
|
GIVEN the feature flag expiration_tracking = false
|
|
WHEN any existing checkout flow runs
|
|
THEN behavior is byte-for-byte identical to pre-migration
|
|
```
|