import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' const read = (relativeUrl) => readFileSync(new URL(relativeUrl, import.meta.url), 'utf8') const loadingPickService = read('../../firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java') const completionCommand = read('../../firstleg/src/main/java/com/baiyue/firstleg/entity/LoadingCompletionCommandEntity.java') const completionWorker = read('../../firstleg/src/main/java/com/baiyue/firstleg/service/LoadingCompletionWorker.java') const loadingPickTests = read('../../firstleg/src/test/java/com/baiyue/firstleg/service/LoadingPickServiceTest.java') const completionWorkerTests = read('../../firstleg/src/test/java/com/baiyue/firstleg/service/LoadingCompletionWorkerTest.java') const settlementTests = read('../../firstleg/src/test/java/com/baiyue/firstleg/service/PdaPickSettlementServiceTest.java') const unifiedSql = read('../../deploy/sql/头程数据库统一增量-MySQL8.sql') const finalizeHttp = read('phase3-loading-finalize.mjs') const pdaPage = read('../../../apps/pda-uniapp/pages/picking/picking.vue') const pdaCompletionState = read('../../../apps/pda-uniapp/utils/loading-completion-state.js') const pdaCompletionTests = read('../../../apps/pda-uniapp/tests/loading-completion-state.test.mjs') function retryDecision({ operationStatus, sameRequest, finalTaskStatus }) { if (!sameRequest) return 'CONFLICT' if (operationStatus === 'PROCESSING') return 'PROCESSING' if (operationStatus === 'COMPLETED') return 'REPLAY' if (finalTaskStatus === 'COMPLETED') return 'RECONCILE_COMPLETE' return 'RESUME' } test('offline orchestration model distinguishes processing replay recovery and conflict', () => { assert.equal(retryDecision({ operationStatus: 'PROCESSING', sameRequest: true }), 'PROCESSING') assert.equal(retryDecision({ operationStatus: 'COMPLETED', sameRequest: true }), 'REPLAY') assert.equal(retryDecision({ operationStatus: 'FAILED', sameRequest: true, finalTaskStatus: 'LOADING' }), 'RESUME') assert.equal(retryDecision({ operationStatus: 'FAILED', sameRequest: false }), 'CONFLICT') }) test('POST complete is reliable acceptance only and returns PROCESSING/COMPLETING', () => { const start = loadingPickService.indexOf('public Task complete(') const end = loadingPickService.indexOf('/** Called only by the completion worker', start) const block = loadingPickService.slice(start, end) assert.match(block, /LoadingCompletionCommandEntity/) assert.match(block, /acceptCompletion/) assert.match(block, /return view\(requiredTask\(taskId\), null\)/) assert.doesNotMatch(block, /settlementService|completeAfterPdaSettlement|publishCompletedEvent/) assert.match(loadingPickService, /"COMPLETING"\.equals\(task\.getTaskStatus\(\)\)[\s\S]{0,80}"PROCESSING"/) }) test('complete does not hold fl_loading_pick_task FOR UPDATE across remote settlement calls', () => { const start = loadingPickService.indexOf('public void processCompletion(') const end = loadingPickService.indexOf('private static void requireSameCompletionCommand', start) const block = loadingPickService.slice(start, end) assert.notEqual(start, -1) assert.match(block, /settlementService\.settleOwner/) assert.doesNotMatch(block, /requiredLoadingTaskForUpdate|requiredTaskForUpdate/) assert.match(loadingPickService, /completionCommands\.insert/) }) test('completion operation has durable PROCESSING and terminal response state', () => { assert.match(completionCommand, /commandStatus/) assert.match(completionCommand, /attemptCount/) assert.match(completionCommand, /nextRetryAt/) assert.match(completionCommand, /completedAt/) assert.match(completionCommand, /failureStage/) const commandTable = unifiedSql.slice(unifiedSql.indexOf('CREATE TABLE IF NOT EXISTS fl_loading_completion_command'), unifiedSql.indexOf(';', unifiedSql.indexOf('CREATE TABLE IF NOT EXISTS fl_loading_completion_command')) + 1) assert.match(commandTable, /command_status/i) assert.match(commandTable, /next_retry_at/i) assert.match(commandTable, /completed_at/i) assert.match(unifiedSql, /CREATE TABLE IF NOT EXISTS fl_loading_completion_checkpoint/) }) test('durable worker processes inbound settlements incrementally with retry backoff', () => { assert.match(completionWorker, /@Scheduled/) assert.match(completionWorker, /commands\.due/) assert.match(completionWorker, /commands\.claim/) assert.match(completionWorker, /15L <<|nextRetryAt|retryAt/) assert.match(completionWorker, /"FAILED"\s*:\s*"RETRYABLE"/) assert.match(loadingPickService, /checkpointAtLeast[\s\S]{0,1800}SETTLED/) assert.match(completionWorkerTests, /transientFailureIsReleasedForBackoffRetry/) assert.match(completionWorkerTests, /"RETRYABLE"/) assert.match(loadingPickTests, /FAILED[\s\S]{0,1500}(?:same operationId|同operationId|resume|恢复)/i) }) test('same operationId concurrency returns PROCESSING promptly instead of waiting for the task row lock', () => { assert.match(loadingPickTests, /sameOperationId[\s\S]{0,1600}PROCESSING/i) assert.match(loadingPickTests, /(?:assertTimeout|timeout|elapsed|millis|seconds)[\s\S]{0,500}(?:CannotAcquireLock|FOR UPDATE|taskForUpdate)/i) assert.match(loadingPickTests, /never\(\)[\s\S]{0,180}requiredLoadingTaskForUpdate|never\(\)[\s\S]{0,180}taskForUpdate/i) }) test('a different operationId is rejected while the completion command is processing', () => { assert.match(loadingPickTests, /(?:different operationId|不同operationId|different key|不同键)[\s\S]{0,1200}(?:A0409|CONFLICT)/i) }) test('fifteen completed Warehouse settlements can be reconciled after Firstleg rollback', () => { assert.match(loadingPickTests, /workerProcessesFifteenInboundOwnersInStableOrderAndFinalizesOnce/) assert.match(loadingPickTests, /IntStream\.range\(0, 15\)/) assert.match(settlementTests, /repeatedSettleWithExistingChildDoesNotConsumeOrTransferAgain/) assert.match(settlementTests, /splits\.appliedBatch\(10001L, "PDA-SPLIT-1-31"\)/) assert.match(settlementTests, /verify\(warehouse, never\(\)\)\.transferBlock/) assert.match(settlementTests, /verify\(warehouse, never\(\)\)\.releaseBlock/) assert.match(loadingPickTests, /workerRestartResumesFromOwnerCheckpointsWithoutRepeatingSettlements/) }) test('recovery contract is fixture-independent and uses durable checkpoints', () => { assert.doesNotMatch(finalizeHttp, /2092817774453780481/) assert.match(loadingPickService, /checkpointAtLeast\(checkpoint, "SETTLED"\)/) assert.match(loadingPickService, /checkpointAtLeast\(checkpoint, "INBOUND_COMPLETED"\)/) assert.match(loadingPickTests, /workerRestartResumesFromOwnerCheckpointsWithoutRepeatingSettlements/) }) test('final acceptance reconciles terminal task and one completion audit after owner checkpoints', () => { for (const fact of ['task', 'plan', 'inbound', 'operation']) { assert.match(finalizeHttp, new RegExp(fact, 'i'), `missing final ${fact} reconciliation`) } assert.match(finalizeHttp, /COMPLETE_LOADING/) assert.match(finalizeHttp, /concurrentCompleteOperations\.length === 1/) assert.match(finalizeHttp, /异步完成命令收敛到终态/) assert.match(loadingPickService, /completionTransaction\.executeWithoutResult/) assert.match(loadingPickService, /private void finalizeCompletion/) }) test('PDA treats accepted completion as processing, returns to list, and never claims completion early', () => { assert.match(pdaCompletionState, /PROCESSING_STATUSES = new Set\(\['COMPLETING', 'PROCESSING'\]\)/) assert.match(pdaCompletionTests, /accepted completion is processing/) assert.match(pdaPage, /loading-completion-state/) assert.match(pdaPage, /(?:PROCESSING|COMPLETING)[\s\S]{0,1200}navigateBackOr/) assert.doesNotMatch(pdaPage, /completeContainerPickingTask[\s\S]{0,700}showToast\('已确认装柜完成'/, 'accepted PROCESSING response must not show a completed success toast') assert.match(pdaPage, /shouldReconcileCompletion/) })