Pure functions for copy-first-row attrs and same-box start/end slot pick. Co-authored-by: Cursor <cursoragent@cursor.com>
117 lines
3.0 KiB
TypeScript
117 lines
3.0 KiB
TypeScript
import { describe, it } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import {
|
|
copyFirstProductLineAttrs,
|
|
resolveStorageSlotRangeFill
|
|
} from './storage-slot-range-fill'
|
|
|
|
describe('copyFirstProductLineAttrs', () => {
|
|
it('copies spec/expectedCellCount/viability to other rows', () => {
|
|
const lines = [
|
|
{ specName: '1ml', expectedCellCount: 1.5, viability: 90, productCode: 'A' },
|
|
{ specName: '', expectedCellCount: undefined, viability: undefined, productCode: 'B' }
|
|
]
|
|
const r = copyFirstProductLineAttrs(lines)
|
|
assert.equal(r.ok, true)
|
|
assert.equal(lines[1].specName, '1ml')
|
|
assert.equal(lines[1].expectedCellCount, 1.5)
|
|
assert.equal(lines[1].viability, 90)
|
|
assert.equal(lines[1].productCode, 'B')
|
|
})
|
|
|
|
it('fails when fewer than 2 lines', () => {
|
|
const r = copyFirstProductLineAttrs([{ specName: 'x' }])
|
|
assert.equal(r.ok, false)
|
|
})
|
|
})
|
|
|
|
describe('resolveStorageSlotRangeFill', () => {
|
|
const box = 'node-1'
|
|
const slots = [
|
|
{
|
|
id: 's1',
|
|
orderNum: 1001,
|
|
facilityNodeId: box,
|
|
slotStatus: '空',
|
|
occupiedCount: 0,
|
|
slotCode: '1-1'
|
|
},
|
|
{
|
|
id: 's2',
|
|
orderNum: 1002,
|
|
facilityNodeId: box,
|
|
slotStatus: '空',
|
|
occupiedCount: 0,
|
|
slotCode: '1-2'
|
|
},
|
|
{
|
|
id: 's3',
|
|
orderNum: 1003,
|
|
facilityNodeId: box,
|
|
slotStatus: '占用',
|
|
occupiedCount: 1,
|
|
slotCode: '1-3'
|
|
},
|
|
{
|
|
id: 's4',
|
|
orderNum: 1004,
|
|
facilityNodeId: box,
|
|
slotStatus: '空',
|
|
occupiedCount: 0,
|
|
slotCode: '1-4'
|
|
}
|
|
]
|
|
|
|
it('fails when start/end different boxes', () => {
|
|
const r = resolveStorageSlotRangeFill({
|
|
slots: [{ ...slots[0] }, { ...slots[1], facilityNodeId: 'other', id: 'sx' }],
|
|
startSlotId: 's1',
|
|
endSlotId: 'sx',
|
|
emptyLineCount: 1,
|
|
formUsedSlotIds: []
|
|
})
|
|
assert.equal(r.ok, false)
|
|
assert.match(r.message, /同一盒子/)
|
|
})
|
|
|
|
it('fails when range contains occupied', () => {
|
|
const r = resolveStorageSlotRangeFill({
|
|
slots,
|
|
startSlotId: 's1',
|
|
endSlotId: 's4',
|
|
emptyLineCount: 2,
|
|
formUsedSlotIds: []
|
|
})
|
|
assert.equal(r.ok, false)
|
|
assert.match(r.message, /已占用/)
|
|
})
|
|
|
|
it('takes first N available in normalized range and skips form-used', () => {
|
|
const emptySlots = slots.filter((s) => s.id !== 's3')
|
|
const r = resolveStorageSlotRangeFill({
|
|
slots: emptySlots,
|
|
startSlotId: 's4',
|
|
endSlotId: 's1',
|
|
emptyLineCount: 2,
|
|
formUsedSlotIds: ['s2']
|
|
})
|
|
assert.equal(r.ok, true)
|
|
if (r.ok) {
|
|
assert.deepEqual(r.slotIds, ['s1', 's4'])
|
|
}
|
|
})
|
|
|
|
it('fails when available count < emptyLineCount', () => {
|
|
const emptySlots = slots.filter((s) => s.id !== 's3')
|
|
const r = resolveStorageSlotRangeFill({
|
|
slots: emptySlots,
|
|
startSlotId: 's1',
|
|
endSlotId: 's2',
|
|
emptyLineCount: 3,
|
|
formUsedSlotIds: []
|
|
})
|
|
assert.equal(r.ok, false)
|
|
assert.match(r.message, /不足/)
|
|
})
|
|
})
|