import { describe, it } from 'node:test' import assert from 'node:assert/strict' import { classifyTasksForCompleteWorkOrderGate, findTaskStepGateIssue, needsProductionReceptionGate } from './production-complete-work-order-gate-logic' describe('classifyTasksForCompleteWorkOrderGate', () => { it('no_tasks when list empty', () => { assert.deepEqual(classifyTasksForCompleteWorkOrderGate([]), { kind: 'no_tasks' }) }) it('all_done when only completed or voided tasks remain', () => { assert.deepEqual( classifyTasksForCompleteWorkOrderGate([ { id: '1', taskStatus: 'ywc' }, { id: '2', taskStatus: 'yzf' } ]), { kind: 'all_done' } ) }) it('check_incomplete returns unfinished tasks only', () => { const decision = classifyTasksForCompleteWorkOrderGate([ { id: '1', taskStatus: 'ywc' }, { id: '2', taskStatus: 'dpz' }, { id: '3', taskStatus: 'jxz' }, { id: '4', taskStatus: 'yzf' } ]) assert.equal(decision.kind, 'check_incomplete') if (decision.kind === 'check_incomplete') { assert.deepEqual( decision.incomplete.map((t) => t.id), ['2', '3'] ) } }) }) describe('needsProductionReceptionGate', () => { it('true for inbound cell_storage and research_donation', () => { assert.equal( needsProductionReceptionGate({ workOrderType: 'inbound', receptionSource: 'cell_storage' } as any), true ) assert.equal( needsProductionReceptionGate({ workOrderType: 'inbound', receptionSource: 'research_donation' } as any), true ) // 缺省 workOrderType 视为入库 assert.equal(needsProductionReceptionGate({ receptionSource: 'cell_storage' } as any), true) }) it('false for outbound/temporary even if receptionSource looks inbound', () => { assert.equal(needsProductionReceptionGate({ receptionSource: 'outbound' } as any), false) assert.equal(needsProductionReceptionGate({} as any), false) // 扩增/传代临时工单:DB 默认 reception_source=cell_storage,且常无 sampleCode assert.equal( needsProductionReceptionGate({ workOrderType: 'temporary', receptionSource: 'cell_storage', sampleCode: '' } as any), false ) assert.equal( needsProductionReceptionGate({ workOrderType: 'outbound', receptionSource: 'cell_storage' } as any), false ) }) }) describe('findTaskStepGateIssue', () => { it('requires assignee', () => { const issue = findTaskStepGateIssue({ steps: [{ id: 's1', processName: '分离', assigneeUserIds: [], plannedStartTime: '2026-01-01' }] }) assert.ok(issue) assert.match(issue!.message, /负责人/) assert.equal(issue!.stepId, 's1') }) it('requires planned start time', () => { const issue = findTaskStepGateIssue({ steps: [{ id: 's1', processName: '分离', assigneeUserIds: ['u1'], plannedStartTime: '' }] }) assert.ok(issue) assert.match(issue!.message, /计划开始时间/) }) it('requires barcode on existing output rows', () => { const issue = findTaskStepGateIssue({ steps: [ { id: 's1', processName: '分离', assigneeUserIds: ['u1'], plannedStartTime: '2026-01-01', outputs: [{ outputName: '细胞袋', outputBarcode: '' }] } ] }) assert.ok(issue) assert.match(issue!.message, /条码/) }) it('allows empty outputs', () => { assert.equal( findTaskStepGateIssue({ steps: [ { id: 's1', processName: '分离', assigneeUserIds: ['u1'], plannedStartTime: '2026-01-01', outputs: [] } ] }), null ) }) it('passes when all filled', () => { assert.equal( findTaskStepGateIssue({ steps: [ { id: 's1', processName: '分离', assigneeUserIds: ['u1'], plannedStartTime: '2026-01-01', outputs: [{ outputName: '细胞袋', outputBarcode: 'BC-1' }] } ] }), null ) }) })