fix(dispatch): synchronize loading evidence and review round validation
This commit is contained in:
@@ -4,7 +4,7 @@ import { readFile } from 'node:fs/promises'
|
||||
import { loadingPhotoLabels, loadingPhotoLimit, loadingPhotoSlots, normalizeLoadingDocuments, getLoadingDocuments, bindLoadingPhoto, uploadLoadingPhoto } from '../src/api/dispatch-loading-documents.js'
|
||||
import { session } from '../src/stores/session.js'
|
||||
const source = (await readFile(new URL('../src/components/DispatchLoadingDocuments.vue', import.meta.url), 'utf8')).match(/<script>([\s\S]*?)<\/script>/)[1]
|
||||
const data = extra => ({ dispatchNo: 'PC/1', recordVersion: '0', dispatchRecordVersion: '3', stage: 'AFTER_LOADING', editable: true, complete: false, photos: [], ...extra })
|
||||
const data = extra => ({ dispatchNo: 'PC/1', recordVersion: '0', dispatchRecordVersion: '3', reviewRound: '1', stage: 'AFTER_LOADING', editable: true, complete: false, photos: [], ...extra })
|
||||
const photo = { slot: 'LOADING_SCENE', fileId: '1001', fileName: 'loading.png', contentType: 'image/png' }
|
||||
function component(overrides = {}) {
|
||||
const values = new Map(), events = []
|
||||
@@ -30,22 +30,64 @@ test('five existing slots carry their business names into upload, replace and pr
|
||||
assert.match(template, /:aria-label="`查看\$\{slotLabels\[slot\]\}大图`"/)
|
||||
assert.doesNotMatch(template, /装车照片 \{\{ index \+ 1 \}\}/)
|
||||
})
|
||||
test('refresh reads all photos saved by PC and refreshes the task stage without another write', async () => {
|
||||
let latest = data({ photos: [photo] }), writes = 0, fail = false
|
||||
const { state, events } = component({ getLoadingDocuments: async () => { if (fail) throw new Error('刷新失败'); return latest },
|
||||
uploadLoadingPhoto: async () => { writes++; return photo }, bindLoadingPhoto: async () => { writes++; return latest } })
|
||||
await state.load()
|
||||
latest = data({ recordVersion: '26', stage: 'DELIVERING', complete: true, photos: loadingPhotoSlots.flatMap((slot, category) =>
|
||||
Array.from({ length: 5 }, (_, index) => ({ ...photo, slot, fileId: `${category + 2}${index}` }))) })
|
||||
await state.refresh()
|
||||
assert.deepEqual(state.snapshot.photos, latest.photos)
|
||||
assert.equal(state.snapshot.stage, 'DELIVERING'); assert.equal(state.completedCategories, 5)
|
||||
assert.ok(!state.snapshot.photos.some(item => item.fileId === photo.fileId))
|
||||
assert.equal(events.filter(event => event[0] === 'saved').length, 1)
|
||||
fail = true; await state.refresh()
|
||||
assert.deepEqual(state.snapshot.photos, latest.photos)
|
||||
assert.equal(state.needsRefresh, true); assert.equal(state.busy, false)
|
||||
assert.equal(events.filter(event => event[0] === 'saved').length, 1)
|
||||
assert.equal(writes, 0)
|
||||
})
|
||||
|
||||
test('PDA reads and binds through its own namespace without customer or warehouse input', async () => {
|
||||
const calls = []; session.apiBase = '/gateway-api'; session.token = 'test-token'
|
||||
globalThis.uni = { request(options) { calls.push(options); options.success({ statusCode: 200, data: { code: '200', data: data() } }) } }
|
||||
await getLoadingDocuments('PC/1')
|
||||
const body = { recordVersion: '9007199254740993', idempotencyKey: 'stable-key', fileId: '1001' }
|
||||
const body = { recordVersion: '9007199254740993', idempotencyKey: 'stable-key', uploadKey: 'original-upload-key', fileId: '1001' }
|
||||
await bindLoadingPhoto('PC/1', 'LOADING_SCENE', body)
|
||||
assert.match(calls[0].url, /\/pda\/v1\/last-leg\/dispatches\/PC%2F1\/loading-documents$/)
|
||||
assert.equal(calls[1].method, 'PUT'); assert.deepEqual(calls[1].data, body)
|
||||
})
|
||||
test('imported PDF license is opened as a document on demand, never as a photo', async () => {
|
||||
const license = { ...photo, fileName: 'license.pdf', contentType: 'application/pdf' }
|
||||
let downloads = 0, opened
|
||||
const { state } = component({ getLoadingDocuments: async () => data({ photos: [license] }), readLoadingPhoto: async () => { downloads++; return 'private-license.pdf' } })
|
||||
uni.openDocument = options => { opened = options; options.success() }
|
||||
await state.load()
|
||||
assert.equal(downloads, 0); assert.equal(state.completedCategories, 1)
|
||||
await state.viewPdf(license)
|
||||
assert.equal(downloads, 1); assert.equal(opened.filePath, 'private-license.pdf'); assert.equal(opened.fileType, 'pdf')
|
||||
assert.equal(state.busy, false)
|
||||
})
|
||||
test('PDA upload preserves the original request key through the authenticated upload helper', async () => {
|
||||
const calls = []; session.apiBase = '/gateway-api'; session.token = 'test-token'
|
||||
globalThis.uni = { uploadFile(options) { calls.push(options); options.success({ statusCode: 200, data: JSON.stringify({ code: '200', data: photo }) }) } }
|
||||
await uploadLoadingPhoto('PC/1', 'LOADING_SCENE', 'local-image', 'same-key')
|
||||
assert.match(calls[0].url, /loading-documents\/LOADING_SCENE\/uploads\?idempotencyKey=same-key$/)
|
||||
await uploadLoadingPhoto('PC/1', 'LOADING_SCENE', 'local-image', 'same-key', '1')
|
||||
assert.match(calls[0].url, /loading-documents\/LOADING_SCENE\/uploads\?idempotencyKey=same-key&reviewRound=1$/)
|
||||
assert.equal(calls[0].filePath, 'local-image')
|
||||
})
|
||||
test('re-review during photo upload stops old files instead of adopting the latest round', async () => {
|
||||
let latest = data(), uploads = 0, binds = 0, originalRound
|
||||
const { state, events } = component({ getLoadingDocuments: async () => latest,
|
||||
uploadLoadingPhoto: async (_no, _slot, _path, _key, round) => {
|
||||
uploads++; originalRound = round; latest = data({ recordVersion: '8', reviewRound: '2' }); return photo
|
||||
}, bindLoadingPhoto: async () => { binds++; return latest } })
|
||||
uni.chooseImage = async () => ({ tempFilePaths: ['old-photo', 'not-uploaded'] })
|
||||
await state.load(); await state.choose(photo.slot)
|
||||
assert.equal(originalRound, '1'); assert.equal(uploads, 1); assert.equal(binds, 0)
|
||||
assert.match(state.error.message, /审核轮次已变化/); assert.equal(state.busy, false)
|
||||
assert.equal(events.filter(event => event[0] === 'saved').length, 1)
|
||||
})
|
||||
|
||||
test('wrong dispatch, duplicate slots and invalid versions never become saved facts', () => {
|
||||
assert.throws(() => normalizeLoadingDocuments(data(), 'PC/2'), /不一致/)
|
||||
|
||||
Reference in New Issue
Block a user