import test from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { normalizeModuleOrder } from '../admin-test-env-acceptance.mjs' import { sameOutboundTask } from './dropship-pallet-matrix.mjs' import { probePartialPick } from './dropship-outbound-edge-cases.mjs' test('browser fixture is explicit and stops before overseas receipt or outbound', () => { assert.deepEqual(normalizeModuleOrder(['dropship-browser-fixture']), ['merchant-qualification-fixture', 'dropship-browser-fixture']) const source = readFileSync(new URL('dropship-pallet-matrix.mjs', import.meta.url), 'utf8') const branch = source.slice(source.indexOf('if (stopBeforeOverseasPda)'), source.indexOf("await verify(await prepare('SINGLE-MULTI'")) assert.match(branch, /prepare\('BROWSER-MULTI', 2\)/) assert.match(branch, /convertOverseas\(\[record\]\)/) assert.match(branch, /READY_FOR_BROWSER_RECEIVING'[\s\S]*return/) assert.doesNotMatch(branch, /receiveOverseas|outboundOverseas|createPicker/) }) test('partial-pick rejection stays failed; a changed snapshot aborts further picking', async () => { const failures = [] const snapshot = { pallets: [{ pickedPackageCount: 0 }] } const edge = { label: 'SINGLE-SINGLE', task: { outboundNo: 'OD', taskNo: 'PDA' }, pick: { packages: 4, cartonMarks: ['A', 'B', 'C', 'D'], idempotencyKey: 'pick' }, context: { assert: (_module, name, _response, passed) => { if (!passed) failures.push(name) }, requestOverseas: async () => ({ body: { code: 'A0409' } }) }, check: (_name, _response, passed) => assert.ok(passed), call: async () => structuredClone(snapshot), } assert.equal(await probePartialPick(edge), 0) assert.equal(failures.length, 1) assert.match(failures[0], /四箱任务先拣一箱成功/) let calls = 0 edge.call = async () => ({ pallets: [{ pickedPackageCount: calls++ }] }) await assert.rejects(probePartialPick(edge)) }) test('outbound replay preserves business identity and database millisecond timestamps', () => { const task = { id: '1', taskNo: 'PDA1', outboundNo: 'OD1', status: 'PENDING', pushedAt: '2026-09-14T20:24:45.920278604' } assert.equal(sameOutboundTask(task, { ...task, pushedAt: '2026-09-14T20:24:45.92' }), true) assert.equal(sameOutboundTask({ ...task, pushedAt: '2026-09-14T20:24:45.9999' }, { ...task, pushedAt: '2026-09-14T20:24:46' }), true) for (const key of ['id', 'taskNo', 'outboundNo', 'status', 'pushedAt']) { assert.equal(sameOutboundTask(task, { ...task, [key]: 'changed' }), false) } }) const source = readFileSync(new URL('./dropship-pallet-matrix.mjs', import.meta.url), 'utf8') const edges = readFileSync(new URL('./dropship-outbound-edge-cases.mjs', import.meta.url), 'utf8') test('outbound edge probes preserve failures and require zero drift before continuing', () => { assert.match(edges, /deploymentBlocker: true/) assert.match(edges, /四箱任务先拣一箱成功/) assert.match(edges, /分批拣货失败后无库存明细变化/) assert.match(edges, /JSON.stringify\(before\) === JSON.stringify\(after\)/) assert.match(source, /await probePartialPick/) assert.match(source, /pick.packages -= count/) assert.match(source, /pick.cartonMarks = pick.cartonMarks.slice\(count\)/) }) test('outbound creation probes cover concurrent idempotency, invalid cartons and cancellation', () => { assert.match(edges, /Promise.all\(\[create\(\), create\(\)\]\)/) for (const scenario of ['超量创建', '错误SKU创建', '不存在箱唛创建', '重复箱唛创建', '箱数与箱唛不符创建', '出库创建同键异载荷返回A0409', '未拣齐禁止完成出库', '部分已拣禁止取消']) { assert.ok(edges.includes(scenario)) } assert.match(edges, /取消后逐票SKU可用箱数恢复/) assert.match(source, /await outboundCreationGuards\(edge\)/) assert.match(source, /await outboundTaskGuards\(\{ ...edge, task \}\)/) }) test('two inbound orders explicitly share a SKU without losing their separate identities', () => { assert.match(source, /label.startsWith\('MULTI-MERGE-'\)/) assert.match(source, /skuCodes = \[`SKU-SHARED-\$\{marker\}`/) assert.match(source, /row.inboundCode === candidate.inboundCode/) }) test('dedicated outbound uses exact source cartons and verifies completion and stock deduction', () => { assert.match(source, /await outboundOverseas\(group, picker.token\)/) assert.match(source, /one-click-dropship\?customerId=/) assert.match(source, /position.inboundCode === candidate.inboundCode/) assert.match(source, /cartonMarks: inputs\[index\].cartonMarks/) assert.match(source, /outbound.outbound\?\.status === 'OUTBOUNDED'/) assert.match(source, /!remaining.some/) }) test('dropship matrix has a real customer prerequisite without generic dispatch fixtures', () => { const modules = normalizeModuleOrder(['dropship-pallet-matrix']) assert.ok(modules.includes('merchant-qualification-fixture')) assert.ok(modules.includes('dropship-pallet-matrix')) assert.ok(!modules.includes('phase5-overseas-fulfillment')) }) test('all four physical pallet shapes are explicitly produced', () => { assert.match(source, /prepare\('SINGLE-MULTI', 2\)/) assert.match(source, /prepare\('SINGLE-SINGLE', 1\)/) assert.match(source, /prepare\('SINGLE-MERGE', 2, \{ mergeToFirstPallet: true \}\)/) assert.match(source, /prepare\('MULTI-MERGE-A', 1, \{ deferSeal: true \}\)/) assert.match(source, /prepare\('MULTI-MERGE-B', 1, \{ targetPalletNo \}\)/) assert.match(source, /await verify\(first, 1\)/) assert.match(source, /await verify\(second, 1\)/) }) test('allocation checks both physical summaries and uncollapsed per-inbound quantities', () => { assert.match(source, /first-leg\/loading-plans\/\$\{plan.id\}/) assert.match(source, /const actual = detail.items.filter/) assert.match(source, /plan.items\?\.length === pallets.length/) assert.match(source, /actual.reduce\([\s\S]*?record.expectedCartonMarks.length/) assert.match(source, /await conserve\('手工加入后'\)/) assert.match(source, /await conserve\('智能推荐后'\)/) assert.match(source, /fullFlowVerified: false/) }) test('each scenario reaches physical loading through released booking and asynchronous completion', () => { assert.match(source, /ocean-booking-link/) assert.match(source, /readiness\?\.canConfirm === true/) assert.match(source, /action: 'PICK_PALLET'/) assert.match(source, /inventorySnapshotToken: inventory.snapshotToken/) for (const type of ['PHOTO_STEP_1', 'PHOTO_STEP_2', 'PHOTO_STEP_3', 'PHOTO_STEP_4', 'SEAL']) { assert.ok(source.includes(`'${type}'`)) } assert.match(source, /await waitForCompletion\(context/) assert.match(source, /finalTask\?\.taskStatus === 'COMPLETED'/) assert.match(source, /inbound.summary\?\.processStatus === 'LOADED'/) }) test('shipping uses the current group and requires real departure and pickup readiness', () => { assert.match(source, /for \(const group of groups\) await progressShipping\(group\)/) assert.match(source, /containerBookingId: String\(record.loading.cabinet.id\)/) assert.match(source, /\['customs', completeCustoms\]/) assert.match(source, /\['departure', confirmDomesticDeparture\]/) assert.match(source, /\['pickup-ready', runUntilPickupReady\]/) assert.match(source, /confirmedStatus === 'WAITING_FOR_LOADING'/) assert.match(source, /phase4CarrierPickupReady\?\.ready === true/) }) test('overseas plans preserve per-inbound ownership and shared physical identity', () => { assert.match(source, /inbound-plans\/convert-container/) assert.match(source, /matching.length === 1/) assert.match(source, /JSON.stringify\(expectedNos\) === JSON.stringify\(actualNos\)/) assert.match(source, /byPallet.get\(pallet.palletNo\) === identity/) assert.match(source, /JSON.stringify\(replay\) === JSON.stringify\(plans\)/) }) test('overseas receipt uses scoped PDA identity and scans each physical pallet once', () => { assert.match(source, /await createPicker\(context/) assert.match(source, /await disablePicker\(context/) assert.match(source, /const sourceNos = \[\.\.\.new Set\(group.flatMap/) assert.match(source, /current\/receipts\/pallet/) assert.match(source, /duplicate.result === 'ALREADY_RECEIVED'/) assert.match(source, /Number\(inbound.receivedPackages\) === item.expectedCartonMarks.length/) assert.match(source, /completed.status === 'COMPLETED'/) assert.match(source, /fullFlowVerified: false/) })