Files
admin-web/src/modules/overseas-master-data/api/drivers.test.ts
T

76 lines
5.7 KiB
TypeScript
Raw Normal View History

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { ApiResponseError, http } from '@/shared/api/http'
import { changeDriverStatus, createDriver, downloadDriverLicense, getDriver, getDriverQualification, listDriverHistory, listDrivers, reviewDriver, submitDriver, updateDriver, uploadDriverLicense } from './drivers'
import type { DriverDraft } from '../driverTypes'
vi.mock('@/shared/api/http', async importOriginal => ({ ...(await importOriginal<typeof import('@/shared/api/http')>()), http: { get: vi.fn(), post: vi.fn(), put: vi.fn(), patch: vi.fn() } }))
const id = '9223372036854775701'
const fileId = '9223372036854775801'
const base = '/platform/v1/last-leg/fleet/drivers'
const details: DriverDraft = { name: '测试司机', phone: 'test-phone', ownership: 'OWNED', region: 'OVERSEAS', licenseNumber: 'LIC-1', licenseType: 'Class A', licenseIssuedOn: null, licenseExpiresOn: '2026-10-22', licenseZone: 'America/Los_Angeles', remark: null, profile: null }
const record = { id, driverNo: 'DR-TEST', details, active: true, reviewStatus: 'APPROVED', licenseFileId: fileId, recordVersion: '2', updatedBy: 'operator', updatedAt: '2026-10-02T12:00:00', qualification: { canDispatch: true, expiringSoon: true, reason: 'ELIGIBLE', licenseExpiresOn: '2026-10-22' } }
const ok = (data: unknown) => ({ code: '200', message: '成功', data })
beforeEach(() => vi.clearAllMocks())
describe('driver APIs', () => {
it('preserves long IDs and date-only values, uses one page request, and normalizes versions', async () => {
vi.mocked(http.get).mockResolvedValueOnce(ok({ records: [record], page: '1', size: '20', total: '1', pages: '1', summary: { total: '1', owned: '1', dispatched: '0', expiringSoon: '1', expired: '0' } })).mockResolvedValueOnce(ok(record))
const result = await listDrivers({ page: 1, size: 20, active: false })
expect(result).toMatchObject({ page: 1, total: 1, records: [{ id, licenseFileId: fileId, recordVersion: 2, details }] })
expect(http.get).toHaveBeenCalledTimes(1)
await getDriver(id)
expect(http.get).toHaveBeenNthCalledWith(1, base, { params: { page: 1, size: 20, active: false } })
expect(http.get).toHaveBeenNthCalledWith(2, `${base}/${id}`, { params: { expectedEnd: undefined } })
})
it('sends current versions to edits, reviews, status changes, and resubmission', async () => {
vi.mocked(http.post).mockResolvedValue(ok(record))
vi.mocked(http.put).mockResolvedValue(ok(record))
vi.mocked(http.patch).mockResolvedValue(ok(record))
await createDriver(details)
await updateDriver(id, 2, details)
await reviewDriver(id, 3, 'REJECTED', '附件不清楚')
await changeDriverStatus(id, 4, false, '暂停驾驶')
await submitDriver(id, 5)
expect(http.post).toHaveBeenCalledWith(base, details)
expect(http.put).toHaveBeenCalledWith(`${base}/${id}`, { recordVersion: 2, details })
expect(http.post).toHaveBeenCalledWith(`${base}/${id}/review`, { recordVersion: 3, decision: 'REJECTED', reason: '附件不清楚' })
expect(http.patch).toHaveBeenCalledWith(`${base}/${id}/status`, { recordVersion: 4, active: false, reason: '暂停驾驶' })
expect(http.post).toHaveBeenCalledWith(`${base}/${id}/submit`, { recordVersion: 5 })
})
it('uploads a real multipart file with the version and leaves multipart boundary to the browser', async () => {
vi.mocked(http.post).mockResolvedValue(ok(record))
const file = new File(['%PDF-1.7'], 'license.pdf', { type: 'application/pdf' })
await uploadDriverLicense(id, 2, file)
const [url, body] = vi.mocked(http.post).mock.calls[0]!
expect(url).toBe(`${base}/${id}/license`)
expect(body).toBeInstanceOf(FormData)
expect((body as FormData).get('recordVersion')).toBe('2')
expect((body as FormData).get('file')).toBe(file)
})
it('takes task qualification from server and keeps history snapshots distinct', async () => {
vi.mocked(http.get).mockResolvedValueOnce(ok(record.qualification)).mockResolvedValueOnce(ok({ page: '1', size: '20', total: '1', pages: '1', records: [{ recordVersion: '1', action: 'UPLOAD_LICENSE', snapshot: { ...record, recordVersion: '1', reviewStatus: 'PENDING' } }] }))
const expectedEnd = '2026-10-20T18:00:00Z'
expect(await getDriverQualification(id, expectedEnd)).toEqual(record.qualification)
expect(http.get).toHaveBeenNthCalledWith(1, `${base}/${id}/qualification`, { params: { expectedEnd } })
expect((await listDriverHistory(id)).records[0]).toMatchObject({ recordVersion: 1, snapshot: { recordVersion: 1, reviewStatus: 'PENDING' } })
})
it('downloads only real supported blobs and rejects HTTP200 JSON business failures', async () => {
const pdf = new Blob(['%PDF-1.7'], { type: 'application/pdf' })
vi.mocked(http.get).mockResolvedValueOnce(pdf).mockResolvedValueOnce({ code: 'A0404', message: '文件未关联', requestId: 'test-request' }).mockResolvedValueOnce(new Blob(['<html>'], { type: 'text/html' }))
expect(await downloadDriverLicense(id, fileId)).toBe(pdf)
expect(http.get).toHaveBeenNthCalledWith(1, `${base}/${id}/license/${fileId}`, { responseType: 'blob' })
await expect(downloadDriverLicense(id, fileId)).rejects.toMatchObject({ message: '文件未关联', requestId: 'test-request' })
await expect(downloadDriverLicense(id, fileId)).rejects.toThrow('格式不受支持')
})
it('does not turn a failed write into a success or round an unsafe record version', async () => {
vi.mocked(http.post).mockResolvedValueOnce({ code: 'A0409', message: '请刷新后重试' }).mockResolvedValueOnce(ok({ ...record, recordVersion: '9223372036854775807' }))
await expect(reviewDriver(id, 1, 'APPROVED', null)).rejects.toBeInstanceOf(ApiResponseError)
await expect(createDriver(details)).rejects.toThrow('无效的分页或版本信息')
})
})