fix(client): read all customs order pages with bounded concurrency
This commit is contained in:
@@ -17,19 +17,90 @@ const eligibility = evaluate(readFileSync(new URL('../utils/customsEligibility.t
|
||||
const order = (id, status = 'LOADED', taxMode = 'TAX_REFUND') => ({ id, status, taxMode, orderNo: `ORDER-${id}`, businessSummary: { inboundCode: `IN-${id}` } })
|
||||
const detail = id => ({ status: 'COMPLETED', merchantStatus: 'CUSTOMS_COMPLETED', inbounds: [{ orderId: id, taxMode: 'TAX_REFUND' }] })
|
||||
function harness(t, orders, getDetail = async id => detail(id)) {
|
||||
const store = { orders, loadError: '', load: async () => {} }, calls = []
|
||||
const store = { loadError: '', failPage: 0, transformPage: value => value }, calls = [], pageCalls = []
|
||||
const reader = evaluate(readFileSync(new URL('../utils/customsDocumentsReader.ts', import.meta.url), 'utf8'), {
|
||||
'./customsEligibility': eligibility,
|
||||
'../api/unifiedOrders': { getMerchantUnifiedOrders: async ({ page, size }) => {
|
||||
pageCalls.push({ page, size })
|
||||
if (store.loadError || store.failPage === page) throw new Error(store.loadError || '第二页订单查询失败')
|
||||
return store.transformPage({ page, size, total: orders.length, pages: Math.ceil(orders.length / size),
|
||||
records: orders.slice((page - 1) * size, page * size).map(row => ({ orderId: row.id, orderNo: row.orderNo, mainStatus: row.status, taxMode: row.taxMode, inboundCode: row.businessSummary.inboundCode })) })
|
||||
} },
|
||||
'../api/customs': { getMerchantCustomsForOrder: id => { calls.push(id); return getDetail(id) }, isMerchantCustomsUnavailable: error => error?.code === 'A0404' },
|
||||
})
|
||||
const source = readFileSync(new URL('./CustomsDocumentsView.vue', import.meta.url), 'utf8').match(/<script setup lang="ts">([\s\S]*?)<\/script>/)[1]
|
||||
const scope = vue.effectScope()
|
||||
const view = scope.run(() => evaluate(source + '\nexport { load, records, loading, errorMessage, openDetail, selectedOrderId, selectedDetail, dialogOpen, search, queryModel, filteredRecords }', {
|
||||
vue: { ...vue, onMounted() {} },
|
||||
'../public': { useFirstLegOrdersStore: () => store },
|
||||
'../utils/customsEligibility': eligibility,
|
||||
'../api/customs': { getMerchantCustomsForOrder: id => { calls.push(id); return getDetail(id) }, isMerchantCustomsUnavailable: error => error?.code === 'A0404' },
|
||||
'../utils/customsDocumentsReader': reader,
|
||||
}))
|
||||
t.after(() => scope.stop())
|
||||
return { view, store, calls }
|
||||
return { view, store, calls, pageCalls }
|
||||
}
|
||||
|
||||
test('finds historical refund documents beyond the first 200 unified orders', async t => {
|
||||
const orders = Array.from({ length: 200 }, (_, i) => order(`buy-${i}`, 'COMPLETED', 'BUY_DECLARATION'))
|
||||
orders.push(order('historical', 'COMPLETED'))
|
||||
const { view, calls, pageCalls } = harness(t, orders)
|
||||
await view.load()
|
||||
assert.deepEqual(calls, ['historical'])
|
||||
assert.equal(view.records.value[0].order.id, 'historical')
|
||||
assert.deepEqual(pageCalls, [{ page: 1, size: 200 }, { page: 2, size: 200 }])
|
||||
})
|
||||
|
||||
test('second page failure is explicit and retry does not retain an incomplete result', async t => {
|
||||
const orders = Array.from({ length: 201 }, (_, i) => order(String(i)))
|
||||
const { view, store, pageCalls } = harness(t, orders)
|
||||
store.failPage = 2
|
||||
await view.load()
|
||||
assert.equal(view.records.value.length, 0)
|
||||
assert.equal(view.errorMessage.value, '第二页订单查询失败')
|
||||
store.failPage = 0
|
||||
await view.load()
|
||||
assert.equal(view.records.value.length, 201)
|
||||
assert.equal(view.errorMessage.value, '')
|
||||
assert.equal(pageCalls.length, 4)
|
||||
})
|
||||
|
||||
test('empty pages, duplicate records and changing totals cannot silently truncate history', async t => {
|
||||
for (const change of [
|
||||
response => response.page === 2 ? { ...response, records: [] } : response,
|
||||
response => response.page === 2 ? { ...response, records: [{ ...response.records[0], orderId: '0' }] } : response,
|
||||
response => response.page === 2 ? { ...response, total: 202 } : response,
|
||||
]) {
|
||||
const { view, store, pageCalls } = harness(t, Array.from({ length: 201 }, (_, i) => order(String(i), 'COMPLETED', 'BUY_DECLARATION')))
|
||||
store.transformPage = change
|
||||
await view.load()
|
||||
assert.ok(view.errorMessage.value)
|
||||
assert.equal(view.records.value.length, 0)
|
||||
assert.equal(pageCalls.length, 2)
|
||||
}
|
||||
})
|
||||
|
||||
test('document fetch concurrency never exceeds four and each candidate is requested once', async t => {
|
||||
let active = 0, peak = 0
|
||||
const { view, calls, pageCalls } = harness(t, Array.from({ length: 205 }, (_, i) => order(String(i))), async id => {
|
||||
active += 1; peak = Math.max(peak, active)
|
||||
await new Promise(resolve => setImmediate(resolve))
|
||||
active -= 1
|
||||
return detail(id)
|
||||
})
|
||||
await view.load()
|
||||
assert.equal(peak, 4)
|
||||
assert.equal(new Set(calls).size, 205)
|
||||
assert.equal(calls.length, 205)
|
||||
assert.equal(pageCalls.length, 2)
|
||||
assert.equal(view.records.value.length, 205)
|
||||
})
|
||||
|
||||
test('empty result uses one page request and no document requests', async t => {
|
||||
const { view, calls, pageCalls } = harness(t, [])
|
||||
await view.load()
|
||||
assert.equal(pageCalls.length, 1)
|
||||
assert.equal(calls.length, 0)
|
||||
assert.equal(view.errorMessage.value, '')
|
||||
})
|
||||
|
||||
test('tax-refund documents remain accessible through delivered and completed orders; buy orders and preloading are excluded', async t => {
|
||||
const stages = ['DRAFT', 'PENDING_REVIEW', 'WAIT_MEASURE', 'WAIT_PALLETIZE', 'WAIT_SPLIT', 'LOADING', 'LOADED', 'DELIVERED', 'COMPLETED']
|
||||
const orders = stages.flatMap(status => [order(status, status), order(`BUY-${status}`, status, 'BUY_DECLARATION')])
|
||||
|
||||
Reference in New Issue
Block a user