diff --git a/scripts/admin-test-env-modules/truck-appointments-contract.test.mjs b/scripts/admin-test-env-modules/truck-appointments-contract.test.mjs index 3eeaab7a3..7c31f133c 100644 --- a/scripts/admin-test-env-modules/truck-appointments-contract.test.mjs +++ b/scripts/admin-test-env-modules/truck-appointments-contract.test.mjs @@ -2,7 +2,19 @@ import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' -import { activeCustomerIdentity, inspectOceanBookingIdentity, platformWarehouseIdentity, truckAppointmentPayload } from './truck-appointments.mjs' +import { activeCustomerIdentity, inspectOceanBookingIdentity, minimalPdfBytes, platformWarehouseIdentity, truckAppointmentPayload } from './truck-appointments.mjs' + +test('PDF upload fixtures contain real PDF magic bytes instead of MIME-only text', () => { + const bytes = minimalPdfBytes('RUN-2-DISPATCH_ORDER') + const content = Buffer.from(bytes).toString('ascii') + assert.equal(content.slice(0, 5), '%PDF-') + assert.match(content, /%%EOF\n$/) + + const source = readFileSync(new URL('truck-appointments.mjs', import.meta.url), 'utf8') + assert.match(source, /new Blob\(\[minimalPdfBytes\(`\$\{context\.marker\}-BILL_OF_LADING`\)\], \{ type: 'application\/pdf' \}\)/) + assert.match(source, /new Blob\(\[minimalPdfBytes\(`\$\{context\.marker\}-DISPATCH_ORDER`\)\], \{ type: 'application\/pdf' \}\)/) + assert.doesNotMatch(source, /new Blob\(\[\s*`\$\{context\.marker\}-(?:BILL_OF_LADING|DISPATCH_ORDER)`\s*\], \{ type: 'application\/pdf' \}\)/) +}) test('booking prerequisite resolves an ACTIVE platform customer without fixture state', () => { assert.deepEqual(activeCustomerIdentity({ id: '21', tenantName: '现有客户' }), diff --git a/scripts/admin-test-env-modules/truck-appointments.mjs b/scripts/admin-test-env-modules/truck-appointments.mjs index 3fc3fae36..502eed3a5 100644 --- a/scripts/admin-test-env-modules/truck-appointments.mjs +++ b/scripts/admin-test-env-modules/truck-appointments.mjs @@ -14,6 +14,22 @@ function data(response) { return codeOf(response) === '200' ? response.body.data function failure(response) { return response?.http === 0 || response?.http >= 400 || (response?.http === 200 && codeOf(response) !== '200') } function records(value) { return Array.isArray(value) ? value : value?.records || [] } +export function minimalPdfBytes(label = 'truck-appointment-fixture') { + const safeLabel = String(label).replace(/[^\x20-\x7E]/g, '_').slice(0, 64) + return new TextEncoder().encode(`%PDF-1.4 +% ${safeLabel} +1 0 obj +<< /Type /Catalog /Pages 2 0 R >> +endobj +2 0 obj +<< /Type /Pages /Kids [] /Count 0 >> +endobj +trailer +<< /Root 1 0 R >> +%%EOF +`) +} + export function platformWarehouseIdentity(item) { return { warehouseId: id(item?.warehouseId || item?.id || item?.warehouse?.id), platformTenantId: '0' } } @@ -136,7 +152,7 @@ async function fixture(context) { const bookingId = id(created?.oceanBooking?.id); if (!bookingId) return null const form = new FormData(); form.append('expectedVersion', String(created.oceanBooking.version)) form.append('documentType', 'BILL_OF_LADING') - form.append('file', new Blob([`${context.marker}-BILL_OF_LADING`], { type: 'application/pdf' }), `bill-${suffix}.pdf`) + form.append('file', new Blob([minimalPdfBytes(`${context.marker}-BILL_OF_LADING`)], { type: 'application/pdf' }), `bill-${suffix}.pdf`) const uploadResponse = await context.requestMultipart('POST', `${BOOKINGS}/${bookingId}/documents/upload`, form, context.token, { 'Idempotency-Key': `${context.marker}-truck-booking-file`.slice(0, 128) }) const uploaded = context.apiSuccess(MODULE, '上传当前有效 BILL_OF_LADING 文件', uploadResponse, '', OPTIONS) @@ -186,7 +202,7 @@ export async function run(context) { && !Object.hasOwn(appointment, 'customerId') && !Object.hasOwn(appointment, 'customerName'), '', OPTIONS) const path = `${TRUCKS}/${appointmentId}` const attachmentForm = new FormData(); attachmentForm.append('attachmentType', 'DISPATCH_ORDER') - attachmentForm.append('file', new Blob([`${context.marker}-DISPATCH_ORDER`], { type: 'application/pdf' }), + attachmentForm.append('file', new Blob([minimalPdfBytes(`${context.marker}-DISPATCH_ORDER`)], { type: 'application/pdf' }), `dispatch-order-${identity.suffix}.pdf`) const uploadAttachmentResponse = await context.requestMultipart('POST', `${path}/attachments/upload`, attachmentForm, context.token)