优化
This commit is contained in:
@@ -1,122 +1,39 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { verifyFleetFeeCoverage, coveragePlan } from './fleet-fee-coverage-acceptance.mjs'
|
||||
|
||||
const BASE = '/api/platform/v1/last-leg/dispatches', PDA = '/api/pda/v1/last-leg/dispatches', customerId = '900719925474099301'
|
||||
const clone = value => structuredClone(value)
|
||||
function fixture(mode = 'FULL', fault = '', needsLoading = false) {
|
||||
const cases = ['FULL', 'PARTIAL'].map((mode, index) => ({ marker: 'CURRENT-TEST', mode, dispatchNo: `DS-${index + 1}`, customerId,
|
||||
transferBody: { plannedDeliveryAt: '2026-10-22T01:00:00Z', loadingType: 'PALLET', appointmentAccount: 'SELF', fees: [{ direction: 'RECEIVABLE', feeType: 'DELIVERY_FEE', feeName: '派送费', amount: 17.5, currency: 'USD' }] } }))
|
||||
const source = cases.find(c => c.mode === mode), calls = [], assertions = [], ledger = new Map(), saved = new Map()
|
||||
const states = cases.map((c, n) => ({ dispatchNo: c.dispatchNo, recordVersion: 0, stage: needsLoading && c === source ? 'LOADING' : 'AFTER_LOADING', items: [1, 2].map(i => ({
|
||||
dispatchItemId: `${n + 1}1${i}`, sourceKey: `PLAN:${n + 1}2${i}`, sourceVersion: 'a'.repeat(64), customerId, warehousePalletId: `${n + 1}3${i}`, planPalletId: `${n + 1}2${i}`,
|
||||
inboundCode: `IN-${n + 1}-${i}`, destination: { key: `FBA:A${i}`, warehouseCode: `A${i}` }, scheduledPackages: 2, pickedPackages: needsLoading && c === source ? 0 : 2 })) }))
|
||||
if (fault === 'overlap') states[1].items[0].warehousePalletId = states[0].items[0].warehousePalletId
|
||||
const triples = cases.map((c, n) => ({ dispatchNo: c.dispatchNo, tripNo: `TR-${n + 1}`, warehouseId: '92', stops: [{ index: 0, destinationKey: 'ORIGIN:92' }, { index: 1, destinationKey: 'FBA:A1' }, { index: 2, destinationKey: 'FBA:A2' }] }))
|
||||
let dirty = false, replayed = false
|
||||
const totals = s => ({ itemCount: s.items.length, palletCount: s.items.length, packageCount: 4, pickedPackageCount: s.items.reduce((n, i) => n + i.pickedPackages, 0), shippedPackageCount: 0 })
|
||||
const detail = s => ({ dispatchNo: s.dispatchNo, status: 'ACTIVE', stage: s.stage, recordVersion: s.recordVersion, counts: totals(s), pickingReconciled: s.items.every(i => i.pickedPackages === 2),
|
||||
dispatchInfo: { route: 'A1,A2' }, orderDetails: s.items.map(i => ({ customerId, sourceKey: i.sourceKey, inboundCode: i.inboundCode, destinationFbaWarehouse: i.destination.warehouseCode })) })
|
||||
const cargo = s => s.items.map((i, n) => ({ ...i, orderId: `4${s.dispatchNo.slice(-1)}${n + 1}`, sourceRegion: 'FIRSTLEG', sourceInboundId: `9007199254740994${s.dispatchNo.slice(-1)}${n + 1}`, packages: 2 }))
|
||||
const groups = s => cargo(s).map((row, n) => ({ groupKey: `G-${s.dispatchNo}-${n}`, customerId, destination: row.destination, groupCargoHash: `${n + 1}`.repeat(64), externalFreightHash: 'b'.repeat(64), externalQuotes: [], feeCoverageCargoHash: `${n + 1}`.repeat(64), cargo: [row], blockReasons: [], requiredServices: ['DELIVERY_FEE'],
|
||||
feeSources: fault === 'missing-source' && n === 1 ? [] : [{ financeFeeId: `9007199254740995${s.dispatchNo.slice(-1)}${n}`, customerId: fault === 'foreign-customer' && n === 0 ? '999' : customerId,
|
||||
currency: 'CNY', amount: fault === 'mutated-original' && saved.has(s.dispatchNo) ? 201 : 200, sourceVersion: 1, sourceHash: `fee-hash-${n}`, status: 'AVAILABLE', services: ['DELIVERY_FEE'], sourceKeys: [row.sourceKey] }] }))
|
||||
const ack = (s, action, result = {}) => ({ dispatchNo: s.dispatchNo, action, recordVersion: s.recordVersion, status: 'ACTIVE', stage: s.stage, counts: totals(s), result, replayed: false })
|
||||
const response = (data, message = '') => ({ http: 200, body: { code: message ? 'A0409' : '200', message, data: clone(data) }, requestId: 'OFFLINE-REQ', traceId: 'OFFLINE-TRACE' })
|
||||
const context = { marker: source.marker, createdResources: { fleetFeeCoverageCases: cases, fleetTrips: triples }, checkpoint: async () => {},
|
||||
assert(_module, name, _response, passed) { assertions.push({ name, passed }) }, addStep(_module, name, _response, passed) { assertions.push({ name, passed }) }, addCoverageGap() {}, apiSuccess(_m, _l, r) { return r.body?.data },
|
||||
async request(method, raw, body, token) {
|
||||
calls.push({ method, path: raw, body: clone(body), token })
|
||||
const path = raw.split('?')[0], params = new URLSearchParams(raw.split('?')[1]), normalized = path.replace(PDA, BASE), number = normalized.slice(BASE.length + 1).split('/')[0]
|
||||
const state = states.find(s => s.dispatchNo === number), commandKey = `${normalized}:${body?.idempotencyKey}`
|
||||
if (method === 'POST' && ledger.has(commandKey)) { assert.deepEqual(body, ledger.get(commandKey).body); replayed = true; return response({ ...clone(ledger.get(commandKey).data), replayed: true }) }
|
||||
const remember = data => { ledger.set(commandKey, { body: clone(body), data: clone(data) }); return response(data) }
|
||||
if (method === 'GET' && path === BASE) return response({ records: states.filter(s => s.dispatchNo === params.get('dispatchNo')).map(s => ({ ...detail(s), warehouseId: '92' })) })
|
||||
if (path.includes('/outbound-orders?') || raw.includes('/outbound-orders?')) return response({ records: dirty ? [{ outboundNo: 'OUT-DIRTY', dispatchNo: source.dispatchNo }] : [], total: dirty ? 1 : 0, page: 1, size: 200 })
|
||||
if (path.match(/\/outbound-orders\/OUT-DS-[12]$/)) { const n = path.slice(path.lastIndexOf('OUT-') + 4), s = states.find(s => s.dispatchNo === n), p = saved.get(n); return response({ outbound: { outboundNo: `OUT-${n}`, dispatchNo: n, status: 'WAIT_OUTBOUND' }, deliveries: p.groups.map((g, i) => ({ deliveryNo: `DEL-${n}-${i}`, status: 'WAIT_DELIVERY' })) }) }
|
||||
const delivery = path.match(/\/delivery-orders\/DEL-(DS-[12])-(\d)$/)
|
||||
if (delivery) {
|
||||
assert.equal(params.get('customerId'), customerId)
|
||||
const s = states.find(s => s.dispatchNo === delivery[1]), index = Number(delivery[2]), group = groups(s)[index], input = saved.get(s.dispatchNo).groups[index]
|
||||
const references = input.feeCoverage.map(selection => ({ source: { ...group.feeSources.find(f => f.financeFeeId === selection.financeFeeId), sourceType: 'INBOUND_FEE', sourceInboundId: group.cargo[0].sourceInboundId,
|
||||
...(fault === 'snapshot-source-amount' ? { amount: 999 } : {}) }, services: selection.services }))
|
||||
const evidence = input.feeCoverage.length ? { cargoHash: group.feeCoverageCargoHash, cargo: fault === 'wrong-cargo' ? cargo(s) : group.cargo,
|
||||
references: fault === 'missing-reference' ? [] : references, operatorId: 'OP', confirmedAt: replayed && fault === 'rewrite-confirmation' ? '2026-10-19T02:00:00Z' : '2026-10-19T01:00:00Z' } : null
|
||||
let fees = input.fees.map(f => ({ id: `8${index}`, feeDirection: f.direction, feeType: f.feeType, currency: f.currency, amount: f.amount, reviewStatus: 'APPROVED', sourceStatus: 'ACTIVE' }))
|
||||
if (fault === 'duplicate-new-fee' && index === 0 || fault === 'replay-adds-fee' && replayed) fees.push({ id: '999', feeDirection: 'RECEIVABLE', feeType: 'DELIVERY_FEE', currency: 'USD', amount: 17.5, reviewStatus: 'APPROVED', sourceStatus: 'ACTIVE' })
|
||||
if (fault === 'charge-covered-destination') fees = index === 0 ? [{ id: '999', feeDirection: 'RECEIVABLE', feeType: 'DELIVERY_FEE', currency: 'USD', amount: 17.5, reviewStatus: 'APPROVED', sourceStatus: 'ACTIVE' }] : []
|
||||
return response({ order: { dispatchNo: s.dispatchNo, deliveryNo: `DEL-${s.dispatchNo}-${index}`, customerId: fault === 'wrong-delivery-customer' ? '999' : customerId, outboundNo: `OUT-${s.dispatchNo}`, destinationWarehouseCode: group.destination.warehouseCode }, fees, feeCoverage: evidence })
|
||||
}
|
||||
assert.ok(state, `${method} ${raw}`)
|
||||
if (path.startsWith(PDA) && method === 'GET' && normalized.endsWith(number)) return response({ ...detail(state), items: state.items.map((i, n) => ({ ...i, positions: [{ positionId: `6${n}`, availablePackages: 2 }] })) })
|
||||
if (method === 'POST' && normalized.endsWith('/pick')) { assert.ok(token); if (body.recordVersion !== state.recordVersion) return response(null, '公共版本已变化'); const itemId = normalized.split('/').at(-2); state.items.find(i => i.dispatchItemId === itemId).pickedPackages += body.packages; state.recordVersion++; return remember(ack(state, 'PICK')) }
|
||||
if (method === 'GET' && normalized.endsWith('/loading')) return response({ ...detail(state), manifestHash: 'c'.repeat(64), loadingComplete: { allowed: true }, pallets: state.items.map((i, n) => ({ ...i, palletNo: `PAL-${n}`, deliveryIndex: n + 1, palletSpaces: 1, weightKg: 20, volumeCbm: 0.2,
|
||||
evidence: ['palletSpaces', 'weightKg', 'volumeCbm'].map(field => ({ field, value: field === 'palletSpaces' ? 1 : field === 'weightKg' ? 20 : 0.2, actual: true, sourceType: 'MEASURED', sourceBusinessNo: i.inboundCode, observedAt: '2026-10-19T01:00:00Z' })) })) })
|
||||
if (method === 'POST' && normalized.endsWith('/loading-complete')) { assert.equal(body.recordVersion, state.recordVersion); assert.equal(state.items.every(i => i.pickedPackages === 2), true); state.stage = 'AFTER_LOADING'; state.recordVersion++; return remember(ack(state, 'LOADING_COMPLETE', { confirmedLoad: { manifestHash: body.manifestHash, pallets: body.measurements } })) }
|
||||
if (method === 'GET' && path.endsWith('/items')) return response({ dispatchNo: number, recordVersion: state.recordVersion, status: 'ACTIVE', stage: state.stage, items: state.items })
|
||||
if (method === 'GET' && path.endsWith('/route')) return response({ dispatchNo: number, recordVersion: state.recordVersion, stops: triples.find(t => t.dispatchNo === number).stops })
|
||||
if (method === 'GET' && path.endsWith('/delivery-arrangement')) return response({ ...detail(state), cargoHash: 'a'.repeat(64), loadingCompletionHash: 'c'.repeat(64), arrange: { allowed: !saved.has(number) }, groups: groups(state), arrangement: saved.has(number) ? { dispatchNo: number } : null })
|
||||
if (method === 'POST' && path.endsWith('/transfer-delivery')) {
|
||||
assert.ok(body.idempotencyKey); assert.equal(body.recordVersion, state.recordVersion); assert.equal(state.stage, 'AFTER_LOADING')
|
||||
if (body.cargoHash !== 'a'.repeat(64)) { if (fault === 'failed-command-writes') dirty = true; return response(null, '装车或实际货物已经变化') }
|
||||
if (body.groups.some(g => g.feeCoverage.some(f => f.sourceVersion !== 1))) return response(null, '原费用版本已变化')
|
||||
if (source.mode === 'FULL' && body.groups.some(g => g.fees.length)) return response(null, '本组服务已全部覆盖,不得重复登记应收')
|
||||
if (source.mode === 'PARTIAL' && !body.groups.some(g => g.fees.length)) return response(null, '本组尚有未覆盖的服务费用,请核对')
|
||||
saved.set(number, clone(body)); state.recordVersion++
|
||||
const result = ack(state, 'ARRANGE_DELIVERY', { dispatchNo: number, cargoHash: body.cargoHash, outbounds: [{ customerId, outboundNo: `OUT-${number}`, status: 'WAIT_OUTBOUND', packageCount: 4 }],
|
||||
deliveries: groups(state).map((g, n) => ({ groupKey: g.groupKey, customerId, destination: g.destination, outboundNo: `OUT-${number}`, deliveryNo: `DEL-${number}-${n}`, status: 'WAIT_DELIVERY', packageCount: 2 })) })
|
||||
if (fault === 'unknown-write') return { http: 0, body: null, raw: 'connection reset' }
|
||||
return remember(result)
|
||||
}
|
||||
assert.equal(method, 'GET', raw); assert.equal(path, `${BASE}/${number}`); return response(detail(state))
|
||||
} }
|
||||
return { context, source, cases, calls, assertions, states, groups, clearFault() { fault = '' } }
|
||||
import { verifyFleetFeeCoverage } from './fleet-fee-coverage-acceptance.mjs'
|
||||
const BASE = '/api/platform/v1/last-leg/dispatches'
|
||||
function fixture(fault = '') {
|
||||
const cases = ['FULL','PARTIAL'].map((mode,i)=>({marker:'RUN',mode,dispatchNo:`DS-${i}`,customerId:'C'}))
|
||||
const trips = cases.map(c=>({dispatchNo:c.dispatchNo,tripNo:`TR-${c.mode}`,warehouseId:'92',loadingCommand:{body:{idempotencyKey:`KEY-${c.mode}`},result:{action:'LOADING_COMPLETE',dispatchNo:c.dispatchNo,stage:'DELIVERING',result:{loadingCompletionHash:'HASH'}}}}))
|
||||
const items = cases.map((c,i)=>[{customerId:'C',warehousePalletId:fault==='overlap'?'PAL':`PAL-${i}`,sourceKey:`SOURCE-${i}`,destination:{key:'FBA:A'},pickedPackages:2,scheduledPackages:2}])
|
||||
const docs = i=>({outbounds:[{customerId:'C',outboundNo:`OUT-${i}`,status:'OUTBOUNDED'}],deliveries:[{customerId:'C',outboundNo:`OUT-${i}`,deliveryNo:`DEL-${i}`,destination:{key:'FBA:A'},status:'DELIVERING',packageCount:2}]})
|
||||
const detail = i=>({dispatchNo:`DS-${i}`,recordVersion:7,status:'ACTIVE',stage:'DELIVERING',pickingReconciled:true,counts:{packageCount:2,pickedPackageCount:2,shippedPackageCount:2},documents:fault==='missing-docs'?{}:docs(i)})
|
||||
const calls=[]
|
||||
const context={marker:'RUN',createdResources:{fleetFeeCoverageCases:cases,fleetTrips:trips},checkpoint:async()=>{},assert(_m,label,_r,ok){assert.ok(ok,label)},apiSuccess(_m,_l,r){return r.body.data},async request(method,raw){
|
||||
calls.push({method,raw});assert.equal(method,'GET')
|
||||
const url=new URL(raw,'https://offline.invalid'),path=url.pathname
|
||||
let data
|
||||
if(path===BASE){const i=Number(url.searchParams.get('dispatchNo').slice(-1));data={records:[{...detail(i),warehouseId:'92'}]}}
|
||||
else if(path.includes('/delivery-orders/')){const i=Number(path.slice(-1));assert.equal(url.searchParams.get('customerId'),'C');data={order:{dispatchNo:`DS-${i}`,deliveryNo:`DEL-${i}`,outboundNo:`OUT-${i}`,customerId:fault==='foreign-customer'?'OTHER':'C',packageCount:2,status:'DELIVERING'},fees:fault==='duplicate-fee'?[{id:'F'},{id:'F'}]:[],feeCoverage:null}}
|
||||
else{const i=Number(path.split('/dispatches/')[1][3]);data=path.endsWith('/items')?{...detail(i),items:items[i]}:detail(i)}
|
||||
return {http:200,body:{code:'200',data},requestId:'REQ',traceId:'TRACE'}
|
||||
}}
|
||||
return {context,cases,trips,calls}
|
||||
}
|
||||
|
||||
for (const mode of ['FULL', 'PARTIAL']) test(`${mode} uses native grouped fees, immutable confirmation evidence and original-key replay`, async () => {
|
||||
const f = fixture(mode), proof = await verifyFleetFeeCoverage(f.context, 'coverage', f.source)
|
||||
assert.equal(proof.status, 'VERIFIED'); assert.equal(proof.newReceivable, mode === 'FULL' ? 0 : 17.5); assert.equal(proof.deliveryNos.length, 2)
|
||||
assert.ok(f.assertions.every(a => a.passed)); assert.ok(f.calls.every(c => !/cancel|unbind|pre-arrange|fee-coverage/.test(c.path)))
|
||||
assert.equal(proof.confirmationRequest.recordVersion, 0); assert.equal(proof.confirmationRequest.groups.length, 2)
|
||||
await assert.rejects(verifyFleetFeeCoverage(f.context, 'coverage', f.source), /未执行/)
|
||||
for(const index of [0,1])test(`automatic delivery reads authoritative documents for fixture ${index}`,async()=>{
|
||||
const f=fixture(),proof=await verifyFleetFeeCoverage(f.context,'automatic',f.cases[index])
|
||||
assert.equal(proof.status,'VERIFIED');assert.deepEqual(proof.deliveryNos,[`DEL-${index}`])
|
||||
assert.equal(proof.feeCoverageSelection,'NOT_APPLICABLE_AUTOMATIC_DELIVERY')
|
||||
assert.equal(Object.hasOwn(proof,'newReceivable'),false)
|
||||
assert.deepEqual(proof.confirmationRequest,f.trips[index].loadingCommand.body)
|
||||
assert.ok(f.calls.every(c=>c.method==='GET'))
|
||||
assert.equal((await verifyFleetFeeCoverage(f.context,'automatic',f.cases[index],{reconcilePersisted:true})).status,'VERIFIED')
|
||||
await assert.rejects(verifyFleetFeeCoverage(f.context,'automatic',f.cases[index]),/未执行/)
|
||||
})
|
||||
|
||||
test('LOADING performs actual PDA picks, immutable pick replay and explicit loading completion before arranging', async () => {
|
||||
const f = fixture('FULL', '', true), proof = await verifyFleetFeeCoverage(f.context, 'coverage', f.source, { pickerToken: 'OFFLINE-ONLY' })
|
||||
assert.equal(proof.status, 'VERIFIED')
|
||||
const loading = f.calls.findIndex(c => c.method === 'POST' && c.path.endsWith('/loading-complete')), arrangement = f.calls.findIndex(c => c.method === 'POST' && c.path.endsWith('/transfer-delivery'))
|
||||
assert.ok(loading > f.calls.findIndex(c => c.path.endsWith('/pick'))); assert.ok(arrangement > loading)
|
||||
for(const fault of ['overlap','missing-docs','foreign-customer','duplicate-fee'])test(`automatic proof rejects ${fault}`,async()=>{
|
||||
const f=fixture(fault);await assert.rejects(verifyFleetFeeCoverage(f.context,'automatic',f.cases[0]));assert.notEqual(f.cases[0].proof.status,'VERIFIED')
|
||||
})
|
||||
|
||||
test('reviewed persisted reconciliation only replays original successful native command after actual document readback', async () => {
|
||||
const f = fixture('FULL', 'wrong-cargo'); await assert.rejects(verifyFleetFeeCoverage(f.context, 'coverage', f.source)); assert.ok(f.source.proof.outboundNo)
|
||||
const original = clone(f.source.proof.confirmationRequest), before = f.calls.length; f.clearFault()
|
||||
assert.equal((await verifyFleetFeeCoverage(f.context, 'coverage', f.source, { reconcilePersisted: true })).status, 'VERIFIED')
|
||||
assert.ok(f.calls.slice(before).filter(c => c.method === 'POST').every(c => JSON.stringify(c.body) === JSON.stringify(original)))
|
||||
})
|
||||
|
||||
for (const fault of ['foreign-customer', 'missing-source', 'failed-command-writes', 'wrong-cargo', 'missing-reference', 'duplicate-new-fee', 'rewrite-confirmation', 'mutated-original', 'charge-covered-destination', 'replay-adds-fee', 'wrong-delivery-customer', 'overlap', 'unknown-write', 'snapshot-source-amount']) test(`native fee proof rejects ${fault}`, async () => {
|
||||
const f = fixture(fault === 'charge-covered-destination' ? 'PARTIAL' : 'FULL', fault)
|
||||
await assert.rejects(verifyFleetFeeCoverage(f.context, 'coverage', f.source)); assert.notEqual(f.source.proof.status, 'VERIFIED')
|
||||
if (['foreign-customer', 'missing-source', 'overlap'].includes(fault)) assert.equal(f.calls.some(c => c.method === 'POST'), false)
|
||||
if (fault === 'unknown-write') { assert.equal(f.source.proof.status, 'OUTCOME_UNKNOWN'); const last = f.calls.at(-1); assert.equal(last.method, 'POST'); assert.equal(f.context.createdResources.dispatchWriteBoundary.stopped, true) }
|
||||
})
|
||||
|
||||
test('two current registered source groups are required before HTTP; missing PDA and ambiguous mixed provenance never guess', async () => {
|
||||
const missing = fixture(); missing.context.createdResources.fleetFeeCoverageCases.pop(); await assert.rejects(verifyFleetFeeCoverage(missing.context, 'coverage', missing.source), /两组不重叠/); assert.equal(missing.calls.length, 0)
|
||||
const noPicker = fixture('FULL', '', true); await assert.rejects(verifyFleetFeeCoverage(noPicker.context, 'coverage', noPicker.source), /PDA身份/); assert.equal(noPicker.calls.some(c => c.method === 'POST'), false)
|
||||
const f = fixture('PARTIAL'), groups = f.groups(f.states[1]); groups[0].cargo.push({ ...groups[0].cargo[0], sourceInboundId: '999' }); assert.throws(() => coveragePlan({ groups }, f.source), /不能唯一映射/)
|
||||
const multi = fixture('PARTIAL'), many = multi.groups(multi.states[1]), third = clone(many[1]); third.groupKey = 'GROUP-THIRD'; third.cargo[0].sourceKey = 'PLAN:999'; third.cargo[0].sourceInboundId = '999'; third.feeSources[0].financeFeeId = '9999'; third.feeSources[0].sourceKeys = ['PLAN:999']; many.push(third)
|
||||
assert.throws(() => coveragePlan({ groups: many }, multi.source), /明确逐组费用/)
|
||||
multi.source.transferBody.groupFees = { [many[1].groupKey]: [{ ...multi.source.transferBody.fees[0], amount: 6 }] }
|
||||
assert.throws(() => coveragePlan({ groups: many }, multi.source), /明确逐组费用/)
|
||||
multi.source.transferBody.groupFees[third.groupKey] = [{ ...multi.source.transferBody.fees[0], amount: 7 }]
|
||||
assert.deepEqual(coveragePlan({ groups: many }, multi.source).groups.map(g => g.fees.map(f => f.amount)), [[], [6], [7]])
|
||||
const unsafe = fixture(); unsafe.source.customerId = 9007199254740992; assert.throws(() => coveragePlan({ groups: unsafe.groups(unsafe.states[0]) }, unsafe.source), /ID精度无效/)
|
||||
})
|
||||
|
||||
test('public native delivery DTO retains grouped fees and requires version and loading hashes', () => {
|
||||
const dto = readFileSync(new URL('../../lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/port/DispatchDeliveryModels.java', import.meta.url), 'utf8')
|
||||
assert.ok(dto.includes('record ArrangeDelivery(Long recordVersion')); assert.ok(dto.includes('String loadingCompletionHash')); assert.ok(dto.includes('List<ArrangeGroup> groups'))
|
||||
test('requires original current-run loading completion receipt and registered disjoint fixtures',async()=>{
|
||||
const f=fixture();delete f.trips[0].loadingCommand;await assert.rejects(verifyFleetFeeCoverage(f.context,'automatic',f.cases[0]))
|
||||
const missing=fixture();missing.cases.pop();await assert.rejects(verifyFleetFeeCoverage(missing.context,'automatic',missing.cases[0]));assert.equal(missing.calls.length,0)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user