This commit is contained in:
2026-10-08 17:07:57 +08:00
parent 16d2d9111a
commit c1ddb658bd
111 changed files with 2914 additions and 941 deletions
@@ -1,3 +1,4 @@
import { verifyDownloadCenterExport } from './download-center.mjs'
import { createHash } from 'node:crypto'
import { expectedAllocations } from './fleet-trip-cost-acceptance.mjs'
@@ -116,14 +117,6 @@ function requireQuoteCustomer(context) {
return customer
}
export function readCsv(response, requiredHeader) {
const text = new TextDecoder().decode(response?.bytes || new Uint8Array()).replace(/^\uFEFF/, '')
if (response?.http !== 200 || /json/i.test(response?.contentType || '') || /^\s*[{[]/.test(text)
|| !/^text\/csv(?:;|$)/i.test(response?.contentType || '') || !text.split(/\r?\n/, 1)[0].includes(requiredHeader)) {
throw new Error(`Export is not a valid CSV: HTTP=${response?.http}, code=${response?.body?.code || 'n/a'}, requestId=${response?.requestId || 'n/a'}, traceId=${response?.traceId || 'n/a'}`)
}
return text
}
const currency = value => { if (!['CNY', 'USD'].includes(value)) throw new Error('Unsupported or missing currency fact'); return value }
const amounts = rows => rows.map(row => [currency(row.currency), number(row.amount)]).sort((a, b) => a[0].localeCompare(b[0]))
const costs = rows => rows.map(row => [currency(row.currency), number(row.directCost), row.ownCost == null ? null : number(row.ownCost), row.externalCost == null ? null : number(row.externalCost)]).sort((a, b) => a[0].localeCompare(b[0]))
@@ -197,6 +190,7 @@ async function execute(context, { scope, writeMeasured = false, idempotencyNames
check(label, response, response.http === 200 && id(response.body?.code) === expectedCode && response.requestId && response.traceId)
}
async function binary(path) { const response = await context.requestBinary('GET', path); recordResponse('GET', path, response); return response }
const exportContext = Object.assign(Object.create(context), { request, requestBinary: (_method, path) => binary(path) })
function retain(row) {
check('报价返回稳定业务号', null, typeof row?.quoteNo === 'string' && row.quoteNo.length > 0)
if (!proof.quotes.includes(row.quoteNo)) proof.quotes.push(row.quoteNo)
@@ -281,13 +275,8 @@ async function execute(context, { scope, writeMeasured = false, idempotencyNames
check('用途独立状态未串改', null, stillPurchase.status === 'ACTIVE' && id(stillPurchase.recordVersion) === id(purchase.recordVersion))
const history = await command('GET', `${QUOTES}/${encode(sale.quoteNo)}/history`, undefined, '读取报价不可变版本历史')
check('历史保留原始零价与更正版本', null, history.length === 3 && history.some(h => Number(h.snapshot?.details?.looseDeliveryPrice) === 12.5) && history.some(h => Number(h.snapshot?.details?.looseDeliveryPrice) === 15))
const quoteCsv = await binary(`${QUOTES}/export?${query({ keyword: details.originCity })}`)
const quoteText = readCsv(quoteCsv, '报价号')
check('报价CSV为真实文件并包含本轮业务号', quoteCsv, quoteText.includes(sale.quoteNo) && quoteText.includes(purchase.quoteNo))
const error = await binary(`${QUOTES}/export?size=0`)
check('报价导出参数错误保留业务码', error, error.http === 200 && id(error.body?.code) === 'A0422')
let rejected = false; try { readCsv(error, '报价号') } catch { rejected = true }
check('报价下载拒绝JSON错误文件', error, rejected)
await verifyDownloadCenterExport(exportContext, MODULE, 'FLEET_QUOTE_LIST', { keyword: details.originCity }, ['报价号'], [sale.quoteNo, purchase.quoteNo])
await verifyDownloadCenterExport(exportContext, MODULE, 'FLEET_QUOTE_LIST', { kind: 'INVALID' }, [], [], 'FAILED')
proof.quoteStatus = 'VERIFIED'
}
if (scope === 'OPERATIONS_ONLY') {
@@ -308,14 +297,8 @@ async function execute(context, { scope, writeMeasured = false, idempotencyNames
}
const finalReport = await report()
if (writeMeasured) check('运营列表批量回显最新实测记录', null, Number(finalReport.page.records[0].measured?.actualDistanceMiles) === 25.5 && Number(finalReport.page.records[0].measured?.actualFuelLitres) === 8.2)
const exported = await binary(`${OPERATIONS}/export?${query({ from, to, warehouseId: fixture.warehouseId, keyword: fixture.tripNo })}`)
const operationsCsv = readCsv(exported, '实际里程(mile)')
check('运营CSV包含本轮车次及实际里程字段', exported, operationsCsv.includes(fixture.tripNo) && (!writeMeasured || operationsCsv.includes('25.5')))
const exportError = await binary(`${OPERATIONS}/export?${query({ from: to, to: from })}`)
check('错误导出保留标准业务错误而非CSV成功', exportError, exportError.http === 200 && id(exportError.body?.code) === 'A0400')
let rejected = false
try { readCsv(exportError, '车次号') } catch { rejected = true }
check('下载检查器拒绝JSON错误响应', exportError, rejected)
await verifyDownloadCenterExport(exportContext, MODULE, 'TRIP_OPERATIONS_LIST', { from, to, warehouseId: fixture.warehouseId, keyword: fixture.tripNo }, ['车次号', '实际里程(mile)'], [fixture.tripNo, ...(writeMeasured ? ['25.5'] : [])])
await verifyDownloadCenterExport(exportContext, MODULE, 'TRIP_OPERATIONS_LIST', { from: to, to: from }, [], [], 'FAILED')
proof.operationsStatus = 'VERIFIED'
}
proof.status = 'VERIFIED'