Files
baiyue-firstleg/scripts/admin-test-env-modules/fleet-cross-customer-fixture-contract.test.mjs
T

322 lines
25 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { createFleetCrossCustomerFixture, preparationContract, safeFixtureCount } from './fleet-cross-customer-fixture.mjs'
import { fixtureContract } from './fleet-cross-customer-acceptance.mjs'
// Strict offline HTTP model executes the real qualification and prepareFleetTrip helpers too.
function model(fault = '', countPatch = {}) {
const copy = value => structuredClone(value), calls = [], customers = [], transfers = [], dispatches = []
const options = { execute: true, warehouseId: '10', destinationWarehouseId: '20', pdaToken: 'caller-pda-secret' }
const trip = { trip: { tripNo: 'TRIP', recordVersion: '0', warehouseId: '10', status: 'PLANNED', maxPalletSpaces: 30,
vehiclePlate: 'TEST', driverName: 'TEST', driverPhone: '19900000000' }, dispatches: [] }
const origin = { warehouse: { id: '10', tenantId: '0', warehouseType: 'OVERSEAS', status: 'ACTIVE', warehouseCode: 'ORIGIN', warehouseName: 'Origin' },
capabilities: [{ capabilityCode: 'RECEIVING', enabled: fault !== 'disabled receiving' }] }
const destination = { warehouse: { id: '20', tenantId: '0', warehouseType: 'FBA', status: 'ACTIVE', warehouseCode: 'FBA', warehouseName: 'Destination' },
addresses: [{ addressLine1: 'TEST street', countryCode: 'US' }] }
const response = data => ({ http: 200, body: { code: '200', data: copy(data) }, requestId: 'offline-request', traceId: 'offline-trace' })
const buckets = { availablePackages: '1', holdPackages: '0', allocatedPackages: '0', pickedPackages: '0', receivingPackages: '0' }
const stock = transfer => {
const n = transfers.indexOf(transfer), scope = { customerId: transfer.customerId, overseasWarehouseId: '10', inboundCode: transfer.transferNo }
const position = { ...scope, ...buckets, id: `P${n}`, balanceId: `B${n}`, palletId: `PAL${n}`, palletNo: `PALLET${n}` }
if (fault === 'foreign position') position.customerId = 'OTHER'
return { balance: { ...scope, ...buckets, id: `B${n}`, inTransitPackages: '0', procurementPackages: '0' }, positions: [position],
ledger: [{ ...scope, id: `L${n}`, balanceId: `B${n}`, positionId: `P${n}`, palletNo: `PALLET${n}`, businessType: 'STOCK_IN', businessOrderNo: transfer.transferNo,
beforePackages: '0', deltaPackages: '1', afterPackages: '1' }] }
}
let native
const cargo = () => transfers.map((transfer, n) => ({ dispatchItemId: `ITEM${n}`, customerId: transfer.customerId,
warehousePalletId: `PAL${n}`, palletNo: `PALLET${n}`, scheduledPackages: 1, pickedPackages: 0,
destination: { key: `FBA:${transfer.destinationWarehouseCode}` } }))
const parentFixture = { marker: 'PARENT', tenantId: 'PARENT-ID', password: 'parent-secret' }
const context = { marker: 'RUN-CROSS', token: 'admin-secret', options: { customerName: 'Parent customer' }, context: { merchantQualificationFixture: parentFixture },
createdResources: { merchantQualificationFixture: { marker: 'PARENT' } },
assert(_module, label, _response, passed) { assert.ok(passed, label) },
apiSuccess(_module, label, result) { assert.equal(result.http, 200, label); assert.equal(result.body.code, '200', label); return result.body.data },
async findAvailablePhone() { return `1990000000${customers.length}` },
async requestWithBody(method, raw, form) {
calls.push({ method, raw, multipart: true }); assert.ok(form instanceof FormData)
assert.match(raw, /^\/api\/platform\/v1\/files\/qualifications\/\d+\/required-package\?/)
return response({})
},
async requestMultipart(method, raw, file, token = this.token) {
calls.push({ method, raw, token, multipart: true }); assert.ok(file.content.length > 0)
if (raw.includes('/license?')) return response({ id: 'DRIVER', recordVersion: '1', licenseFileId: 'LICENSE' })
const evidenceKind = raw.match(/^\/api\/platform\/v1\/last-leg\/dispatches\/DS-ONE\/evidence\/(VEHICLE|DRIVER_PHOTO|DRIVER)$/)?.[1]
if (evidenceKind) {
assert.equal(token, 'admin-secret')
assert.equal(file.type, 'image/png')
return response({ fileId: { VEHICLE: '901', DRIVER_PHOTO: '902', DRIVER: '903' }[evidenceKind] })
}
assert.match(raw, /\/warehouse-transfers\/IN[01]\/measurement-photos\/files$/); assert.equal(token, options.pdaToken)
return response({ fileId: 'PHOTO', sizeBytes: file.content.length })
},
async request(method, raw, body, token = this.token) {
calls.push({ method, raw, body: copy(body), token })
const url = new URL(raw, 'https://offline.invalid'), path = url.pathname
if (path.startsWith('/api/platform/')) assert.equal(token, 'admin-secret')
if (path.startsWith('/api/pda/')) assert.equal(token, options.pdaToken)
if (path.endsWith('/warehouse-admin/10')) return response(origin)
if (path.endsWith('/warehouse-admin/20')) return response(destination)
if (path.endsWith('/warehouse-admin/21')) return response({ ...destination,
warehouse: { ...destination.warehouse, id: '21', warehouseCode: 'FBA2', warehouseName: 'Second destination' } })
if (path === '/api/platform/v1/iam/tenants') {
const n = customers.length, id = fault === 'reused customer' ? '100' : String(100 + n)
const customer = { id, username: `user${n}`, password: `fresh-secret-${n}`, marker: body.internalRemark }
customers.push(customer)
return response({ tenant: { id }, administratorUsername: customer.username, temporaryPassword: customer.password })
}
if (path.endsWith('/submit-review') && path.includes('/iam/')) return response({ status: 'PENDING' })
if (path === '/api/platform/v1/iam/reviews') return response(customers.map(row => ({ id: `REV${row.id}`, tenantId: row.id })))
if (path.endsWith('/dimension-initialization')) return response({ status: 'READY' })
if (path.endsWith('/approve')) return response({})
if (path.endsWith('/qualifications')) return response(['BUSINESS_LICENSE', 'LEGAL_PERSON_ID'].map(qualificationType => ({ qualificationType, status: 'APPROVED' })))
if (path === '/api/auth/login') {
assert.equal(token, '')
const customer = customers.find(row => row.username === body.loginId); assert.equal(body.password, customer.password)
return response({ accessToken: `merchant-${customer.id}-secret`, refreshToken: `refresh-${customer.id}-secret` })
}
if (path === '/api/auth/logout') {
assert.equal(body.refreshToken, token.replace('merchant-', 'refresh-'))
if (fault === 'logout failure' && token.includes('100')) throw new Error('offline logout failure')
return response(null)
}
const merchant = customers.find(row => token === `merchant-${row.id}-secret`)
if (path === '/api/merchant/v1/iam/tenant/profile') return response({ id: fault === 'foreign login' ? 'OTHER' : merchant.id })
if (path === '/api/merchant/v1/last-leg/warehouse-transfers') {
assert.ok(merchant); assert.equal(body.inboundCode, undefined); assert.deepEqual(body.sourceLastlegOrderIds, [])
assert.equal(body.needRelabel, false); assert.equal(body.items.length, 1); assert.equal(body.items[0].packageCount, 1); assert.equal(body.items[0].quantity, 1)
const transferNo = `IN${transfers.length}`
const itemId = `FORECAST-ITEM${transfers.length}`
const transfer = { transferNo, inboundCode: transferNo, customerId: merchant.id, operatingWarehouseId: body.operatingWarehouseId,
status: 'DRAFT', packageCount: 1, pallets: [], destinationWarehouseCode: body.destinationWarehouseCode,
items: [{ ...copy(body.items[0]), id: itemId, ...(fault === 'missing declared quantity' ? { quantity: null } : {}) }],
cartons: fault === 'missing forecast carton' ? [] : [{ cartonMark: `BX-FORECAST${transfers.length}`, status: 'FORECAST',
transferItemId: fault === 'foreign forecast item' ? 'OTHER-ITEM' : itemId }] }
transfers.push(transfer); return response(transfer)
}
if (path.includes('/warehouse-transfers/')) {
const transfer = transfers.find(row => path.includes(`/${row.transferNo}`)), n = transfers.indexOf(transfer)
assert.ok(transfer)
if (path.startsWith('/api/merchant/')) assert.equal(transfer.customerId, merchant.id)
if (path.endsWith('/submit')) transfer.status = 'PENDING_REVIEW'
else if (path.endsWith('/review')) { assert.equal(url.searchParams.get('customerId'), transfer.customerId); assert.equal(body.decision, 'APPROVE'); transfer.status = 'WAIT_PDA_RECEIVING' }
else if (path.endsWith('/pallets/drafts')) {
// The actual PalletDraft DTO accepts identity only; never derive measurement from ignored request fields.
assert.deepEqual(Object.keys(body), ['idempotencyKey'])
transfer.pallets = [{ id: `DRAFT${n}`, status: 'DRAFT', cartonCount: 0, lengthCm: 0, widthCm: 0, heightCm: 0, volumeCbm: 0, grossWeightKg: 0 }]
}
else if (path.endsWith('/generate')) Object.assign(transfer.pallets[0], { status: 'GENERATED', warehousePalletId: `PAL${n}`, palletNo: `PALLET${n}` })
else if (path.endsWith('/bind')) { assert.equal(body.palletCode, transfer.pallets[0].palletNo); transfer.pallets[0].status = 'BOUND' }
else if (path.endsWith('/cartons/count')) {
assert.equal(method, 'POST'); assert.equal(transfer.pallets[0].status, 'BOUND')
assert.equal(transfer.pallets[0].cartonCount, 0); assert.equal(body.forecastPackageCount, transfer.packageCount)
assert.equal(body.boxes.length, 1)
const box = body.boxes[0], item = transfer.items[0], carton = transfer.cartons[0], pallet = transfer.pallets[0]
assert.equal(box.cartonMark, carton.cartonMark); assert.equal(box.transferItemId, item.id)
assert.equal(box.transferPalletId, pallet.id); assert.notEqual(box.transferPalletId, pallet.warehousePalletId)
assert.deepEqual([box.length, box.width, box.height, box.weight], [100, 100, 100, 100])
assert.deepEqual(box.items, [{ skuCode: item.skuCode, quantity: item.quantity }])
Object.assign(carton, { transferPalletId: pallet.id, status: 'COUNTED', countedAt: '2026-10-20T09:00:00',
length: box.length, width: box.width, height: box.height, weight: box.weight, quantity: 1,
items: [{ transferItemId: item.id, skuCode: item.skuCode, quantity: 1 }] })
Object.assign(pallet, { cartonCount: 1, volumeCbm: 1, grossWeightKg: 100 })
if (fault === 'uncounted carton') { carton.status = 'FORECAST'; carton.countedAt = null }
if (fault === 'wrong counted pallet') carton.transferPalletId = 'OTHER-PALLET'
if (fault === 'wrong counted sku') carton.items[0].skuCode = 'OTHER-SKU'
if (fault === 'missing carton measurement') carton.weight = null
if (fault === 'zero counted pallet') pallet.cartonCount = 0
if (fault === 'wrong measured pallet weight') pallet.grossWeightKg = 0
}
else if (path.endsWith('/palletize')) {
assert.equal(transfer.cartons.length, 1); assert.equal(transfer.cartons[0].status, 'COUNTED'); assert.ok(transfer.cartons[0].countedAt)
assert.equal(transfer.pallets[0].cartonCount, 1); assert.equal(transfer.pallets[0].grossWeightKg, 100)
assert.equal(transfer.pallets[0].volumeCbm, 1); assert.ok(body.photoFileIds.length)
transfer.status = 'WAIT_CUSTOMER_CONFIRMATION'; transfer.pallets[0].status = 'COMPLETED'
}
else if (path.endsWith('/confirm-measurement')) transfer.status = 'READY_FOR_DISPATCH'
else assert.equal(method, 'GET')
return response(transfer)
}
if (path.includes('/inventory/')) {
const transfer = transfers.find(row => row.customerId === url.searchParams.get('customerId')), facts = stock(transfer)
if (path.endsWith('/positions')) return response(facts.positions)
if (/\/balances\/B[01]$/.test(path)) return response(facts.balance)
// Match InventoryLedgerView: the public DTO has neither balanceId nor positionId.
if (path.endsWith('/ledger')) return response({ records: facts.ledger.map(({ balanceId, positionId, ...view }) => view), total: fault === 'truncated ledger' ? 2 : 1 })
assert.ok(path.endsWith('/balances')); return response({ records: [facts.balance], total: 1 })
}
if (path.endsWith('/dispatches/candidates')) {
const n = transfers.findIndex(row => row.transferNo === url.searchParams.get('orderNo')), transfer = transfers[n]
assert.equal(transfer.status, 'READY_FOR_DISPATCH')
return response({ complete: true, candidates: [{ customerId: fault === 'foreign candidate' ? 'OTHER' : transfer.customerId,
warehouseId: '10', inboundCode: transfer.transferNo, palletNo: `PALLET${n}`, availablePackages: 1, destination: { key: `FBA:${transfer.destinationWarehouseCode}` },
sourceVersion: `SOURCE${n}`, blockReasons: [], selection: { sourceType: 'INBOUND_PLAN', planPalletId: `PLAN${n}`, sourceVersion: `SOURCE${n}` } }] })
}
if (path === '/api/platform/v1/last-leg/dispatches' && method === 'POST') {
assert.equal(body.cargo.length, 2); assert.equal(body.warehouseId, '10')
assert.deepEqual(body.cargo.map(row => row.planPalletId), ['PLAN0', 'PLAN1'])
assert.ok(body.cargo.every(row => !Object.hasOwn(row, 'customerId')))
native = { dispatchNo: 'DS-ONE', status: 'ACTIVE', stage: 'BEFORE_LOADING', reviewState: 'DRAFT', recordVersion: 0,
counts: { itemCount: 2, packageCount: 2, pickedPackageCount: 0, shippedPackageCount: 0, ...countPatch }, reviews: {},
documents: { outbounds: [], deliveries: [] }, dispatchInfo: {} }
trip.dispatches = [{ dispatchNo: native.dispatchNo }]
Object.assign(trip.trip, { tripPurpose: 'DELIVERY', stops: [{ name: 'Origin' }, ...[...new Set(transfers.map(row => row.destinationWarehouseCode))]
.map(code => ({ name: code, destinationKey: `FBA:${code}` }))] })
dispatches.push(native); return response(native)
}
if (path.endsWith('/master-data/vehicles')) { trip.trip.vehiclePlate = body.plateNumber.toUpperCase(); return response({ id: 'VEHICLE' }) }
if (path.endsWith('/fleet/drivers')) return response({ id: 'DRIVER', recordVersion: '0' })
if (path.endsWith('/fleet/drivers/DRIVER/review')) return response({ reviewStatus: 'APPROVED' })
if (path === '/api/platform/v1/last-leg/fleet/trips') return response({ records: [trip.trip], total: 1, size: 200, page: 1 })
if (path === '/api/platform/v1/last-leg/fleet/trips/TRIP') return response(trip)
if (path === '/api/platform/v1/last-leg/dispatches/DS-ONE/items') return response({ dispatchNo: native.dispatchNo, recordVersion: native.recordVersion,
items: fault === 'missing second cargo' ? cargo().slice(0,1) : cargo() })
if (path.startsWith('/api/platform/v1/last-leg/dispatches/DS-ONE')) {
if (method === 'PUT') {
assert.equal(body.recordVersion, native.recordVersion)
assert.deepEqual(body.vehicle.evidenceFileIds, ['901'])
assert.deepEqual(body.driver.evidence.photoFileIds, ['902'])
assert.deepEqual(body.driver.evidence.fileIds, ['903'])
Object.assign(trip.trip, { vehicleId: body.vehicle.vehicleId, driverId: body.driver.driverId })
native.vehicleInfo = { vehicle: { mode: 'MASTER', vehicleId: 'VEHICLE', plate: trip.trip.vehiclePlate }, driver: { driverId: 'DRIVER' } }
native.recordVersion++
} else if (method === 'POST') {
assert.equal(body.recordVersion, native.recordVersion); native.recordVersion++
if (path.endsWith('/submit-review')) native.reviewState = 'DISPATCH_PENDING'
if (path.endsWith('/dispatch-review')) { native.reviewState = 'LOADING_PENDING'; native.reviews.dispatchReview = { status: 'APPROVED' } }
if (path.endsWith('/loading-review')) { native.reviewState = 'APPROVED'; native.reviews.loadingReview = { status: 'APPROVED' }; native.stage = 'LOADING' }
}
const detail = copy(native)
if (fault === 'foreign delivery') detail.documents.deliveries = [{ customerId: 'OTHER' }]
return response(detail)
}
throw new Error(`Unexpected offline request ${method} ${path}`)
},
}
options.readInventory = async scope => {
assert.equal(scope.marker, context.marker); assert.equal(scope.warehouseId, options.warehouseId)
const facts = stock(transfers.find(row => row.customerId === scope.customerId))
const raw = { scope: { ...scope, coverage: 'CUSTOMER_WAREHOUSE_COMPLETE' }, balances: [facts.balance], positions: facts.positions, ledger: facts.ledger }
// The physical position table links to its inbound through balance_id; only the public joined DTO has inboundCode.
for (const position of raw.positions) delete position.inboundCode
if (fault === 'wrong raw scope') raw.scope.marker = 'OLD'
if (fault === 'wrong raw ledger position') raw.ledger[0].positionId = 'OTHER'
if (fault === 'wrong raw ledger balance') raw.ledger[0].balanceId = 'OTHER'
return copy(raw)
}
return { context, options, calls, customers, transfers, trip, parentFixture }
}
test('default preparation is network-free and exposes caller-owned lifecycle', async () => {
assert.equal(await createFleetCrossCustomerFixture(), preparationContract)
assert.equal(preparationContract.stage, fixtureContract.stage)
})
test('real helpers build independent current-run customers, receipts and one complete shared trip without picking', async () => {
const m = model(), oldEnvironment = { username: process.env.MERCHANT_TEST_USERNAME, password: process.env.MERCHANT_TEST_PASSWORD }
const fixture = await createFleetCrossCustomerFixture(m.context, m.options)
assert.equal(fixture.stage, fixtureContract.stage)
assert.equal(fixture.members.length, 2); assert.notEqual(fixture.members[0].customerId, fixture.members[1].customerId)
assert.ok(fixture.members.every(row => row.receiptVerified && row.marker === m.context.marker && row.positionId && row.balanceId))
assert.equal(m.context.context.merchantQualificationFixture, m.parentFixture)
assert.deepEqual(m.context.createdResources.merchantQualificationFixture, { marker: 'PARENT' })
assert.equal(m.context.options.customerName, 'Parent customer')
assert.deepEqual({ username: process.env.MERCHANT_TEST_USERNAME, password: process.env.MERCHANT_TEST_PASSWORD }, oldEnvironment)
assert.notEqual(m.customers[0].marker, m.customers[1].marker)
const proof = m.context.createdResources.fleetCrossCustomerFixture
assert.equal(proof.status, 'READY'); assert.deepEqual(proof.sessionCleanup.map(row => row.status), ['LOGGED_OUT', 'LOGGED_OUT'])
assert.equal(m.trip.dispatches.length, 1); assert.equal(new Set(fixture.members.map(row => row.dispatchNo)).size, 1); assert.deepEqual(m.trip.trip.stops.map(row => row.destinationKey).filter(Boolean), ['FBA:FBA'])
assert.equal(m.calls.filter(row => row.method === 'POST' && row.raw === '/api/platform/v1/last-leg/dispatches').length, 1)
assert.equal(m.calls.filter(row => row.raw.endsWith('/cartons/count')).length, 2)
for (const member of fixture.members) {
const prefix = `/api/pda/v1/last-leg/warehouse-transfers/${member.incomingNo}`
const draft = m.calls.findIndex(row => row.raw === `${prefix}/pallets/drafts`)
const bind = m.calls.findIndex(row => row.raw.startsWith(`${prefix}/pallets/`) && row.raw.endsWith('/bind'))
const count = m.calls.findIndex(row => row.raw === `${prefix}/cartons/count`)
const photo = m.calls.findIndex(row => row.raw === `${prefix}/measurement-photos/files`)
const receipt = m.calls.findIndex(row => row.raw === `${prefix}/palletize`)
assert.ok(draft < bind && bind < count && count < photo && photo < receipt)
assert.equal(member.measurementReceipt.countedCarton.cartonMark, m.calls[count].body.boxes[0].cartonMark)
assert.equal(member.measurementReceipt.syntheticTestMaterials, true)
assert.equal(member.measurementReceipt.finalizedPallet.cartonCount, 1)
}
assert.equal(m.calls.filter(row => row.raw.endsWith('/transfer-delivery')).length, 0)
assert.ok(!m.calls.some(row => /\/pick$|\/depart$|\/bookings|\/capacity-plan/.test(row.raw)))
const persisted = JSON.stringify(m.context.createdResources)
for (const secret of ['caller-pda-secret', 'admin-secret', 'fresh-secret-', 'merchant-100-secret', 'refresh-100-secret']) assert.ok(!persisted.includes(secret))
})
for (const fault of ['disabled receiving', 'reused customer', 'foreign login', 'missing forecast carton', 'missing declared quantity', 'foreign forecast item', 'uncounted carton', 'wrong counted pallet', 'wrong counted sku', 'missing carton measurement', 'zero counted pallet', 'wrong measured pallet weight', 'foreign position', 'truncated ledger', 'wrong raw scope', 'wrong raw ledger position', 'wrong raw ledger balance', 'foreign candidate', 'missing second cargo', 'foreign delivery', 'logout failure']) {
test(`fail closed, retain partial resources and clean owned sessions: ${fault}`, async () => {
const m = model(fault)
await assert.rejects(createFleetCrossCustomerFixture(m.context, m.options))
const proof = m.context.createdResources.fleetCrossCustomerFixture
assert.equal(proof.status, 'FAILED')
const logins = m.calls.filter(row => row.raw === '/api/auth/login'), logouts = m.calls.filter(row => row.raw === '/api/auth/logout')
assert.equal(logouts.length, logins.length)
assert.ok(!m.calls.some(row => row.method === 'DELETE' || row.raw.includes('/users/')))
if (fault === 'disabled receiving') assert.equal(m.calls.filter(row => row.method !== 'GET').length, 0)
if (['missing forecast carton', 'missing declared quantity', 'foreign forecast item', 'uncounted carton', 'wrong counted pallet', 'wrong counted sku', 'missing carton measurement', 'zero counted pallet', 'wrong measured pallet weight'].includes(fault))
assert.equal(m.calls.some(row => row.raw.endsWith('/palletize') || row.raw === '/api/platform/v1/last-leg/dispatches'), false)
const previousCount = m.calls.length
await assert.rejects(createFleetCrossCustomerFixture(m.context, m.options), /already started/)
assert.equal(m.calls.length, previousCount)
})
}
test('missing explicit warehouse or caller PDA fails before reading or writing APIs', async () => {
for (const field of ['warehouseId', 'destinationWarehouseId', 'pdaToken', 'readInventory']) {
const m = model(); delete m.options[field]
await assert.rejects(createFleetCrossCustomerFixture(m.context, m.options), /Missing/)
assert.equal(m.calls.length, 0)
}
})
test('native Jackson Long count strings build the same complete one-main fixture', async () => {
const m = model('', { packageCount: '2', pickedPackageCount: '0', shippedPackageCount: '0' })
const fixture = await createFleetCrossCustomerFixture(m.context, m.options)
assert.equal(fixture.dispatchNo, 'DS-ONE')
assert.equal(fixture.tripNo, 'TRIP')
assert.equal(fixture.members.length, 2)
assert.ok(fixture.members.every(member => member.dispatchNo === fixture.dispatchNo))
})
test('explicit per-member destinations are read before writes and preserved in both receipts and route', async () => {
const m = model(); m.options.destinationWarehouseIds = ['20', '21']
const fixture = await createFleetCrossCustomerFixture(m.context, m.options)
assert.deepEqual(fixture.members.map(row => row.destination.key), ['FBA:FBA', 'FBA:FBA2'])
assert.deepEqual(m.trip.trip.stops.slice(1).map(row => row.destinationKey), ['FBA:FBA', 'FBA:FBA2'])
const secondDestination = m.calls.findIndex(row => row.raw.includes('/warehouse-admin/21'))
const firstWrite = m.calls.findIndex(row => row.method !== 'GET')
assert.ok(secondDestination >= 0 && secondDestination < firstWrite)
assert.deepEqual(m.calls.filter(row => row.method === 'POST' && row.raw === '/api/merchant/v1/last-leg/warehouse-transfers')
.map(row => row.body.destinationWarehouseId), ['20', '21'])
})
test('malformed member destination scope fails without network', async () => {
for (const destinations of [[], ['20'], ['20', '21', '22'], ['21', '20'], ['20', '']]) {
const m = model(); m.options.destinationWarehouseIds = destinations
await assert.rejects(createFleetCrossCustomerFixture(m.context, m.options), /Two explicit/)
assert.equal(m.calls.length, 0)
}
})
test('fixture count parser accepts only explicit non-negative safe integers and rejects coercion', () => {
for (const [value, expected] of [[0, 0], ['0', 0], [2, 2], ['2', 2], [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], [String(Number.MAX_SAFE_INTEGER), Number.MAX_SAFE_INTEGER]]) {
assert.equal(safeFixtureCount(value), expected)
}
for (const value of [null, undefined, true, false, '', ' ', ' 2 ', '02', '+2', '-0', '-1', -1, 1.5, '1.5', '1e2', '2E0', NaN, Infinity, {}, [], Number.MAX_SAFE_INTEGER + 1, '9007199254740992']) {
assert.throws(() => safeFixtureCount(value), /非负安全整数/)
}
})
test('invalid created main counts fail before resource preparation or picking', async () => {
for (const field of ['packageCount', 'pickedPackageCount', 'shippedPackageCount']) {
const m = model('', { [field]: field === 'packageCount' ? '2e0' : false })
await assert.rejects(createFleetCrossCustomerFixture(m.context, m.options), /非负安全整数/)
assert.equal(m.calls.some(row => row.raw.endsWith('/master-data/vehicles')), false)
assert.equal(m.calls.some(row => row.raw.endsWith('/pick')), false)
}
})