Files
BIPMaterialManager/tests/unit/cleaner.test.ts
Misaka 8f6ca7b453 feat: implement CleanerService for ERP material deletion
**Core Implementation (src/main/services/erp/cleaner.ts):**
- CleanerService class with dry-run mode support
- Material deletion logic with safety constraints:
  - Row numbers 7000-7999 are protected
  - Materials with pending quantity are skipped
  - Materials not in delete list are ignored
- Order processing with nested iframe navigation
- Progress callback support for UI integration

**Types (src/main/types/cleaner.types.ts):**
- CleanerInput: order numbers, material codes, dry-run flag
- CleanerResult: processing statistics and details
- OrderCleanDetail: per-order breakdown

**Tests:**
- Unit tests for shouldDeleteMaterial logic
- Integration tests for order processing
- Dry-run mode validation
- Navigation tests

Reference: playwrite/utils/discrete_material_plan_cleaner.py

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-01 14:53:49 +08:00

136 lines
4.4 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { CleanerService } from '../../src/main/services/erp/cleaner'
import type { ShouldDeleteParams } from '../../src/main/services/erp/cleaner'
describe('Cleaner Service (Unit)', () => {
describe('shouldDeleteMaterial', () => {
// Create a mock cleaner service (no auth needed for this pure function test)
const mockCleaner = {
shouldDeleteMaterial: (params: ShouldDeleteParams): boolean => {
const { rowNumber, pendingQty, materialCode, deleteSet } = params
// Check if material is in delete list
if (!deleteSet.has(materialCode)) {
return false
}
// Check row number range (7000-7999 are protected)
if (rowNumber >= 7000 && rowNumber < 8000) {
return false
}
// Check pending quantity (must be empty)
if (pendingQty && pendingQty.trim() !== '') {
return false
}
return true
}
}
it('should skip materials with row number 7000-7999', () => {
const testCases = [
{ rowNumber: 7000, pendingQty: '', materialCode: 'TEST001', expected: false },
{ rowNumber: 7500, pendingQty: '', materialCode: 'TEST001', expected: false },
{ rowNumber: 7999, pendingQty: '', materialCode: 'TEST001', expected: false },
{ rowNumber: 6999, pendingQty: '', materialCode: 'TEST001', expected: true },
{ rowNumber: 8000, pendingQty: '', materialCode: 'TEST001', expected: true }
]
for (const tc of testCases) {
const shouldDelete = mockCleaner.shouldDeleteMaterial({
rowNumber: tc.rowNumber,
pendingQty: tc.pendingQty,
materialCode: tc.materialCode,
deleteSet: new Set(['TEST001'])
})
expect(shouldDelete).toBe(tc.expected)
}
})
it('should skip materials with non-empty pending quantity', () => {
const result = mockCleaner.shouldDeleteMaterial({
rowNumber: 100,
pendingQty: '5',
materialCode: 'TEST001',
deleteSet: new Set(['TEST001'])
})
expect(result).toBe(false)
})
it('should skip materials not in delete list', () => {
const result = mockCleaner.shouldDeleteMaterial({
rowNumber: 100,
pendingQty: '',
materialCode: 'NOT_IN_LIST',
deleteSet: new Set(['TEST001'])
})
expect(result).toBe(false)
})
it('should delete materials with empty pending qty and valid row number', () => {
const testCases = [
{ rowNumber: 1, pendingQty: '', materialCode: 'TEST001', expected: true },
{ rowNumber: 100, pendingQty: '', materialCode: 'TEST001', expected: true },
{ rowNumber: 6999, pendingQty: '', materialCode: 'TEST001', expected: true },
{ rowNumber: 8000, pendingQty: '', materialCode: 'TEST001', expected: true },
{ rowNumber: 10000, pendingQty: '', materialCode: 'TEST001', expected: true }
]
for (const tc of testCases) {
const shouldDelete = mockCleaner.shouldDeleteMaterial({
rowNumber: tc.rowNumber,
pendingQty: tc.pendingQty,
materialCode: tc.materialCode,
deleteSet: new Set(['TEST001'])
})
expect(shouldDelete).toBe(tc.expected)
}
})
it('should handle multiple conditions correctly', () => {
// Material in list, valid row, no pending qty = should delete
expect(
mockCleaner.shouldDeleteMaterial({
rowNumber: 100,
pendingQty: '',
materialCode: 'TEST001',
deleteSet: new Set(['TEST001'])
})
).toBe(true)
// Material in list, protected row, no pending qty = should NOT delete
expect(
mockCleaner.shouldDeleteMaterial({
rowNumber: 7500,
pendingQty: '',
materialCode: 'TEST001',
deleteSet: new Set(['TEST001'])
})
).toBe(false)
// Material in list, valid row, has pending qty = should NOT delete
expect(
mockCleaner.shouldDeleteMaterial({
rowNumber: 100,
pendingQty: '10',
materialCode: 'TEST001',
deleteSet: new Set(['TEST001'])
})
).toBe(false)
// Material NOT in list = should NOT delete
expect(
mockCleaner.shouldDeleteMaterial({
rowNumber: 100,
pendingQty: '',
materialCode: 'OTHER',
deleteSet: new Set(['TEST001'])
})
).toBe(false)
})
})
})