import ElementPlus from 'element-plus' import { mount, flushPromises, enableAutoUnmount } from '@vue/test-utils' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { ApiResponseError } from '@/shared/api/http' import { listOverseasFeeRetries } from '../api/overseasFees' import OverseasFeeRetryHistory from './OverseasFeeRetryHistory.vue' vi.mock('../api/overseasFees', () => ({ listOverseasFeeRetries: vi.fn() })) enableAutoUnmount(afterEach) beforeEach(() => { vi.mocked(listOverseasFeeRetries).mockReset().mockResolvedValue([]) vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} }) }) afterEach(() => vi.unstubAllGlobals()) describe('费用重试记录', () => { it('shows every retry with its actual operator, timestamp, result and failure reason', async () => { vi.mocked(listOverseasFeeRetries).mockResolvedValue([ { requestId: 'retry-2', requestedAt: '2026-10-23T11:22:33', operatorName: '李四 [2]', action: '重试推送', result: 'SUCCESS', errorMessage: null }, { requestId: 'retry-1', requestedAt: '2026-10-23T10:20:30', operatorName: '张三 [1]', action: '重试扣费', result: 'FAILED', errorMessage: '可用额度不足' }, ]) const wrapper = mount(OverseasFeeRetryHistory, { props: { feeId: '9007199254740993' }, global: { plugins: [ElementPlus] } }) await flushPromises() expect(listOverseasFeeRetries).toHaveBeenCalledWith('9007199254740993') const text = wrapper.text() for (const value of ['李四 [2]', '张三 [1]', '2026-10-23 11:22:33', '2026-10-23 10:20:30', '重试成功', '重试后仍失败', '可用额度不足']) expect(text).toContain(value) }) it('keeps request failures visible and retries without losing the fee identity', async () => { vi.mocked(listOverseasFeeRetries).mockRejectedValueOnce(new ApiResponseError('查询暂不可用', 'UNAVAILABLE', 503)) const wrapper = mount(OverseasFeeRetryHistory, { props: { feeId: 'fee-1' }, global: { plugins: [ElementPlus] } }) await flushPromises(); expect(wrapper.text()).toContain('查询暂不可用') await wrapper.get('button').trigger('click'); await flushPromises() expect(listOverseasFeeRetries).toHaveBeenLastCalledWith('fee-1'); expect(wrapper.text()).toContain('暂无重试记录') }) it('explains the failed settlement step in Chinese and keeps full technical details collapsed', async () => { const raw = 'EventDeliveryFailure: Consumer rejected event: code=S0500, message=Internal server error, requestId=original-request, traceId=original-trace' vi.mocked(listOverseasFeeRetries).mockResolvedValue([ { requestId: 'r2', requestedAt: '', operatorName: '李四', action: '重试推送', result: 'SUCCESS', errorMessage: null }, { requestId: 'r1', requestedAt: '', operatorName: '张三', action: '重试推送', result: 'FAILED', errorMessage: raw }, ]) const wrapper = mount(OverseasFeeRetryHistory, { props: { feeId: 'fee-1' }, global: { plugins: [ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('推送结算失败:财务系统处理异常,请联系管理员排查后重试。') expect(wrapper.findAll('details')).toHaveLength(1) expect(wrapper.get('details').attributes('open')).toBeUndefined() expect(wrapper.get('summary').text()).toBe('查看技术详情') expect(wrapper.get('pre').text()).toBe(raw) }) it('ignores a late response from another fee', async () => { let resolve!: (rows: Awaited>) => void vi.mocked(listOverseasFeeRetries).mockReturnValueOnce(new Promise(yes => { resolve = yes })) const wrapper = mount(OverseasFeeRetryHistory, { props: { feeId: 'old-fee' }, global: { plugins: [ElementPlus] } }) await wrapper.setProps({ feeId: 'new-fee' }); await flushPromises() resolve([{ requestId: 'old', requestedAt: '', operatorName: '旧费用操作人', action: '重试', result: 'SUCCESS', errorMessage: null }]); await flushPromises() expect(wrapper.text()).not.toContain('旧费用操作人') }) it('refreshes the open history when the actual delivery result changes', async () => { const record = { requestId: 'r1', requestedAt: '', operatorName: '张三', action: '重试推送', result: 'SUBMITTED', errorMessage: null } as const vi.mocked(listOverseasFeeRetries).mockResolvedValueOnce([record]).mockResolvedValueOnce([{ ...record, result: 'SUCCESS' }]) const wrapper = mount(OverseasFeeRetryHistory, { props: { feeId: 'fee-1', refreshKey: 'PENDING' }, global: { plugins: [ElementPlus] } }) await flushPromises(); expect(wrapper.text()).toContain('已提交重试') await wrapper.setProps({ refreshKey: 'PUBLISHED' }); await flushPromises() expect(wrapper.text()).toContain('重试成功'); expect(wrapper.text()).not.toContain('已提交重试') expect(listOverseasFeeRetries).toHaveBeenCalledTimes(2) }) })