87 lines
5.1 KiB
JavaScript
87 lines
5.1 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import test from 'node:test'
|
|
|
|
import { normalizeWeightPalletOptionPage } from './inbound-weight-ocr.mjs'
|
|
|
|
const read = (relativeUrl) => readFileSync(new URL(relativeUrl, import.meta.url), 'utf8')
|
|
const controller = read('../../firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformInboundMeasurementController.java')
|
|
const pageResult = read('../../common-core/src/main/java/com/baiyue/common/core/api/PageResult.java')
|
|
const longJackson = read('../../common-web/src/main/java/com/baiyue/common/web/LongIdJacksonConfiguration.java')
|
|
const openapi = read('../../firstleg/src/main/resources/openapi/firstleg-v1.yaml')
|
|
const adminApi = read('../../../apps/admin-web/src/modules/inbounds/weight/api/weightOcr.ts')
|
|
const adminApiTests = read('../../../apps/admin-web/src/modules/inbounds/weight/api/weightOcr.test.ts')
|
|
const adminPanel = read('../../../apps/admin-web/src/modules/inbounds/weight/components/WeightOcrPanel.vue')
|
|
const adminBehavior = read('../../../apps/admin-web/src/modules/inbounds/weight/components/WeightOcrPanel.behavior.test.ts')
|
|
|
|
function endpointBlock(source, marker, nextMarker) {
|
|
const start = source.indexOf(marker)
|
|
assert.notEqual(start, -1, `missing marker: ${marker}`)
|
|
const end = source.indexOf(nextMarker, start + marker.length)
|
|
return source.slice(start, end === -1 ? source.length : end)
|
|
}
|
|
|
|
test('formal success accepts two records and string pagination numbers', () => {
|
|
const records = [{ palletId: '1' }, { palletId: '2' }]
|
|
assert.deepEqual(normalizeWeightPalletOptionPage({ records, page: '1', size: '20', total: '2', pages: '1' }),
|
|
{ records, page: 1, size: 20, total: 2, pages: 1 })
|
|
})
|
|
|
|
test('formal empty page remains a successful page instead of an error', () => {
|
|
assert.deepEqual(normalizeWeightPalletOptionPage({ records: [], page: '1', size: '20', total: '0', pages: '0' }),
|
|
{ records: [], page: 1, size: 20, total: 0, pages: 0 })
|
|
})
|
|
|
|
test('malformed array and invalid page metadata are rejected by the HTTP contract', () => {
|
|
assert.equal(normalizeWeightPalletOptionPage([]), null)
|
|
assert.equal(normalizeWeightPalletOptionPage({ records: [], page: 1, size: 20, total: -1, pages: 0 }), null)
|
|
assert.equal(normalizeWeightPalletOptionPage({ records: [], page: 1, size: 101, total: 0, pages: 0 }), null)
|
|
})
|
|
|
|
test('Controller DTO and response envelope use PageResult records/page/size/total/pages', () => {
|
|
const block = endpointBlock(controller, '@GetMapping("/weight-pallet-options")', '@BusinessAudit')
|
|
assert.match(block, /ApiResponse<PageResult<InboundMeasurementResponses\.PalletSnapshot>>/)
|
|
assert.match(pageResult, /record PageResult<T>\(List<T> records, long page, long size, long total, long pages\)/)
|
|
})
|
|
|
|
test('OpenAPI documents the page object and permits runtime string pagination numbers', () => {
|
|
const endpoint = endpointBlock(openapi,
|
|
' /api/platform/v1/first-leg/inbounds/{inboundId}/weight-pallet-options:',
|
|
' /api/platform/v1/first-leg/inbounds/{inboundId}/weight-results:')
|
|
assert.match(endpoint, /data: \{\$ref: '#\/components\/schemas\/WeightPalletOptionPage'\}/)
|
|
const schema = endpointBlock(openapi, ' WeightPalletOptionPage:', ' CreateWeightResultRequest:')
|
|
assert.match(schema, /required: \[records, page, size, total, pages\]/)
|
|
for (const field of ['page', 'size', 'total', 'pages']) {
|
|
assert.match(schema, new RegExp(`${field}:[^\n]*(?:oneOf|string)`),
|
|
`${field} must permit the string form emitted by the shared Long serializer`)
|
|
}
|
|
assert.match(longJackson, /serializerByType\(Long\.TYPE, ToStringSerializer\.instance\)/)
|
|
})
|
|
|
|
test('Admin reads records, normalizes numeric strings, and never treats data as an array', () => {
|
|
const block = endpointBlock(adminApi, 'export async function getWeightPalletOptions', 'export async function uploadWeightImage')
|
|
assert.match(block, /ApiEnvelope<WeightPalletOptionPageResponse>/)
|
|
assert.match(block, /records:\s*page\.records/)
|
|
assert.match(block, /normalizedPageNumber\(page\.(?:page|size|total|pages)/)
|
|
assert.doesNotMatch(block, /ApiEnvelope<WeightPalletOption\[\]>/)
|
|
assert.match(adminPanel, /palletOptions\.value = result\.records/)
|
|
})
|
|
|
|
test('Admin behavior covers two-record success and an empty successful page', () => {
|
|
assert.match(adminApiTests, /formal paged pallet response[\s\S]{0,900}records[\s\S]{0,300}page: '1'/)
|
|
assert.match(adminBehavior, /no pallet options[\s\S]{0,500}palletPage\(\[\]\)/)
|
|
})
|
|
|
|
test('Admin behavior exposes a body code business error as a candidate load failure', () => {
|
|
assert.match(adminBehavior, /getWeightPalletOptions\)\.mockRejectedValueOnce\(new ApiResponseError\([^)]*'A0500'/)
|
|
assert.match(adminBehavior, /可选托盘加载失败|托盘服务暂不可用/)
|
|
})
|
|
|
|
test('Admin behavior separately covers a non-2xx HTTP or network error', () => {
|
|
assert.match(adminBehavior, /(?:HTTP error|HTTP错误|network error|网络错误)[\s\S]{0,700}(?:AxiosError|HTTP_5|status)/i)
|
|
})
|
|
|
|
test('Admin aborts and ignores an expired candidate response', () => {
|
|
assert.match(adminBehavior, /in-flight pallet request[\s\S]{0,900}signal\?\.aborted[\s\S]{0,300}过期托盘/)
|
|
})
|