feat(test): continue reviewed return source sample with pre-signature audit
This commit is contained in:
@@ -13,7 +13,7 @@ import { verifyFleetExternalReceipt } from './admin-test-env-modules/fleet-exter
|
||||
import { verifyFleetExternalDispatch } from './admin-test-env-modules/fleet-external-dispatch-acceptance.mjs'
|
||||
import { verifyFleetHandover, cleanupFleetHandoverResources } from './admin-test-env-modules/fleet-handover-acceptance.mjs'
|
||||
import { verifyFleetTripCosts } from './admin-test-env-modules/fleet-trip-cost-acceptance.mjs'
|
||||
import { verifyFleetReturnRedispatch } from './admin-test-env-modules/fleet-return-redispatch-acceptance.mjs'
|
||||
import { verifyFleetReturnRedispatch, waitForRedispatchSignatureAudit, selectReturnSources } from './admin-test-env-modules/fleet-return-redispatch-acceptance.mjs'
|
||||
import { resolvePickerAccess, createPicker, disablePicker } from './admin-test-env-modules/phase4-overseas-pda.mjs'
|
||||
import { pickAll, saveReceiptAndComplete } from './admin-test-env-modules/phase5-overseas-fulfillment.mjs'
|
||||
|
||||
@@ -154,11 +154,12 @@ export function parseFleetArguments(args) {
|
||||
}
|
||||
if (config.environment !== 'TEST' || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/.test(config.runId)
|
||||
|| (config.execute && (!args.includes('--environment') || config.runId === 'plan'))) throw Error('Explicit TEST and fresh run-id required')
|
||||
if (Boolean(config.resumeReport) !== Boolean(config.resumeStage) || (config.resumeStage && !['FULL_COVERAGE', 'FULL_COVERAGE_PERSISTED', 'EXTERNAL_RECEIPT_READY', 'EXTERNAL_PALLETIZED', 'EXTERNAL_RECEIPT_CONFIRMED', 'TERMINAL_READONLY', 'DEPARTED_BEFORE_COSTS', 'DEPARTED_AFTER_COSTS_BEFORE_HANDOVER', 'DEPARTED_VEHICLE_CREATED'].includes(config.resumeStage))) throw Error('Explicit reviewed checkpoint required')
|
||||
if (config.resumeStage?.startsWith('DEPARTED_') ? !/^[a-f0-9]{64}$/.test(config.resumeSha256 || '') : config.resumeSha256)
|
||||
if (Boolean(config.resumeReport) !== Boolean(config.resumeStage) || (config.resumeStage && !['FULL_COVERAGE', 'FULL_COVERAGE_PERSISTED', 'EXTERNAL_RECEIPT_READY', 'EXTERNAL_PALLETIZED', 'EXTERNAL_RECEIPT_CONFIRMED', 'TERMINAL_READONLY', 'DEPARTED_BEFORE_COSTS', 'DEPARTED_AFTER_COSTS_BEFORE_HANDOVER', 'DEPARTED_VEHICLE_CREATED', 'RETURN_SOURCE_SELECTION_BEFORE_RETURN'].includes(config.resumeStage))) throw Error('Explicit reviewed checkpoint required')
|
||||
if (config.resumeStage?.startsWith('DEPARTED_') || config.resumeStage === 'RETURN_SOURCE_SELECTION_BEFORE_RETURN' ? !/^[a-f0-9]{64}$/.test(config.resumeSha256 || '') : config.resumeSha256)
|
||||
throw Error('Departed recovery requires the exact reviewed report SHA256')
|
||||
if (config.outputDir && !/^reports\/[A-Za-z0-9][A-Za-z0-9_-]{0,95}$/.test(config.outputDir)) throw Error('Output must be a fresh named directory directly under reports')
|
||||
if (!['handover', 'return-redispatch'].includes(config.scenario) || (config.scenario === 'return-redispatch' && config.resumeReport))
|
||||
if (!['handover', 'return-redispatch'].includes(config.scenario) || (config.scenario === 'return-redispatch' && config.resumeReport && config.resumeStage !== 'RETURN_SOURCE_SELECTION_BEFORE_RETURN')
|
||||
|| (config.resumeStage === 'RETURN_SOURCE_SELECTION_BEFORE_RETURN' && config.scenario !== 'return-redispatch'))
|
||||
throw Error('Return redispatch only accepts a fresh run, never a reviewed old checkpoint')
|
||||
return config
|
||||
}
|
||||
@@ -169,7 +170,12 @@ export function fleetExecutionPlan(config) {
|
||||
stages: ['A actual return 2', 'original remaining signatures; A incomplete and B complete', 'RETURN_RECEIPT draft lock and pre-arrange', 'new PDA picks and DEPART 2', 'redispatch signature; A complete', 'source event/ACK read-only evidence remains required'] } : null,
|
||||
outputDir: config.outputDir || `reports/fleet-${config.runId}`, producer: config.resumeReport ? `Reviewed ${config.resumeStage} checkpoint in the same original fixture` : 'phase4-overseas-dispatch with its normal fresh prerequisites',
|
||||
resumeReport: config.resumeReport || null, resumeStage: config.resumeStage || null, resumeSha256: config.resumeSha256 || null,
|
||||
stages: config.resumeStage?.startsWith('DEPARTED_')
|
||||
stages: config.resumeStage === 'RETURN_SOURCE_SELECTION_BEFORE_RETURN'
|
||||
? ['Verify exact source-selection-failure report SHA256 and three definitive delivery starts', 'Read-only original DEPART/handover/cost/cargo/resources and DELIVERING versions',
|
||||
'Recheck repaired A/B return candidates and complete source quantities before any write', 'Create fresh bound PDA session',
|
||||
'Original sample actual return and redispatch; no repeated original departure/handover/cost/start-delivery',
|
||||
'Wait for coordinator pre-signature A0/B1 event evidence gate', 'New signature and source ACK evidence; close new and original resources']
|
||||
: config.resumeStage?.startsWith('DEPARTED_')
|
||||
? ['Verify reviewed report SHA256', afterCosts
|
||||
? 'Read-only exact trip/version/DEPART/cargo, unchanged verified costs, empty handovers and unsigned deliveries'
|
||||
: 'Read-only exact original trip/version/DEPART/cargo and empty costs/handovers/unsigned deliveries',
|
||||
@@ -189,6 +195,30 @@ export function fleetExecutionPlan(config) {
|
||||
export function reviewedFullCheckpoint(report, stage = 'FULL_COVERAGE') {
|
||||
const resources = report?.createdResources, proof = resources?.fleetAcceptance, full = resources?.fleetFeeCoverageCases?.[0]
|
||||
const trip = resources?.fleetTrips?.find(row => row.tripNo === proof?.tripNo)
|
||||
if (stage === 'RETURN_SOURCE_SELECTION_BEFORE_RETURN') {
|
||||
const attempt = trip?.returnRedispatch, originals = attempt?.originalDeliveries, writes = attempt?.requests?.filter(row => row.method !== 'GET')
|
||||
const failures = report?.steps?.filter(row => row.passed === false)
|
||||
if (report?.success !== false || proof?.stage !== 'RETURN_REDISPATCH' || proof.status !== 'CHECKING'
|
||||
|| proof.marker !== attempt?.marker || trip?.costAcceptance?.status !== 'VERIFIED' || trip.handover?.status !== 'VERIFIED'
|
||||
|| proof.departure?.status !== 'IN_TRANSIT' || proof.departure.tripNo !== trip.tripNo
|
||||
|| trip.closure || proof.closure || proof.receipts?.length || attempt.status !== 'FAILED'
|
||||
|| attempt.failure !== 'Fresh redispatch fixture requires source A with at least 3 boxes and a separate domestic source B'
|
||||
|| attempt.exceptionNo || attempt.sources || attempt.newTripNo || attempt.originalSignatures
|
||||
|| originals?.length !== 3 || new Set(originals.map(row => row.deliveryNo)).size !== 3 || writes?.length !== 3
|
||||
|| originals.some(row => id(row.customerId) !== id(proof.customerId) || id(row.overseasWarehouseId) !== id(proof.warehouseId)
|
||||
|| !proof.dispatchNos.includes(row.dispatchNo) || row.status !== 'WAIT_DELIVERY' || row.signedAt || Number(row.signedPackages || 0))
|
||||
|| attempt.requests.some(row => row.status !== 'RESPONDED' || row.http !== 200 || id(row.code) !== '200' || !row.requestId || !row.traceId)
|
||||
|| writes.some(row => {
|
||||
const original = originals.find(item => item.deliveryNo === row.result?.deliveryNo)
|
||||
return !original || row.method !== 'PUT' || row.path !== `/api/platform/v1/last-leg/delivery-orders/${original.deliveryNo}/status?customerId=${proof.customerId}`
|
||||
|| row.body?.status !== 'DELIVERING' || id(row.body.expectedVersion) !== id(original.version)
|
||||
|| row.result.deliveryStatus !== 'DELIVERING' || row.result.replayed !== false
|
||||
}) || new Set(writes.map(row => row.result?.deliveryNo)).size !== 3
|
||||
|| resources.fleetCostActors?.status !== 'DISABLED' || failures?.length !== 1 || failures[0].name !== '模块执行'
|
||||
|| failures[0].evidence !== attempt.failure)
|
||||
throw Error('Return source recovery requires the exact known candidate failure and three definitive delivery starts, with no return/signature writes')
|
||||
return { resources, marker: proof.marker, returnSourceSelection: true }
|
||||
}
|
||||
if (stage === 'DEPARTED_BEFORE_COSTS' || stage === 'DEPARTED_AFTER_COSTS_BEFORE_HANDOVER' || stage === 'DEPARTED_VEHICLE_CREATED') {
|
||||
const vehicleCreated = stage === 'DEPARTED_VEHICLE_CREATED'
|
||||
const afterCosts = stage !== 'DEPARTED_BEFORE_COSTS'
|
||||
@@ -321,10 +351,11 @@ export async function verifyFleetTerminalState(context) {
|
||||
}
|
||||
|
||||
/** All business checks happen before creating replacement test sessions or issuing a business command. */
|
||||
export async function verifyDepartedRecovery(context, { costsAlreadyVerified = false, createdVehicle } = {}) {
|
||||
export async function verifyDepartedRecovery(context, { costsAlreadyVerified = false, createdVehicle, returnSourceReady = false } = {}) {
|
||||
const proof = context.createdResources.fleetAcceptance
|
||||
const trip = context.createdResources.fleetTrips.find(row => row.tripNo === proof.tripNo)
|
||||
const original = trip.costAcceptance.departure, snapshot = original.snapshot, load = snapshot.confirmedLoad
|
||||
const expectedTrip = returnSourceReady ? trip.handover.finalTrip : snapshot.trip
|
||||
const base = `/api/platform/v1/last-leg/fleet/trips/${encodeURIComponent(trip.tripNo)}`
|
||||
const evidence = { status: 'CHECKING', tripNo: trip.tripNo, requests: [], dispatchDetails: [], deliveries: [] }
|
||||
context.createdResources.fleetDepartureRecoveryVerification = evidence
|
||||
@@ -337,20 +368,43 @@ export async function verifyDepartedRecovery(context, { costsAlreadyVerified = f
|
||||
return r.body.data
|
||||
}
|
||||
const current = await get(base)
|
||||
check(current.trip?.status === 'IN_TRANSIT' && id(current.trip.recordVersion) === id(snapshot.trip.recordVersion)
|
||||
check(current.trip?.status === 'IN_TRANSIT' && id(current.trip.recordVersion) === id(expectedTrip.recordVersion)
|
||||
&& current.trip.departedAt === proof.departure.departedAt && id(current.trip.warehouseId) === id(proof.warehouseId)
|
||||
&& id(current.trip.vehicleId) === id(snapshot.trip.vehicleId) && id(current.trip.driverId) === id(snapshot.trip.driverId)
|
||||
&& id(current.trip.vehicleId) === id(expectedTrip.vehicleId) && id(current.trip.driverId) === id(expectedTrip.driverId)
|
||||
&& !current.trip.vehicleRelease && !current.trip.driverRelease, 'trip version, resources or departure changed')
|
||||
const history = await get(`${base}/history?page=1&size=100`)
|
||||
const departures = history.records?.filter(row => row.action === 'DEPART')
|
||||
check(Array.isArray(history.records) && Number(history.total) === history.records.length
|
||||
&& departures?.length === 1 && same(departures[0], original)
|
||||
&& history.records.every(row => BigInt(row.recordVersion) <= BigInt(original.recordVersion)), 'immutable DEPART/history changed')
|
||||
&& history.records.every(row => BigInt(row.recordVersion) <= BigInt(expectedTrip.recordVersion)), 'immutable DEPART/history changed')
|
||||
if (returnSourceReady) {
|
||||
const expectedHistory = trip.handover.requests.filter(row => row.method === 'GET' && row.path.startsWith(`${base}/history?`)).at(-1)?.result
|
||||
check(expectedHistory && same(history.records, expectedHistory.records), 'verified handover history changed')
|
||||
check(typeof context.context?.readRecoveryTripResources === 'function', 'current resource occupancy reader missing')
|
||||
const bindings = await context.context.readRecoveryTripResources(trip.tripNo)
|
||||
check(bindings.length === 2 && bindings.every(row => row.tripNo === trip.tripNo)
|
||||
&& bindings.some(row => row.resourceKind === 'VEHICLE' && id(row.resourceId) === id(expectedTrip.vehicleId))
|
||||
&& bindings.some(row => row.resourceKind === 'DRIVER' && id(row.resourceId) === id(expectedTrip.driverId)), 'current resources no longer belong to the original trip')
|
||||
evidence.resourceBindings = bindings
|
||||
const vehicleSnapshot = trip.handover.requests.find(row => row.method === 'POST'
|
||||
&& row.path === '/api/platform/v1/last-leg/master-data/vehicles' && id(row.result?.id) === id(expectedTrip.vehicleId))?.result
|
||||
const driverSnapshot = trip.handover.requests.find(row => row.method === 'POST'
|
||||
&& row.path === `/api/platform/v1/last-leg/fleet/drivers/${expectedTrip.driverId}/review`)?.result
|
||||
check(vehicleSnapshot && driverSnapshot, 'verified current resource snapshots missing')
|
||||
const vehicle = await get(`/api/platform/v1/last-leg/master-data/vehicles/${expectedTrip.vehicleId}`)
|
||||
const driver = await get(`/api/platform/v1/last-leg/fleet/drivers/${expectedTrip.driverId}`)
|
||||
check(same(vehicle, vehicleSnapshot), 'current vehicle snapshot changed')
|
||||
check(['id', 'recordVersion', 'active', 'reviewStatus', 'licenseFileId', 'details'].every(field => same(driver[field], driverSnapshot[field])), 'current driver identity/version/qualification facts changed')
|
||||
evidence.currentVehicle = vehicle; evidence.currentDriver = driver
|
||||
}
|
||||
const costs = await get(`${base}/costs?page=1&size=100`), handovers = await get(`${base}/handovers`)
|
||||
check((costsAlreadyVerified ? same(costs, trip.costAcceptance.final)
|
||||
: costs.page?.records?.length === 0 && Number(costs.page.total) === 0 && costs.summary?.totals?.length === 0)
|
||||
&& costs.summary.manifestHash === load.manifestHash && id(costs.summary.departureVersion) === id(original.recordVersion), 'costs already written or cargo basis changed')
|
||||
check(handovers.records?.length === 0 && Number(handovers.total) === 0, 'handover already exists')
|
||||
if (returnSourceReady) {
|
||||
const expected = trip.handover.requests.filter(row => row.method === 'GET' && row.path.startsWith(`${base}/handovers?`)).at(-1)?.result
|
||||
check(expected && Number(handovers.total) === 1 && same(handovers.records, expected.records), 'original verified handover changed')
|
||||
} else check(handovers.records?.length === 0 && Number(handovers.total) === 0, 'handover already exists')
|
||||
check(same(current.dispatches.map(row => row.dispatchNo).sort(), [...proof.dispatchNos].sort())
|
||||
&& current.dispatches.every(row => id(row.customerId) === id(proof.customerId)), 'trip dispatch binding changed')
|
||||
for (const number of proof.dispatchNos) {
|
||||
@@ -369,8 +423,12 @@ export async function verifyDepartedRecovery(context, { costsAlreadyVerified = f
|
||||
for (const number of deliveryNos) {
|
||||
const detail = await get(`/api/platform/v1/last-leg/delivery-orders/${encodeURIComponent(number)}?customerId=${encodeURIComponent(proof.customerId)}`)
|
||||
const order = detail.order
|
||||
const started = returnSourceReady ? trip.returnRedispatch.requests.find(row => row.method === 'PUT' && row.result?.deliveryNo === number) : null
|
||||
check(order?.deliveryNo === number && id(order.customerId) === id(proof.customerId) && proof.dispatchNos.includes(order.dispatchNo)
|
||||
&& order.status === 'WAIT_DELIVERY' && id(order.version) === '1' && !order.signedAt && !Number(order.signedPackages || 0)
|
||||
&& order.status === (returnSourceReady ? 'DELIVERING' : 'WAIT_DELIVERY')
|
||||
&& id(order.version) === (returnSourceReady ? String(Number(started?.body.expectedVersion) + 1) : '1')
|
||||
&& (!returnSourceReady || order.vehiclePlate === expectedTrip.vehiclePlate && order.driverName === expectedTrip.driverName && order.driverPhone === expectedTrip.driverPhone)
|
||||
&& !order.signedAt && !Number(order.signedPackages || 0)
|
||||
&& !order.completedAt && order.documentSummary?.categories?.every(row => Number(row.fileCount) === 0), 'delivery has started, changed or been signed')
|
||||
evidence.deliveries.push(order)
|
||||
}
|
||||
@@ -409,6 +467,74 @@ export async function readReviewedVehicleOccupation(env, vehicleId, fetcher = gl
|
||||
return result.rows[0]
|
||||
}
|
||||
|
||||
export async function readRecoveryTripResources(env, tripNo, fetcher = globalThis.fetch) {
|
||||
validateEvidenceConfig(env)
|
||||
if (!/^[A-Za-z0-9_-]{1,128}$/.test(tripNo)) throw Error('Exact recovery trip number required')
|
||||
const sql = ['SET NAMES utf8mb4 COLLATE utf8mb4_bin;', 'SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;',
|
||||
'START TRANSACTION WITH CONSISTENT SNAPSHOT, READ ONLY;',
|
||||
`SELECT JSON_OBJECT('resourceKind',resource_kind,'resourceId',CAST(resource_id AS CHAR),'tripNo',trip_no) FROM ll_fleet_resource_assignment WHERE trip_no='${tripNo}' ORDER BY resource_kind;`, 'COMMIT;'].join('\n')
|
||||
const script = buildEvidenceRemoteScript('lastleg', sql, env.FBA_CORE_JENKINS_AGENT)
|
||||
const response = await fetcher(new URL('scriptText', env.JENKINS_URL.replace(/\/?$/, '/')), { method: 'POST',
|
||||
headers: { Authorization: 'Basic ' + Buffer.from(env.JENKINS_USER + ':' + env.JENKINS_API_TOKEN).toString('base64'), 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({ script }), signal: AbortSignal.timeout(55000) })
|
||||
if (!response.ok) throw Error('Recovery resources readonly HTTP failure')
|
||||
const lines = (await response.text()).split(/\r?\n/).filter(line => line.startsWith('FBA_CORE_JSON='))
|
||||
if (lines.length !== 1) throw Error('Recovery resource evidence ambiguous')
|
||||
const result = JSON.parse(lines[0].slice('FBA_CORE_JSON='.length))
|
||||
if (result.ok !== true || !Array.isArray(result.rows)) throw Error('Recovery resource query failed')
|
||||
return result.rows
|
||||
}
|
||||
|
||||
export async function verifyReturnSourceRecovery(context, { verifyOriginal = verifyDepartedRecovery } = {}) {
|
||||
const original = await verifyOriginal(context, { costsAlreadyVerified: true, returnSourceReady: true })
|
||||
const run = context.createdResources.fleetAcceptance, rows = []
|
||||
const previous = original.trip.returnRedispatch
|
||||
for (const delivery of previous.originalDeliveries) {
|
||||
const response = await context.request('GET', `/api/platform/v1/last-leg/delivery-orders/${delivery.deliveryNo}/return-inbound-options?customerId=${run.customerId}`)
|
||||
if (!success(response) || !response.requestId || !response.traceId || !Array.isArray(response.body.data)) throw Error('Repaired return candidates must be definitive')
|
||||
for (const relation of response.body.data) {
|
||||
const tail = await context.request('GET', `/api/platform/v1/last-leg/orders/${relation.lastMileOrderId}?customerId=${run.customerId}`)
|
||||
if (!success(tail) || !tail.requestId || !tail.traceId || relation.deliveryNo !== delivery.deliveryNo
|
||||
|| relation.outboundNo !== delivery.outboundNo || id(tail.body.data?.order?.customerId) !== id(run.customerId)
|
||||
|| id(tail.body.data.order.overseasWarehouseId) !== id(run.warehouseId)
|
||||
|| id(tail.body.data.order.id) !== id(relation.lastMileOrderId)) throw Error('Repaired source owner or delivery identity mismatch')
|
||||
rows.push({ relation, tail: tail.body.data.order, delivery })
|
||||
}
|
||||
}
|
||||
const selected = selectReturnSources(rows)
|
||||
context.createdResources.fleetDepartureRecoveryVerification.returnSourceSelection = selected
|
||||
await context.checkpoint()
|
||||
return original
|
||||
}
|
||||
|
||||
export async function continueReviewedReturnSources(context, { verify = verifyReturnSourceRecovery,
|
||||
resolveAccess = resolvePickerAccess, makePicker = createPicker, closePicker = disablePicker,
|
||||
redispatch = verifyFleetReturnRedispatch, closeOriginal = finishFleetTrip, cleanup = cleanupFleetHandoverResources } = {}) {
|
||||
const verified = await verify(context), trip = verified.trip, run = context.createdResources.fleetAcceptance
|
||||
trip.previousSourceSelectionAttempts = [trip.returnRedispatch]; delete trip.returnRedispatch
|
||||
run.previousPicker = run.picker; delete run.picker
|
||||
run.pickerMarker = `${context.marker}-RETURN-RECOVERY-${randomUUID().slice(0, 8)}`
|
||||
await context.checkpoint()
|
||||
let picker
|
||||
try {
|
||||
const child = Object.create(context); child.marker = run.pickerMarker
|
||||
const access = await resolveAccess(context)
|
||||
picker = access ? await makePicker(child, 71, run.warehouseId, access) : null
|
||||
if (!picker?.token || !picker.membershipId) throw Error('Source recovery requires a fresh bound PDA session')
|
||||
run.picker = { membershipId: picker.membershipId, username: picker.username }; await context.checkpoint()
|
||||
const partial = context.createdResources.fleetFeeCoverageCases.find(row => row.mode === 'PARTIAL')
|
||||
const result = await redispatch(context, MODULE, trip, { customerId: run.customerId, warehouseId: run.warehouseId,
|
||||
pickerToken: picker.token, dispatchNos: run.dispatchNos, deliveryNos: [...partial.proof.deliveryNos, trip.externalDispatch.deliveryNo], sourceSelectionRecovery: true })
|
||||
run.receipts = result.originalSignatures.map(row => ({ deliveryNo: row.deliveryNo, signedAt: row.signedAt }))
|
||||
context.context.saveFleetReturnRedispatchFixture(result)
|
||||
run.closure = await closeOriginal(context, MODULE, trip)
|
||||
if (run.closure?.status !== 'CARGO_COMPLETED_AND_RESOURCES_RELEASED') throw Error('Recovered original trip did not close')
|
||||
await cleanup(context, MODULE, trip)
|
||||
run.stage = 'FINISHED'; run.status = 'HTTP_CHAIN_VERIFIED_WITH_ACK_GAP'; await context.checkpoint()
|
||||
return run
|
||||
} finally { await closePicker(context, picker, 71) }
|
||||
}
|
||||
|
||||
export async function runFleetHandoverStage(context, trip, pickerToken, dispatchDetails, { verify = verifyFleetHandover, onReady, reviewedVehicle } = {}) {
|
||||
const state = await verify(context, MODULE, trip, pickerToken, dispatchDetails, { reviewedVehicle })
|
||||
const run = context.createdResources.fleetAcceptance
|
||||
@@ -802,7 +928,7 @@ export async function runFleetAcceptance(config, { env = process.env, readers, c
|
||||
const previous = resolve(root, config.resumeReport), previousRelative = relative(resolve(root, 'reports'), previous)
|
||||
if (!previousRelative || previousRelative.startsWith('..') || isAbsolute(previousRelative)) throw Error('Resume report must be inside this TEST repository reports')
|
||||
const bytes = readFileSync(previous)
|
||||
if (config.resumeStage?.startsWith('DEPARTED_') && createHash('sha256').update(bytes).digest('hex') !== config.resumeSha256)
|
||||
if ((config.resumeStage?.startsWith('DEPARTED_') || config.resumeStage === 'RETURN_SOURCE_SELECTION_BEFORE_RETURN') && createHash('sha256').update(bytes).digest('hex') !== config.resumeSha256)
|
||||
throw Error('Reviewed departure report SHA256 mismatch; no environment request sent')
|
||||
resumed = reviewedFullCheckpoint(JSON.parse(bytes.toString('utf8')), config.resumeStage)
|
||||
save('reviewed-checkpoint.json', { source: config.resumeReport, sha256: createHash('sha256').update(bytes).digest('hex'), stage: config.resumeStage })
|
||||
@@ -826,6 +952,9 @@ export async function runFleetAcceptance(config, { env = process.env, readers, c
|
||||
context.context.fleetRunId = config.runId
|
||||
context.context.fleetScenario = config.scenario || 'handover'
|
||||
context.context.readReviewedVehicleOccupation = vehicleId => readReviewedVehicleOccupation(env, vehicleId)
|
||||
context.context.readRecoveryTripResources = tripNo => readRecoveryTripResources(env, tripNo)
|
||||
context.context.onBeforeRedispatchSignature = proof => waitForRedispatchSignatureAudit(context, proof,
|
||||
{ root, output, secrets: [...secrets, context.token, context.pdaToken, context.refreshToken] })
|
||||
context.context.saveFleetHandoverFixture = fixture => save('handover-fixture.json', fixture)
|
||||
context.context.saveFleetReturnRedispatchFixture = fixture => save('return-redispatch-fixture.json', fixture)
|
||||
let handoverCheckpoint = 0
|
||||
@@ -839,6 +968,7 @@ export async function runFleetAcceptance(config, { env = process.env, readers, c
|
||||
}
|
||||
context.checkpoint = () => context.context.checkpointFleetHandover(context.createdResources.fleetAcceptance)
|
||||
const fleetScenario = async (ctx, options = {}) => {
|
||||
if (options.returnSourceSelection) return continueReviewedReturnSources(ctx)
|
||||
if (options.resumeDeparted) return continueReviewedDeparture(ctx, { prepareActors: prepareCostActors,
|
||||
costsAlreadyVerified: options.costsAlreadyVerified, createdVehicle: options.createdVehicle })
|
||||
const costActors = options.terminalReadOnly || scenario !== runFleetScenario ? undefined : await prepareCostActors?.(ctx)
|
||||
@@ -867,6 +997,7 @@ export async function runFleetAcceptance(config, { env = process.env, readers, c
|
||||
context.token = login.body.data.accessToken; context.refreshToken = login.body.data.refreshToken || ''
|
||||
await context.executeModule(MODULE, () => fleetScenario(context, { resumeFull: !resumed.receiptReady,
|
||||
resumeDeparted: resumed.departed,
|
||||
returnSourceSelection: resumed.returnSourceSelection,
|
||||
costsAlreadyVerified: resumed.costsAlreadyVerified,
|
||||
createdVehicle: resumed.createdVehicle,
|
||||
resumePersisted: resumed.persisted, resumeReceiptReady: resumed.receiptReady, resumePalletized: resumed.palletized, resumeReceiptConfirmed: resumed.receiptConfirmed, terminalReadOnly: resumed.terminal }))
|
||||
|
||||
Reference in New Issue
Block a user