# Bug 2 Preservation Property Tests - Results

## Test Execution Summary

**Date**: Task 5 Execution  
**Status**: ✅ ALL TESTS PASSED  
**Test File**: `api/src/inventory.service.bug2.preservation.test.ts`  
**Code State**: UNFIXED (before Bug 2 fix implementation)

## Test Results

### Test Suite: Bug 2 Preservation - Global Inventory Calculations Unchanged

**Total Tests**: 9  
**Passed**: 9  
**Failed**: 0  
**Duration**: 51ms

---

## Property 2.1: getSummary() aggregates across all warehouses

**Validates**: Requirement 6.1

### Test 1: Calculate Total Available MT as sum of all inventory_balance records
- **Status**: ✅ PASSED
- **Description**: Verified that getSummary() correctly sums currentQuantityMt from all inventory_balance records across multiple warehouses
- **Test Data**: 
  - Warehouse A: 5,000 MT
  - Warehouse B: 8,000 MT
  - Warehouse C: 3,000 MT
- **Expected**: Total Available MT = 16,000 MT, Total Empty MT = 10,000 MT
- **Actual**: Matched expected values
- **Conclusion**: getSummary() correctly aggregates across all warehouses

### Test 2: Calculate Total Empty MT correctly when Total Available exceeds threshold
- **Status**: ✅ PASSED
- **Description**: Verified that Total Empty MT is clamped to 0 when Total Available exceeds the 26,000 MT threshold
- **Test Data**: Total Available MT = 30,000 MT (exceeds threshold)
- **Expected**: Total Empty MT = 0 (Math.max(0, 26000 - 30000))
- **Actual**: Total Empty MT = 0
- **Conclusion**: Math.max clamping works correctly

### Test 3: Property-Based Test - getSummary() correctly aggregates any set of inventory balances
- **Status**: ✅ PASSED
- **Description**: Generated 50 random test cases with 1-10 inventory balances (0-10,000 MT each)
- **Property Verified**: 
  - Total Available MT = sum of all currentQuantityMt values
  - Total Empty MT = Math.max(0, 26000 - Total Available MT)
- **Test Runs**: 50 random cases
- **Conclusion**: Aggregation formula holds for all generated inputs

---

## Property 2.2: getInventory() displays per-batch records

**Validates**: Requirement 6.2

### Test 4: Return one record per inventory_balance entry
- **Status**: ✅ PASSED
- **Description**: Verified that getInventory() returns exactly one record per inventory_balance entry
- **Test Data**: 2 inventory balance records
- **Expected**: 2 inventory records with correct structure and availableMt values
- **Actual**: Returned 2 records with correct structure
- **Conclusion**: getInventory() correctly displays per-batch records

### Test 5: Property-Based Test - getInventory() returns one record per balance
- **Status**: ✅ PASSED
- **Description**: Generated 20 random test cases with 1-5 inventory balances
- **Property Verified**: 
  - Number of returned records = number of balance entries
  - Each record's availableMt matches the corresponding balance's currentQuantityMt
- **Test Runs**: 20 random cases
- **Conclusion**: One-to-one mapping between balances and inventory records holds

---

## Property 2.3: getExpiry() aggregates across warehouses

**Validates**: Requirement 6.3

### Test 6: Aggregate batch quantities across multiple warehouses
- **Status**: ✅ PASSED
- **Description**: Verified that getExpiry() aggregates the same batch across multiple warehouses
- **Test Data**: 
  - BATCH-001 in Warehouse A: 1,000 MT
  - BATCH-001 in Warehouse B: 2,000 MT
- **Expected**: One record with 3,000 MT total, listing both warehouses
- **Actual**: Returned one record with 3,000 MT
- **Conclusion**: getExpiry() correctly aggregates across warehouses

### Test 7: Aggregate same batch from multiple sheets
- **Status**: ✅ PASSED
- **Description**: Verified that getExpiry() aggregates the same batch appearing in multiple approved sheets
- **Test Data**: 
  - Sheet 1: BATCH-001 with 1,000 MT
  - Sheet 2: BATCH-001 with 2,000 MT
- **Expected**: One record with 3,000 MT total
- **Actual**: Returned one record with 3,000 MT
- **Conclusion**: getExpiry() correctly aggregates across multiple sheets

---

## Property 2.4: Stocked-out batches are excluded

**Validates**: Requirement 6.4

### Test 8: Exclude stocked-out batches from getExpiry()
- **Status**: ✅ PASSED
- **Description**: Verified that batches marked stockedOut: true are excluded from expiry results
- **Test Data**: 
  - BATCH-001: 1,000 MT (not stocked out)
  - BATCH-002: 2,000 MT (stocked out)
- **Expected**: Only BATCH-001 in results
- **Actual**: Only BATCH-001 returned
- **Conclusion**: Stocked-out batches are correctly excluded

### Test 9: Property-Based Test - Stocked-out batches never appear in expiry results
- **Status**: ✅ PASSED
- **Description**: Generated 20 random test cases with 2-5 batches, some marked as stocked out
- **Property Verified**: 
  - No batch marked stockedOut: true appears in getExpiry() results
  - Only non-stocked-out batches appear in results
- **Test Runs**: 20 random cases
- **Conclusion**: Stocked-out exclusion holds for all generated inputs

---

## Observations on Unfixed Code

### Baseline Behaviors Confirmed

1. **Global Aggregation**: getSummary() correctly aggregates inventory across ALL warehouses using inventory_balance records. This behavior must be preserved after the Bug 2 fix.

2. **Per-Batch Display**: getInventory() correctly displays one record per inventory_balance entry, showing per-batch inventory data. This behavior must be preserved.

3. **Cross-Warehouse Expiry Tracking**: getExpiry() correctly aggregates batch quantities across multiple warehouses for expiry tracking. This behavior must be preserved.

4. **Stocked-Out Filtering**: Batches marked stockedOut: true are correctly excluded from expiry queries. This behavior must be preserved.

### Key Insights

- The inventory_balance collection is the source of truth for global inventory metrics (getSummary, getInventory)
- The stock_movement_sheets collection is used for expiry tracking (getExpiry)
- The Bug 2 fix will modify getBatches() to use warehouse-specific data from stock_movement_sheets
- The fix should NOT affect getSummary(), getInventory(), or getExpiry() behaviors

### Property-Based Testing Coverage

- **Total Property Tests**: 3
- **Total Random Test Cases Generated**: 90 (50 + 20 + 20)
- **Coverage**: Tested aggregation formulas, record mapping, and filtering logic across wide input ranges

---

## Conclusion

✅ **All preservation tests PASS on unfixed code**

This confirms the baseline behaviors that must be preserved after implementing the Bug 2 fix. These tests will be re-run after the fix to ensure no regressions occur.

### Next Steps

1. Implement Bug 2 fix in `getBatches()` method (Task 6.1)
2. Verify bug condition exploration test passes (Task 6.2)
3. Re-run these preservation tests to confirm no regressions (Task 6.3)

### Test Maintenance

These tests should:
- Continue to PASS after the Bug 2 fix is implemented
- Be run as part of the regression test suite
- Be updated only if the preservation requirements change (unlikely)
