import assert from 'node:assert/strict' import fs from 'node:fs' import path from 'node:path' import test from 'node:test' import { fileURLToPath } from 'node:url' import { run } from './phase4-overseas-pda.mjs' const here = path.dirname(fileURLToPath(import.meta.url)) const source = fs.readFileSync(path.join(here, 'phase4-overseas-pda.mjs'), 'utf8') test('PDA module is injected, token-scoped, traceable, and contains no direct network or secret source', () => { assert.match(source, /export async function run\(context\)/) assert.match(source, /requestOverseas\(/) assert.match(source, /requestOverseasMultipart\(/) assert.match(source, /requestId=/) assert.match(source, /traceId=/) assert.doesNotMatch(source, /https?:\/\//) assert.doesNotMatch(source, /fetch\(|process\.env/) assert.doesNotMatch(source, /planNo|allocationId/) }) test('PDA module covers the container-number receiving contract and excludes outbound domains', () => { for (const token of ['/candidates', '/prepare', '/current/unload-photos/files', '/current/sign', '/current/skus/resolve', '/current/cartons/resolve', '/current/pallets/resolve', '/current/receipts/bulk', '/current/receipts/carton', '/current/complete']) assert.ok(source.includes(token), token) for (const role of ['LICENSE_PLATE', 'CONTAINER_SEAL', 'DOOR_OPEN']) assert.ok(source.includes(role), role) for (const forbidden of ['/outbound', '/dispatch', '/delivery', '/internal/']) assert.equal(source.includes(forbidden), false, forbidden) assert.match(source, /同键同载荷重放/) assert.match(source, /同键异载荷冲突/) assert.match(source, /跨柜切换锁返回409/) assert.match(source, /第二仓库操作员抢占同柜返回409/) assert.match(source, /WAREHOUSE_PICKER/) assert.match(source, /lastleg:pda:operate/) assert.match(source, /超收被拒绝/) assert.match(source, /重复箱新键被拒绝/) assert.match(source, /key\(context, `fba-bulk-\$\{index\}`\)/) assert.match(source, /key\(context, `overseas-carton-\$\{index\}`\)/) assert.match(source, /模式硬拒绝/) assert.match(source, /current返回404正常空态/) assert.match(source, /安全扫描返回A0400/) assert.match(source, /customerId: 0/) assert.match(source, /overseasWarehouseId, password/) assert.match(source, /inbound\?\.overseasWarehouseId/) assert.doesNotMatch(source, /const warehouseId = id\(inbound\?\.destinationWarehouseId\)/) assert.match(source, /目的仓 .* 不得代替物理仓/) assert.match(source, /retained as acceptance evidence/) assert.match(source, /preferredProducer\?\.cartonMarks\?\.length/) assert.match(source, /for \(const \[index, cartonMark\] of cartonMarks\.entries\(\)\)/) assert.match(source, /last-leg\/orders\?page=1&size=200&containerNo=/) assert.match(source, /phase4OverseasPda\.orderId = id\(order\.id\)/) assert.match(source, /phase4OverseasPda\.customerId = id\(order\.customerId\)/) }) test('missing WAREHOUSE_PICKER overseas authority is a deployment blocker', async () => { const calls = []; const gaps = []; const steps = [] const context = { marker: 'PDA-CONTRACT', context: {}, createdResources: {}, async request(method, requestPath) { calls.push({ method, requestPath }) return { method, path: requestPath, http: 200, body: { code: '200', data: [] }, requestId: 'request-contract-1', traceId: 'trace-contract-0001' } }, async requestOverseas(...args) { calls.push(args); throw new Error('must not request without credentials') }, async requestOverseasMultipart(...args) { calls.push(args); throw new Error('must not request without credentials') }, apiSuccess(module, name, response) { return response.body.data }, addStep(...args) { steps.push(args) }, assert(module, name, response, passed, evidence, options) { steps.push({ module, name, passed, evidence, options }) }, addCoverageGap(...args) { gaps.push(args) }, } await run(context) assert.ok(calls.some((call) => call.requestPath === '/api/platform/v1/iam/roles')) assert.ok(calls.some((call) => call.requestPath === '/api/platform/v1/iam/permissions')) assert.ok(gaps.some((gap) => gap[2] === '海外PDA仓库操作员权限')) assert.ok(steps.some((step) => step?.passed === false && step?.options?.deploymentBlocker)) }) test('empty candidate response cannot become a zero-business-request pass', async () => { const calls = []; const gaps = []; const steps = [] const response = (data, requestPath, method = 'GET') => ({ method, path: requestPath, http: 200, body: { code: '200', data }, requestId: 'req-contract', traceId: 'trace-contract' }) let userSequence = 0 const roleId = '4000005'; const permissionId = '5107015' const context = { marker: 'PDA-CONTRACT', overseasToken: '', createdResources: {}, context: {}, async findAvailablePhone() { userSequence += 1; return `1750000000${userSequence}` }, async request(method, requestPath, body) { calls.push({ channel: 'domestic', method, requestPath }) if (method === 'GET' && requestPath.endsWith('/roles')) return response([ { id: roleId, roleCode: 'WAREHOUSE_PICKER', status: 'ACTIVE', permissionIds: [permissionId] }, ], requestPath, method) if (method === 'GET' && requestPath.endsWith('/permissions')) return response([ { id: permissionId, permissionCode: 'lastleg:pda:operate' }, ], requestPath, method) if (method === 'POST' && requestPath.endsWith('/users')) return response({ membershipId: String(9000 + userSequence), username: body.username, tenantId: '0', roleIds: [roleId], overseasWarehouseId: String(body.overseasWarehouseId), status: 'ACTIVE', }, requestPath, method) if (method === 'POST' && requestPath === '/api/auth/login') return response({ overseasAccessToken: `pda-token-${userSequence}` }, requestPath, method) if (method === 'PUT' && /\/users\/\d+\/status$/.test(requestPath)) return response({ status: 'DISABLED' }, requestPath, method) throw new Error(`unexpected domestic request ${method} ${requestPath}`) }, async requestOverseas(method, requestPath, body, headers, token) { calls.push({ channel: 'overseas', method, requestPath }) if (method === 'POST' && requestPath === '/api/platform/v1/warehouse-admin') return response('900', requestPath, method) if (requestPath.endsWith('/current')) return { method, path: requestPath, http: 200, body: { code: 'A0404', message: 'not found' }, requestId: 'req-current', traceId: 'trace-current' } return response([], requestPath, method) }, apiSuccess(module, name, result) { return result.body.data }, addStep(module, name, result, passed, evidence, options) { steps.push({ module, name, result, passed, evidence, options }) }, assert(module, name, result, passed, evidence, options) { steps.push({ module, name, result, passed, evidence, options }) }, addCoverageGap(...args) { gaps.push(args) }, } await run(context) assert.ok(calls.some((call) => call.requestPath.endsWith('/candidates'))) assert.ok(steps.some((step) => step.name.includes('正常空态') && step.passed)) assert.ok(steps.some((step) => step.name.includes('PDA柜号候选覆盖前置') && !step.passed && step.options.deploymentBlocker)) assert.equal(gaps.length, 1) assert.equal(context.createdResources.phase4OverseasPda.finalStatus, null) assert.equal(context.createdResources.phase4OverseasPda.pickerMembershipIds.length, 2) assert.equal(calls.filter((call) => /\/users\/\d+\/status$/.test(call.requestPath)).length, 2) })