test(firstleg): package carrier node demo checks

This commit is contained in:
2026-09-01 12:02:57 +08:00
parent 53078b4281
commit 6eaf484fcd
5 changed files with 67 additions and 7 deletions
@@ -10,14 +10,25 @@ export const STANDARD_NODES = Object.freeze([
])
export const EXCEPTION_CODES = Object.freeze(['CUSTOMS_HOLD', 'TERMINAL_HOLD', 'DOCUMENT_HOLD', 'UNKNOWN_HOLD'])
export const NODE_SEQUENCES = Object.freeze(Object.fromEntries(STANDARD_NODES.map((node, index) => [node, (index + 1) * 10])))
const ISO_CONTAINER_PATTERN = /^[A-Z]{3}[UJZ][0-9]{7}$/
function id(value) { return value == null ? '' : String(value) }
function number(value) { return Number.isFinite(Number(value)) }
function nullableText(value) { return value == null || typeof value === 'string' }
function nullableId(value) { return value == null || validLongId(value) }
function nullableIso(value) { return value == null || (typeof value === 'string' && !Number.isNaN(Date.parse(value))) }
function validLongId(value) { return typeof value === 'string' && /^\d{16,}$/.test(value) }
function validNumericId(value) { return typeof value === 'string' && /^\d+$/.test(value) && value !== '0' }
function validContainerNo(value) {
if (typeof value !== 'string' || !ISO_CONTAINER_PATTERN.test(value)) return false
const letterValues = [10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38]
let sum = 0
for (let index = 0; index < 10; index += 1) {
const code = value.charCodeAt(index)
sum += (code >= 48 && code <= 57 ? code - 48 : letterValues[code - 65]) * (2 ** index)
}
return Number(value[10]) === (sum % 11) % 10
}
function codeOf(response) { return response?.body?.code == null ? 'missing' : String(response.body.code) }
function rejected(response) { return response?.http === 200 && ['A0409', '409'].includes(codeOf(response)) }
function monotonicRejected(response) {
@@ -45,7 +56,8 @@ export function manualNodePayload({ expectedVersion, standardNode, observedAt, e
function validTask(task) {
return validLongId(task?.id) && Boolean(task?.queryNo) && validNumericId(task?.customerId)
&& validLongId(task?.oceanBookingId) && QUERY_STATUSES.includes(task?.status)
&& nullableId(task?.containerBookingId) && nullableText(task?.latestActualNode)
&& validLongId(task?.containerBookingId) && task?.trackingType === 'CONTAINER_NO'
&& validContainerNo(task?.trackingNo) && nullableText(task?.latestActualNode)
&& (task?.latestActualNode == null || STANDARD_NODES.includes(task.latestActualNode))
&& number(task?.version)
&& ['latestActualAt', 'lastSuccessAt', 'sourceUpdatedAt', 'expiresAt', 'stoppedAt']