fix(fleet): reuse reviewed handover vehicle and check formal responses

This commit is contained in:
2026-10-03 21:00:07 +08:00
parent 99a469e6cf
commit 18b39b4378
5 changed files with 226 additions and 48 deletions
+46 -1
View File
@@ -38,6 +38,51 @@ function departedRecoveryFixture() {
return { report, ctx, calls, responses, base, trip }
}
function createdVehicleRecoveryFixture() {
const f = afterCostsRecoveryFixture(), snapshot = { id: '2106360046045675522', plateNumber: 'HO-24255909-3BC', recordVersion: '0',
status: 'IN_SERVICE', ownership: 'OWNED', region: 'OVERSEAS', maxPalletSpaces: 30, maxLoadKg: 18000, maxVolumeCbm: 90 }
f.report.steps[0].name = '接替车辆返回真实身份及车牌'
const request = { method: 'POST', path: '/api/platform/v1/last-leg/master-data/vehicles', body: { plateNumber: 'HO-24255909-3bc' },
status: 'RESPONDED', http: 200, code: '200', requestId: 'created-r', traceId: 'created-t', result: snapshot }
f.trip.handover.requests.push(request)
f.responses.set('/api/platform/v1/last-leg/master-data/vehicles/' + snapshot.id, structuredClone(snapshot))
f.ctx.context.readReviewedVehicleOccupation = async vehicleId => ({ vehicleId, assignmentCount: 0 })
return { ...f, snapshot, request }
}
test('created-vehicle checkpoint requires exact successful uppercase response and no subsequent writes', () => {
const { report } = createdVehicleRecoveryFixture()
const accepted = reviewedFullCheckpoint(report, 'DEPARTED_VEHICLE_CREATED')
assert.equal(accepted.createdVehicle.snapshot.plateNumber, 'HO-24255909-3BC')
assert.equal(accepted.createdVehicle.creationRequest.body.plateNumber, 'HO-24255909-3bc')
for (const mutation of [f => f.request.http = 0, f => f.request.status = 'UNKNOWN_RESULT', f => f.request.code = 'A0409',
f => f.request.traceId = null, f => f.trip.handover.requests.push({ method: 'POST', path: '/drivers' })]) {
const f = createdVehicleRecoveryFixture(); mutation(f)
assert.throws(() => reviewedFullCheckpoint(f.report, 'DEPARTED_VEHICLE_CREATED'), /Created vehicle recovery/)
}
assert.throws(() => reviewedFullCheckpoint(report, 'DEPARTED_AFTER_COSTS_BEFORE_HANDOVER'), /checkpoint/)
})
for (const fault of ['none', 'changed', 'occupied', 'missing-reader']) test(`created vehicle exact snapshot and no occupation mandatory: ${fault}`, async () => {
const f = createdVehicleRecoveryFixture(), accepted = reviewedFullCheckpoint(f.report, 'DEPARTED_VEHICLE_CREATED')
if (fault === 'changed') f.responses.get('/api/platform/v1/last-leg/master-data/vehicles/' + f.snapshot.id).recordVersion = '1'
if (fault === 'occupied') f.ctx.context.readReviewedVehicleOccupation = async vehicleId => ({ vehicleId, assignmentCount: 1 })
if (fault === 'missing-reader') delete f.ctx.context.readReviewedVehicleOccupation
const options = { costsAlreadyVerified: true, createdVehicle: accepted.createdVehicle }
if (fault === 'none') { const r = await verifyDepartedRecovery(f.ctx, options); assert.equal(r.reviewedVehicle.snapshot.id, f.snapshot.id) }
else await assert.rejects(continueReviewedDeparture(f.ctx, { ...options, makePicker: () => assert.fail('no session before verification') }), /recovery refused/)
})
test('occupancy reader is scoped to exact numeric vehicle and readonly evidence transport', async () => {
const env = { JENKINS_URL: 'https://test.invalid/', JENKINS_USER: 'reader', JENKINS_API_TOKEN: 'secret', FBA_CORE_JENKINS_AGENT: 'java-test-agent' }
const result = await readReviewedVehicleOccupation(env, '123', async (url, init) => {
assert.equal(url.pathname, '/scriptText'); assert.equal(init.method, 'POST'); assert.ok(init.body.get('script').includes('executeGroovy'))
return new Response('FBA_CORE_JSON={"ok":true,"rows":[{"vehicleId":"123","assignmentCount":0}]}')
})
assert.deepEqual(result, { vehicleId: '123', assignmentCount: 0 })
await assert.rejects(readReviewedVehicleOccupation(env, '123; DELETE', () => assert.fail('invalid identity')), /numeric/)
})
function afterCostsRecoveryFixture() {
const fixture = departedRecoveryFixture(), { report, trip, responses, base } = fixture
report.createdResources.fleetAcceptance.stage = 'IN_TRANSIT_HANDOVER'
@@ -156,7 +201,7 @@ test('changed source report SHA blocks recovery before creating context or loggi
rmSync(temporary, { recursive: true, force: true })
}
})
import { parseFleetArguments, fleetExecutionPlan, runFleetAcceptance, reviewedFullCheckpoint, verifyFleetTerminalState, runFleetHandoverStage, fleetCostShares, prepareFleetCostActors, findFleetActorPhone, guardFleetWrites, redactFleetWrite, verifyDepartedRecovery, continueReviewedDeparture, completeDepartedFleetScenario } from './fleet-test-env-acceptance.mjs'
import { parseFleetArguments, fleetExecutionPlan, runFleetAcceptance, reviewedFullCheckpoint, verifyFleetTerminalState, runFleetHandoverStage, fleetCostShares, prepareFleetCostActors, findFleetActorPhone, guardFleetWrites, redactFleetWrite, verifyDepartedRecovery, continueReviewedDeparture, completeDepartedFleetScenario, readReviewedVehicleOccupation } from './fleet-test-env-acceptance.mjs'
for (const response of [
{ http: 0 }, { http: 503, body: { code: 'A0500' }, requestId: 'r', traceId: 't' },