Files
pda-overseas-uniapp/tests/discard-rent-preparation.test.mjs
T
2026-10-29 13:37:35 +08:00

87 lines
5.2 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { confirmCargoDiscard } from '../src/api/fulfillment.js'
import { session } from '../src/stores/session.js'
const rentPath = '/api/pda/v1/last-leg/storage-rent/preparations'
const stockPath = '/api/pda/v1/last-leg/cargo-discards/CD%2F1/pallets/P%2F1/confirm'
const body = { lines: [{ itemId: '9007199254740993', actualPackages: 2 }], photoFileIds: ['101'], scannedCartonMarks: ['BOX-1', 'BOX-2'], confirmAllCartons: false, idempotencyKey: 'discard-key' }
const ready = { data: { preparationId: '9007199254740995', ready: true, status: 'READY' } }
const confirmed = { data: { discardNo: 'CD/1', palletNo: 'P/1', status: 'COMPLETED' } }
const pending = { data: { preparationId: '9007199254740995', ready: false, status: 'FREEZE_PENDING' } }
const submit = () => confirmCargoDiscard('CD/1', 'P/1', body.lines, body.photoFileIds, body.scannedCartonMarks, false, { idempotencyKey: body.idempotencyKey })
function transport(responses) {
const calls = []; session.apiBase = '/api'; session.token = 'overseas-token'
globalThis.uni = { request(options) {
calls.push({ method: options.method, url: options.url, data: options.data })
const response = responses.shift(); assert.ok(response, 'unexpected additional request')
if (response.network) return options.fail({ errMsg: 'network down' })
options.success({ statusCode: 200, data: { code: '200', ...response }, header: {} })
} }
return calls
}
test('出库先预校验仓租,再用相同明细、照片、箱号和幂等键确认一次', async () => {
const calls = transport([ready, confirmed]); await submit()
assert.deepEqual(calls, [
{ method: 'POST', url: rentPath, data: { mode: 'DISCARD', businessNo: 'CD/1', palletNo: 'P/1', ...body } },
{ method: 'POST', url: stockPath, data: body }
])
})
test('免费或不计仓租的服务端资格可以直接继续出库', async () => {
const calls = transport([{ data: { status: 'NOT_REQUIRED', ready: true, amount: 0 } }, confirmed])
await submit(); assert.equal(calls.length, 2); assert.equal(calls[1].url, stockPath)
})
test('等待财务回执期间只读状态,就绪后只提交一次出库', async t => {
t.mock.method(globalThis, 'setTimeout', callback => { callback(); return 0 })
const calls = transport([pending, ready, confirmed]); await submit()
assert.deepEqual(calls.map(call => call.method), ['POST', 'GET', 'POST'])
assert.equal(calls[1].url, rentPath + '/9007199254740995')
})
test('余额授信不足时显示财务原因,释放未使用预占且不发出库请求', async () => {
const calls = transport([{ data: { ...pending.data, status: 'FAILED', message: '可用余额和授信额度不足' } }, { data: {} }])
await assert.rejects(submit(), error => error.code === 'A0409' && /仓租校验未通过.*余额和授信额度不足/.test(error.message))
assert.deepEqual(calls.map(call => call.method), ['POST', 'DELETE'])
})
test('缺失、失败或断网的预校验不降级绕过,保留诊断信息', async () => {
for (const response of [{ code: 'A0404', message: 'missing', requestId: 'req', traceId: 'trace' }, { code: 'A0409', message: 'conflict' }, { network: true }]) {
const calls = transport([response])
await assert.rejects(submit(), error => response.network ? error.network : error.code === response.code && error.requestId === response.requestId && error.traceId === response.traceId)
assert.equal(calls.length, 1)
}
})
test('回执超时释放预占,阻止出库并提示稍后重新提交', async t => {
t.mock.method(globalThis, 'setTimeout', callback => { callback(); return 0 })
const calls = transport([pending, ...Array(20).fill(pending), { data: {} }])
await assert.rejects(submit(), /仓租资金预校验尚未通过/)
assert.equal(calls.filter(call => call.method === 'GET').length, 20)
assert.equal(calls.at(-1).method, 'DELETE')
assert.ok(calls.every(call => call.url.includes('/storage-rent/')))
})
test('状态查询失败仍释放原预占;释放失败不覆盖原始诊断', async t => {
t.mock.method(globalThis, 'setTimeout', callback => { callback(); return 0 })
const calls = transport([pending, { code: 'S0503', message: 'read failed', requestId: 'req', traceId: 'trace' }, { code: 'S0503', message: 'release failed' }])
await assert.rejects(submit(), error => error.message === 'read failed' && error.requestId === 'req' && error.traceId === 'trace')
assert.deepEqual(calls.map(call => call.method), ['POST', 'GET', 'DELETE'])
})
test('就绪标记必须为布尔真,异常响应不能放行出库', async () => {
const calls = transport([{ data: { ...ready.data, ready: 'true' } }, { data: {} }])
await assert.rejects(submit(), /预校验/); assert.equal(calls.at(-1).method, 'DELETE')
})
test('出库请求已发送后,业务错误或结果不明不重发、不取消原预占', async () => {
for (const failure of [{ code: 'A0409', message: 'stock changed' }, { network: true }]) {
const calls = transport([ready, failure])
await assert.rejects(submit(), error => failure.network ? error.network : error.code === failure.code)
assert.equal(calls.length, 2); assert.ok(calls.every(call => call.method === 'POST'))
}
})