import { flushPromises, mount } from '@vue/test-utils'
import { beforeEach, expect, it, vi } from 'vitest'
import CargoVerificationForm from './CargoVerificationForm.vue'
import { getCargoVerification, saveCargoVerification, type CargoVerificationContext } from './cargoVerificationApi'
import { ApiResponseError } from '@/shared/api/http'
const permissions = vi.hoisted(() => ({ canWrite: true }))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: (permission: string) => !permission.endsWith('fba-appointment') || permissions.canWrite }) }))
vi.mock('element-plus', () => ({ ElMessage: { warning: vi.fn(), success: vi.fn() }, ElMessageBox: { confirm: vi.fn() } }))
vi.mock('./cargoVerificationApi', () => ({ getCargoVerification: vi.fn(), saveCargoVerification: vi.fn() }))
const source = { sourceKey: 'ORDER:9007199254740993', customerId: 'customer-1', destinationKey: 'FBA:ONT8' }
const context: CargoVerificationContext = {
source: { ...source, orderId: '9007199254740993', inboundCode: 'IN-TEST-001', bookingNo: null, containerNo: 'TEST-CN-001', originWarehouseId: 'warehouse-1', originWarehouseName: '测试仓', destinationWarehouseCode: 'ONT8', packages: null, sourceVersion: null, canFollowup: true, blockReasons: [] },
verification: null, allocatedPackages: 0, unknownAllocationCount: 0, effectivePackages: null, availablePackages: null, sourceChanged: false, allowedActions: ['VERIFY_CARGO'], blockReasons: [],
}
function render() { return mount(CargoVerificationForm, { props: { source }, global: { stubs: { ElButton: { props: ['nativeType'], template: '' }, LoadErrorState: { props: ['message'], template: '
{{ message }}
' }, ApiErrorMeta: true } } }) }
beforeEach(() => {
vi.clearAllMocks(); permissions.canWrite = true; vi.mocked(getCargoVerification).mockResolvedValue(context)
vi.mocked(saveCargoVerification).mockResolvedValue({ version: 1, sourceVersion: null, destinationKey: source.destinationKey, sourcePackages: null, packageCount: 20, shipmentReferences: ['FBA-TEST'], remark: null, operatorName: '测试调度', verifiedAt: '2026-10-15T03:00:00Z' })
})
it('saves independent cargo verification before any appointment exists, preserving unknown source values', async () => {
const wrapper = render(); await flushPromises()
expect(wrapper.text()).toContain('待核实'); expect(wrapper.text()).not.toContain('9007199254740993')
await wrapper.get('input[type=number]').setValue('20'); await wrapper.get('textarea[aria-label="本票货件号"]').setValue('FBA-TEST\nFBA-TEST')
await wrapper.get('form').trigger('submit'); await flushPromises()
expect(saveCargoVerification).toHaveBeenCalledWith(source, expect.objectContaining({ expectedSourceVersion: null, expectedVersion: 0, packageCount: 20, shipmentReferences: ['FBA-TEST'] }))
expect(vi.mocked(saveCargoVerification).mock.calls[0]![1]).not.toHaveProperty('appointmentStatus')
expect(wrapper.emitted('saved')).toHaveLength(1); wrapper.unmount()
})
it('uses the same key and payload after an unknown save outcome', async () => {
vi.mocked(saveCargoVerification).mockRejectedValueOnce(new Error('network lost'))
const wrapper = render(); await flushPromises()
await wrapper.get('input[type=number]').setValue('20'); await wrapper.get('form').trigger('submit'); await flushPromises()
const first = vi.mocked(saveCargoVerification).mock.calls[0]![1]
expect(wrapper.get('fieldset').attributes('disabled')).toBeDefined()
await wrapper.get('form').trigger('submit'); await flushPromises()
expect(saveCargoVerification).toHaveBeenNthCalledWith(2, source, first); wrapper.unmount()
})
it('refreshes source conflicts without deleting the entered quantity or shipment numbers', async () => {
vi.mocked(saveCargoVerification).mockRejectedValueOnce(new ApiResponseError('原来源已变化', 'A0409', 409))
const wrapper = render(); await flushPromises()
await wrapper.get('input[type=number]').setValue('20'); await wrapper.get('textarea[aria-label="本票货件号"]').setValue('FBA-KEEP')
await wrapper.get('form').trigger('submit'); await flushPromises()
expect(getCargoVerification).toHaveBeenCalledTimes(2)
expect((wrapper.get('input[type=number]').element as HTMLInputElement).value).toBe('20')
expect((wrapper.get('textarea[aria-label="本票货件号"]').element as HTMLTextAreaElement).value).toBe('FBA-KEEP'); wrapper.unmount()
})
it('blocks invalid allocation bounds and denies writes for list-only users', async () => {
vi.mocked(getCargoVerification).mockResolvedValue({ ...context, source: { ...context.source, packages: 50 }, allocatedPackages: 30, availablePackages: 20 })
const wrapper = render(); await flushPromises()
await wrapper.get('input[type=number]').setValue('20'); await wrapper.get('form').trigger('submit')
expect(saveCargoVerification).not.toHaveBeenCalled(); wrapper.unmount()
permissions.canWrite = false; const denied = render(); await flushPromises()
await denied.get('form').trigger('submit'); expect(saveCargoVerification).not.toHaveBeenCalled(); denied.unmount()
})