This commit is contained in:
2026-10-27 17:44:41 +08:00
parent 57cd211973
commit db0e72daeb
37 changed files with 701 additions and 671 deletions
@@ -12,28 +12,22 @@ function fixture(fault) {
const history = row => ({ recordVersion: row.cost.recordVersion, action: row.action, snapshot: copy(row.cost) })
function save(row, action) { row.action = action; row.history.unshift(history(row)) }
function current(row) { const { cost } = row; return copy(cost) }
let supplier
const context = { marker, token: 'offline-test-only', createdResources: {},
const supplier = { id: '8', tenantId: fault === 'foreign supplier' ? '9' : '0', category: 'TRUCKING', truckingScope: 'OVERSEAS', status: fault === 'inactive supplier' ? 'INACTIVE' : 'ACTIVE' }
vehicles.set('71', { id: '71', plateNumber: 'EXISTING-TEST', region: 'OVERSEAS', ownership: fault === 'external vehicle' ? 'EXTERNAL' : 'OWNED', status: fault === 'inactive vehicle' ? 'INACTIVE' : 'IN_SERVICE', recordVersion: 0 })
const context = { marker, token: 'offline-test-only', createdResources: {}, options: { fixedCompanyCostResources: { supplierId: '8', vehicleId: '71' } },
assert(_module, label, _response, condition) { assert.ok(condition, label) },
apiSuccess(_module, label, result) { assert.equal(result?.http, 200, label); assert.equal(result?.body?.code, '200', label); return result.body.data },
async request(method, raw, body, _token, headers) {
calls.push({ method, raw, body: copy(body), headers: copy(headers) }); const url = new URL(raw, 'https://fixture.invalid'), path = url.pathname
if (method === 'POST' && path === '/api/platform/v1/suppliers') { supplier = { id: '8', ...copy(body) }; return response('8') }
if (method === 'GET' && path === '/api/platform/v1/suppliers/8') return response(supplier)
if (method === 'GET' && path.includes('/master-data/exchange-rates/')) return response({ month: path.split('/').at(-1), status: fault === 'inactive exchange' ? 'INACTIVE' : 'ACTIVE', usdToCnyAverage: '7.123456', recordVersion: 0 })
if (path.endsWith('/fee-pool')) return response(fault === 'customer pollution' && costs.size ? [{ poolItemId: 'LEAK', customerId: '9', amount: 27.31, feeItemName: marker }] : [])
if (path.endsWith('/cost-entries')) return response([])
if (path.endsWith('/customer-accounts/9/summary')) return response({ customerId: '9', currency: 'CNY', totalReceivable: 100, receivedAmount: 30, outstandingReceivable: 70,
pendingBilling: 0, totalCost: 0, paidCost: 0, outstandingPayable: 0, asOf: new Date().toISOString() })
if (path === '/api/platform/v1/last-leg/master-data/vehicles' && method === 'POST') {
const row = { id: '71', ...copy(body), recordVersion: 0 }; vehicles.set(row.id, row); return response(row)
}
if (path.startsWith('/api/platform/v1/last-leg/master-data/vehicles/')) {
const row = vehicles.get(path.split('/vehicles/')[1].split('/')[0]); assert.ok(row)
if (method === 'GET') return response(row)
assert.equal(method, 'PATCH'); assert.equal(body.status, 'INACTIVE')
if (fault === 'cleanup failure') return response(null, 'S0503', 503)
row.status = 'INACTIVE'; row.recordVersion++; return response(row)
assert.equal(method, 'GET', 'shared vehicle masters must remain read-only'); return response(row)
}
const care = path.match(/^\/api\/platform\/v1\/last-leg\/fleet\/vehicles\/71\/records(?:\/(.*))?$/)
if (care) {
@@ -118,28 +112,29 @@ function fixture(fault) {
const id = value => String(value)
const options = { pollAttempts: 2, pollDelayMs: 0 }
test('independent runner requires explicit service readiness and only the specified TEST profile', () => {
const env = 'C:/Users/BY-OY/.codex/env/chongchong-global/test-acceptance.env'
const env = 'C:/Users/BY-OY/.codex/env/chongchong-global/test/acceptance.env'
assert.throws(() => requireReady([], [`--env-file=${env}`]), /就绪/)
assert.throws(() => requireReady([`--ready-commit=${READY_COMMIT}`], [`--env-file=${env.replace('test-acceptance', 'dev-acceptance')}`]), /授权目录/)
assert.ok(requireReady([`--ready-commit=${READY_COMMIT}`], [`--env-file=${env}`]).endsWith('test-acceptance.env'))
assert.throws(() => requireReady([`--ready-commit=${READY_COMMIT}`], [`--env-file=${env.replace('/test/', '/dev/')}`]), /授权目录/)
assert.ok(requireReady([`--ready-commit=${READY_COMMIT}`], [`--env-file=${env}`]).replaceAll('\\', '/').endsWith('/test/acceptance.env'))
})
test('readiness gate accepts only the full production anchor, not old anchors, shorthand or another commit', () => {
const profile = ['--env-file=C:/Users/BY-OY/.codex/env/chongchong-global/test-acceptance.env']
const profile = ['--env-file=C:/Users/BY-OY/.codex/env/chongchong-global/test/acceptance.env']
assert.equal(READY_COMMIT, '8700fc34238bc33339351c73c15f3af8de8ce517')
for (const commit of ['a6fd0ba3', 'cb6c54ff', '8700fc34', 'd05ff1cd', `${READY_COMMIT}0`]) {
assert.throws(() => requireReady([`--ready-commit=${commit}`], profile), /就绪/)
}
assert.ok(requireReady([`--ready-commit=${READY_COMMIT}`], profile).endsWith('test-acceptance.env'))
assert.ok(requireReady([`--ready-commit=${READY_COMMIT}`], profile).replaceAll('\\', '/').endsWith('/test/acceptance.env'))
})
test('company cost verifier exercises CNY/USD, void/rebuild, payment replay and customer isolation without direct transport', async () => {
const { context, calls, vehicles, records, costs } = fixture()
await run(context, options)
const proof = context.createdResources.fleetCompanyCostAcceptance
assert.equal(proof.status, 'VERIFIED'); assert.equal(proof.cleanup, 'INACTIVE')
assert.equal(proof.status, 'VERIFIED'); assert.equal(proof.cleanup, 'REUSED_UNCHANGED'); assert.equal(proof.reusedMasterData, true)
assert.equal(proof.cases.length, 2); assert.equal(records.size, 2)
assert.ok([...costs.values()].every(row => row.cost.status === 'PAID' && row.payments.length === 2))
assert.ok([...vehicles.values()].every(row => row.status === 'INACTIVE'))
assert.ok([...vehicles.values()].every(row => row.status === 'IN_SERVICE' && row.recordVersion === 0))
assert.equal(calls.some(call => call.method !== 'GET' && /suppliers|master-data\/vehicles/.test(call.raw)), false)
assert.ok(calls.every(call => call.raw.startsWith('/api/platform/')))
assert.equal(calls.some(call => /cost-entries|customer-accounts|customerId=/.test(call.raw)), false, 'must not borrow another task customer for evidence')
assert.equal(calls.some(call => call.method === 'DELETE'), false)
@@ -149,15 +144,21 @@ test('missing independent marker blocks before any write', async () => {
const { context, calls } = fixture(); context.marker = ''
await assert.rejects(run(context, options), /独立marker/); assert.equal(calls.length, 0)
})
for (const resources of [undefined, {}, { supplierId: '8' }, { supplierId: '8', vehicleId: '0' }, { supplierId: 8, vehicleId: 71 }]) test(`missing fixed company masters stop before HTTP: ${JSON.stringify(resources)}`, async () => {
const { context, calls } = fixture(); context.options.fixedCompanyCostResources = resources
await assert.rejects(run(context, options), /禁止自动创建主档/)
assert.equal(calls.length, 0)
})
for (const fault of ['inactive exchange', 'delivery exhausted', 'never delivered', 'wrong source vehicle', 'wrong source version', 'wrong conversion', 'premature paid',
'duplicate payment replay', 'overpayment allowed', 'partial money drift', 'paid void allowed', 'paid source mutable', 'customer pollution', 'file outage', 'wrong file bytes', 'cleanup failure']) {
'duplicate payment replay', 'overpayment allowed', 'partial money drift', 'paid void allowed', 'paid source mutable', 'customer pollution', 'file outage', 'wrong file bytes',
'foreign supplier', 'inactive supplier', 'external vehicle', 'inactive vehicle']) {
test(`company cost verifier rejects ${fault} and preserves failure evidence`, async () => {
const { context, vehicles } = fixture(fault)
const { context, vehicles, calls } = fixture(fault), before = copy([...vehicles.values()])
await assert.rejects(run(context, options))
const proof = context.createdResources.fleetCompanyCostAcceptance
assert.equal(proof.status, 'FAILED')
assert.ok(proof.error || proof.cleanupError)
if (vehicles.size && fault !== 'cleanup failure') assert.ok([...vehicles.values()].every(row => row.status === 'INACTIVE'))
if (fault === 'cleanup failure') assert.equal(proof.cleanup, 'FAILED')
assert.deepEqual([...vehicles.values()], before)
assert.equal(calls.some(call => call.method !== 'GET' && /suppliers|master-data\/vehicles/.test(call.raw)), false)
})
}