import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' const storeSource = readFileSync(new URL('./firstLegOrders.ts', import.meta.url), 'utf8') const apiSource = readFileSync(new URL('../api/firstLeg.ts', import.meta.url), 'utf8') const unifiedApiSource = readFileSync(new URL('../api/unifiedOrders.ts', import.meta.url), 'utf8') test('maps the first-leg aggregate list fields without loading detail rows', () => { assert.match(apiSource, /interface MerchantFirstLegOrderRow/) assert.match(apiSource, /skuNos: string \| string\[\] \| null/) assert.match(apiSource, /cargoes\?: Array<\{ skuNo\?: string \| null \}>/) assert.match(apiSource, /apiRequest>/) assert.match(storeSource, /function mapFirstLegOrderRow/) for (const field of ['inboundId', 'inboundCode', 'channelName', 'destinationWarehouseName', 'packageCount', 'expressCompany', 'trackingNo', 'totalGrossWeightKg', 'totalNetWeightKg', 'totalVolumeCbm', 'totalQuantity', 'remark', 'shipmentId', 'referenceId', 'createdAt']) { assert.match(storeSource, new RegExp(`row\\.${field}`)) } assert.match(storeSource, /row\.skuNos/) assert.match(storeSource, /row\.cargoes/) assert.match(storeSource, /chargeableWeightKg: null/) assert.match(storeSource, /chargeableVolumeCbm: null/) assert.match(storeSource, /orders\.value = orderResult\.value\.records\.map\(mapFirstLegOrderRow\)/) }) test('declares the documented merchant warehouse and first-leg endpoints', () => { assert.match(apiSource, /url: '\/merchant\/v1\/warehouses'/) assert.match(apiSource, /url: '\/merchant\/v1\/orders\/first-leg'/) assert.match(apiSource, /url: '\/merchant\/v1\/orders\/first-leg\/batch'/) assert.match(apiSource, /`\/merchant\/v1\/orders\/first-leg\/\$\{id\}`/) assert.match(apiSource, /`\/merchant\/v1\/orders\/first-leg\/\$\{id\}\/submit`/) assert.match(apiSource, /`\/merchant\/v1\/warehouses\/\$\{id\}`/) assert.match(apiSource, /url: '\/merchant\/v1\/first-leg\/pricing\/channels'/) assert.match(apiSource, /params: \{ originWarehouseId \}/) assert.match(apiSource, /url: '\/merchant\/v1\/first-leg\/pricing\/estimate'/) assert.match(apiSource, /`\/merchant\/v1\/first-leg\/inbounds\/\$\{encodeURIComponent\(String\(inboundId\)\)\}\/carton-label-pdf`/) assert.match(apiSource, /`\/merchant\/v1\/first-leg\/orders\/\$\{encodeURIComponent\(String\(orderId\)\)\}\/carton-label-pdf`/) assert.match(apiSource, /url: '\/merchant\/v1\/first-leg\/orders\/carton-label-pdf'/) assert.match(apiSource, /data: orderIds\.map\(String\)/) assert.doesNotMatch(apiSource, /`\/merchant\/v1\/inbounds\/\$\{encodeURIComponent\(String\(inboundId\)\)\}\/carton-label-pdf`/) assert.match(apiSource, /signature === '%PDF-'/) assert.match(apiSource, /throw new ApiError\('箱唛服务未返回有效的 PDF 文件', 'INVALID_PDF'\)/) }) test('maps transport and status fields instead of treating container mode as transport mode', () => { assert.match(storeSource, /order\.transportMode === 'SEA'.*order\.containerMode === 'FULL_CONTAINER' \? 'FCL' : 'LCL'/s) assert.match(storeSource, /order\.id \?\? order\.orderId \?\? ''/) assert.match(storeSource, /order\.transportMode \? clientTransport\(order\) : null/) assert.match(storeSource, /draft\.transportMode === 'ROAD'\) return 'TRUCK'/) assert.match(storeSource, /status === 'SUBMITTED'\) return 'PENDING_REVIEW'/) assert.match(storeSource, /status === 'REJECTED'\) return 'PENDING_REVIEW'/) assert.match(storeSource, /status === 'APPROVED' \|\| status === 'ACCEPTED' \|\| status === 'PROCESSING'\) return 'WAIT_MEASURE'/) assert.match(storeSource, /status === 'COMPLETED'\) return 'DELIVERED'/) }) test('prefers unified reads and only falls back to the real legacy API when C02 is unavailable', () => { assert.match(storeSource, /orders\.value = \[\]/) assert.match(storeSource, /warehouses\.value = \[\]/) assert.match(storeSource, /Promise\.allSettled\(\[getOrdersForClient\(query\), loadWarehouseType\('DOMESTIC', true\)\]\)/) assert.match(storeSource, /getMerchantUnifiedOrders\(unifiedQuery\)/) assert.match(storeSource, /isUnifiedOrderQueryUnavailable\(error\)/) assert.match(storeSource, /getMerchantFirstLegOrders\(legacyQuery\(query\)\)/) assert.doesNotMatch(storeSource, /DEMO_PLATFORM_RECEIVING_WAREHOUSES|initialOrders/) }) test('declares the final C02 row and detail contract in the API adapter', () => { assert.match(unifiedApiSource, /const unifiedOrderBaseUrl = '\/merchant\/v1\/orders'/) assert.doesNotMatch(unifiedApiSource, /orders\/unified/) assert.match(unifiedApiSource, /orderId: number \| string/) assert.match(unifiedApiSource, /mainStatus: ApiUnifiedOrderMainStatus/) assert.match(unifiedApiSource, /nextAction: string \| null/) assert.match(unifiedApiSource, /loadingVersion: number/) assert.match(unifiedApiSource, /pickingVersion: number/) assert.match(unifiedApiSource, /summary: MerchantUnifiedOrderRow/) assert.match(unifiedApiSource, /order: MerchantFirstLegOrderDetail/) assert.match(unifiedApiSource, /timeline: MerchantUnifiedTimelineEvent\[\]/) assert.match(unifiedApiSource, /customNodes\?: MerchantUnifiedCustomNode\[\]/) assert.match(unifiedApiSource, /error\.status === 404 \|\| error\.code === 'A0404'/) }) test('keeps unified order pagination valid when the store omits page and size', () => { assert.match(unifiedApiSource, /const \{ page, size, \.\.\.filters \} = params/) assert.match(unifiedApiSource, /params: \{ page: page \?\? 1, size: size \?\? 200, \.\.\.filters \}/) }) test('keeps drafts on the legacy read path without assigning a unified main status', () => { assert.match(storeSource, /getMerchantFirstLegOrders\(legacyQuery\(query, 'DRAFT'\)\)/) assert.match(storeSource, /getById\(id\)\?\.status === 'DRAFT' \? loadLegacyDetail\(id\) : loadUnifiedDetail\(id\)/) assert.match(storeSource, /return loadLegacyDetail\(String\(createdId\)\)/) assert.match(storeSource, /draftLoadError/) }) test('routes supported filters to C02 without sending DRAFT as a main status', () => { assert.match(storeSource, /interface ClientOrderReadQuery extends MerchantUnifiedOrderQuery/) assert.match(storeSource, /if \(query\.draftOnly\)/) assert.match(storeSource, /const unifiedQuery: MerchantUnifiedOrderQuery/) assert.match(storeSource, /getMerchantUnifiedOrders\(unifiedQuery\)/) }) test('maps C02 rows by orderId and leaves absent transport details unknown', () => { assert.match(storeSource, /id: String\(summary\.orderId\)/) assert.match(storeSource, /transportMode: null/) assert.match(storeSource, /summary\?\.loading\?\.loadingVersion/) assert.match(storeSource, /summary\?\.loading\?\.pickingVersion/) assert.match(storeSource, /summary\?\.inboundId == null \? '' : String\(summary\.inboundId\)/) }) test('sends backend-required channel, pricing, customs, destination and cargo fields', () => { for (const field of ['pickupMethod', 'channelCode', 'channelName', 'pricingTaxMode', 'taxMode', 'customsInspection', 'destinationType', 'packageType']) { assert.match(storeSource, new RegExp(field)) } assert.match(storeSource, /containerMode: draft\.transportMode === 'FCL' \? 'FULL_CONTAINER'/) assert.doesNotMatch(storeSource, /customerId: draft\.shipperPartyId/) assert.match(storeSource, /customerName: customerName\.trim\(\)/) assert.match(storeSource, /destinationWarehouseId: draft\.destinationType === 'PRIVATE' \? undefined : draft\.destinationWarehouseId/) assert.match(storeSource, /draft\.pickupMethod === 'PICKUP'.*draft\.shipperPartySnapshot/s) assert.match(storeSource, /draft\.destinationType === 'PRIVATE'.*draft\.consigneePartySnapshot/s) for (const field of [ 'customsHsCode', 'lengthCm', 'widthCm', 'heightCm', 'unitsPerPackage', 'totalQuantity', 'netWeightKg', 'unitDeclaredValue', 'material', 'usage', 'model', 'brand', 'productLengthCm', 'productWidthCm', 'productHeightCm', 'shipmentId', 'referenceId', 'productImageFileId', ]) { assert.match(storeSource, new RegExp(`${field}: item\\.`)) } assert.match(apiSource, /POST[^\n]*product-images|method: 'POST', url: '\/merchant\/v1\/files\/product-images'/) assert.match(apiSource, /GET[^\n]*product-images|method: 'GET', url: `\/merchant\/v1\/files\/product-images\/\$\{fileId\}`/) }) test('separates domestic receiving warehouses from public destinations', () => { assert.match(apiSource, /warehouseType\?: MerchantWarehouseType/) assert.match(apiSource, /params: \{ page: 1, size: 200, status: 'ACTIVE', \.\.\.params \}/) assert.match(storeSource, /loadAllWarehouseRecords\(warehouseType, getMerchantWarehouses\)/) assert.match(storeSource, /loadWarehouseType\('DOMESTIC', true\)/) assert.match(storeSource, /warehouse\.warehouseType !== 'DOMESTIC'/) assert.match(storeSource, /item\.warehouseType === draft\.destinationType/) assert.match(storeSource, /draft\.amazonWarehouseCode = draft\.destinationType === 'FBA' \? destinationWarehouse\.code : ''/) }) test('only blocks submission when a known company audit status is not active', () => { assert.match(storeSource, /companyAuditStatus\?: MerchantTenantStatus/) assert.match(storeSource, /if \(companyAuditStatus && companyAuditStatus !== 'ACTIVE'\)/) }) test('does not expose the out-of-scope cancellation write', () => { assert.doesNotMatch(storeSource, /cancelMerchant|function cancel|\/cancel/) assert.doesNotMatch(apiSource, /\/cancel/) }) test('sends an idempotency key for create and submit writes', () => { assert.match(apiSource, /'Idempotency-Key': createIdempotencyKey\('first-leg-create'\)/) assert.match(apiSource, /'Idempotency-Key': createIdempotencyKey\(`first-leg-submit-\$\{id\}`\)/) assert.match(apiSource, /createMerchantFirstLegOrderBatch\(items: MerchantBatchDraftItem\[\], idempotencyKey: string\)/) assert.match(apiSource, /headers: \{ 'Idempotency-Key': idempotencyKey \}/) assert.match(storeSource, /createMerchantFirstLegOrderBatch\(items, idempotencyKey\)/) }) test('does not expose the contradictory backend rejected state in the client model', () => { assert.match(storeSource, /status === 'REJECTED'\) return 'PENDING_REVIEW'/) assert.match(storeSource, /item\.toStatus === 'REJECTED' \? '订单信息已补充'/) }) test('maps the final backend timeline and keeps missing custom nodes empty', () => { assert.match(storeSource, /\(detail\.timeline \?\? \[\]\)\.flatMap/) assert.match(storeSource, /item\.mainStatus/) assert.match(storeSource, /\(detail\.customNodes \?\? \[\]\)/) assert.match(storeSource, /\.filter\(\(item\) => item\.customerVisible\)/) assert.match(storeSource, /kind: 'CUSTOM_NODE'/) assert.match(storeSource, /status: null/) assert.match(storeSource, /\.sort\(\(left, right\)/) }) test('keeps unified detail mapping recoverable for historical partial responses', () => { assert.match(storeSource, /\(detail\.parties \?\? \[\]\)\.find/) assert.match(storeSource, /\(detail\.statusHistory \?\? \[\]\)\.map/) assert.match(storeSource, /item\.attachmentIds\?\.length \?\? 0/) assert.match(storeSource, /if \(!detail\?\.summary \|\| !detail\.order\?\.order\) throw new Error\('订单详情数据不完整,请刷新列表后重试'\)/) }) test('maps customer-visible carrier and order lifecycle snapshots into detail', () => { assert.match(apiSource, /export interface MerchantFirstLegOrder[\s\S]*carrierId\?: number \| string \| null/) assert.match(storeSource, /carrierName: order\.carrierName \?\? ''/) assert.match(storeSource, /receivingTime: order\.receivingTime \?\? ''/) assert.match(storeSource, /submittedAt: order\.submittedAt \?\? ''/) assert.match(storeSource, /completedAt: order\.completedAt \?\? ''/) }) test('maps backend warehouse snapshots and fulfillment courier fields', () => { assert.match(storeSource, /order\.receivingWarehouseAddress/) assert.match(storeSource, /order\.expressCompany \?\? ''/) assert.match(storeSource, /order\.expressTrackingNo \?\? ''/) assert.match(storeSource, /getMerchantWarehouseDetail\(id\)/) assert.match(storeSource, /receivingCapability\.cutoffTime/) }) test('keeps failed order loads distinct from empty results', () => { assert.match(storeSource, /const loadError = ref\(''\)/) assert.match(storeSource, /orderResult\.reason instanceof Error/) assert.match(storeSource, /warehouseLoadError/) assert.match(storeSource, /return \{ orders, warehouses, loading, loadedFromApi, loadError, warehouseLoadError, draftLoadError/) })