import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import test from 'node:test' import { PALLET_LOCATION_SCENARIOS, inspectCrossScenarioIsolation, inspectScenarioReport, parseMatrixArguments, } from './pallet-location-test-env-acceptance.mjs' const source = await readFile(new URL('./pallet-location-test-env-acceptance.mjs', import.meta.url), 'utf8') test('pallet location matrix declares deterministic one, two, and three-image scenarios', () => { assert.equal(PALLET_LOCATION_SCENARIOS.length, 5) assert.deepEqual(PALLET_LOCATION_SCENARIOS.map((item) => item.expectedPalletCount), [1, 1, 1, 2, 3]) assert.match(source, /--modules', 'orders,inbound-measurement'/) assert.match(source, /--verify-pallet-location-guide', 'true'/) assert.match(source, /--wait-split-package-count', '18'/) assert.match(source, /--wait-split-package-count', '12'/) assert.match(source, /--route-mode', 'random', '--wait-split-random-value', '2'/) assert.match(source, /第3张确认后扩容只追加且既有槽身份不漂移/) assert.match(source, /beforeSlots=1, afterSlots=2/) assert.match(source, /beforeSlots=2, afterSlots=3/) }) test('matrix options select known scenarios without duplicate or unknown names', () => { const parsed = parseMatrixArguments([ '--origin-warehouse-name', '测试仓', '--scenarios', 'single-image-single-slot,two-image-incremental-growth', '--output=reports/custom.json', ]) assert.equal(parsed.originWarehouseName, '测试仓') assert.deepEqual(parsed.scenarioNames, ['single-image-single-slot', 'two-image-incremental-growth']) assert.equal(parsed.output, 'reports/custom.json') assert.throws(() => parseMatrixArguments(['--scenarios', 'unknown']), /未知场景/) assert.throws(() => parseMatrixArguments(['--scenarios', 'measured-volume-growth,measured-volume-growth']), /重复场景/) }) test('scenario inspection requires deletion release, refill reuse, WAIT_SPLIT, and guide identity', () => { const scenario = PALLET_LOCATION_SCENARIOS[0] const requiredNames = [ '删除只解绑图片且原 reservation/location 变为 RESERVED_EMPTY', '替代图片按新 resultId 复用原空槽', '固定客户第 1 单停在待分柜', '报告敏感信息扫描', ] const report = { success: true, marker: 'TEST', summary: { total: 10, failed: 0 }, createdResources: { inboundMeasurement: { inboundId: '11', measurementId: '12', palletCount: 1, processStatus: 'WAIT_SPLIT' }, inboundMeasurementLocationGuide: { inboundId: '11', blockId: '13', reservationId: '14', locationId: '15', deleteSucceeded: true, slotReused: true, }, }, steps: [ ...requiredNames.map((name) => ({ name, passed: true })), { name: '第1张确认后整票库位块计数与槽身份完整', passed: true, evidence: 'blockId=13, reserved=1, actual=1, empty=0, slots=1' }, ], } const inspected = inspectScenarioReport(scenario, report, 0) assert.equal(inspected.passed, true) report.steps.find((step) => step.name.startsWith('删除只解绑')).passed = false assert.equal(inspectScenarioReport(scenario, report, 0).passed, false) }) test('scenario inspection detects naturally observed shared A0 from evidence without coupling to step wording', () => { const scenario = PALLET_LOCATION_SCENARIOS[0] const report = { success: false, createdResources: {}, steps: [{ name: '正式数据 A0 共享溢出观测', passed: true, evidence: 'sharedOverflow=true, slots=1:reservation:A0' }], } assert.equal(inspectScenarioReport(scenario, report, 1).resources.sharedOverflowObserved, true) }) test('three-image scenario rejects a no-op growth assertion even when the base step passed', () => { const scenario = PALLET_LOCATION_SCENARIOS.at(-1) const report = { success: true, createdResources: { inboundMeasurement: { palletCount: 3, processStatus: 'WAIT_SPLIT' }, inboundMeasurementLocationGuide: { blockId: '1', reservationId: '2', locationId: '3', deleteSucceeded: true, slotReused: true, }, }, steps: [ { name: '删除只解绑图片且原 reservation/location 变为 RESERVED_EMPTY', passed: true }, { name: '替代图片按新 resultId 复用原空槽', passed: true }, { name: '固定客户第 1 单停在待分柜', passed: true }, { name: '报告敏感信息扫描', passed: true }, { name: '第2张确认后扩容只追加且既有槽身份不漂移', passed: true, evidence: 'beforeSlots=3, afterSlots=3' }, { name: '第3张确认后扩容只追加且既有槽身份不漂移', passed: true, evidence: 'beforeSlots=3, afterSlots=3' }, ], } const inspected = inspectScenarioReport(scenario, report, 0) assert.equal(inspected.assertions.requiredStepEvidence, false) assert.deepEqual(inspected.mismatchedStepEvidence, scenario.requiredSteps) }) test('cross-scenario inspection permits shared A0 location but rejects shared reservation or block', () => { const results = [ { resources: { inboundId: '1', blockId: '11', reservationId: '21', locationId: 'A0' } }, { resources: { inboundId: '2', blockId: '12', reservationId: '22', locationId: 'A0' } }, ] assert.equal(inspectCrossScenarioIsolation(results).passed, true) results[1].resources.reservationId = '21' assert.equal(inspectCrossScenarioIsolation(results).passed, false) }) test('matrix does not disable locations, execute SQL, deploy, or use a shell child process', () => { assert.doesNotMatch(source, /location-status|batch-disable|UPDATE\s+|DELETE\s+FROM|INSERT\s+INTO/i) assert.doesNotMatch(source, /jenkins|docker\s+compose|kubectl/i) assert.match(source, /shell: false/) assert.match(source, /A0 仅在普通库位自然不足时观测/) })