gongxiangp2/components/detail-generator/DetailJobRecovery.test.tsx

60 lines
4.2 KiB
TypeScript

import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import DetailJobRecovery from './DetailJobRecovery'
import type { DetailJob } from '@/lib/detail-generator/types'
const requestId = 'a1111111-1111-4111-8111-111111111111'
const fixture = (recovery = { canQuery: true, canContinue: false, needsRequestId: false }): DetailJob => ({ id: 'job-1', projectId: 'project-1', ownerId: 'owner-1', kind: 'analyze', status: 'unknown', checkpoint: {}, payload: {}, requestId, error: '原结果待核查', progress: 40, createdAt: '', updatedAt: '', recovery })
beforeEach(() => { global.fetch = jest.fn(async () => ({ ok: true, json: async () => ({ job: fixture() }) })) as any })
test('querying the original result sends one recovery action and never requests a new analysis', async () => {
const updated = jest.fn().mockResolvedValue(undefined)
render(<DetailJobRecovery job={fixture()} onUpdated={updated} />)
const button = screen.getByRole('button', { name: '取回原结果' })
fireEvent.click(button); fireEvent.click(button)
await waitFor(() => expect(updated).toHaveBeenCalledTimes(1))
expect(global.fetch).toHaveBeenCalledTimes(1)
expect((global.fetch as jest.Mock).mock.calls[0][0]).toBe('/api/detail-generator/jobs/job-1/recover')
expect(JSON.parse((global.fetch as jest.Mock).mock.calls[0][1].body)).toEqual({ action: 'query' })
expect(screen.getByText(/未留存仍需核查/)).toBeVisible()
})
test('recovered analysis requires an explicit continuation and does not resend the original request ID', async () => {
render(<DetailJobRecovery job={fixture({ canQuery: false, canContinue: true, needsRequestId: false })} onUpdated={jest.fn().mockResolvedValue(undefined)} />)
expect(global.fetch).not.toHaveBeenCalled()
expect(screen.getByText(/将调用文案模型,不重复分析/)).toBeVisible()
fireEvent.click(screen.getByRole('button', { name: '继续文案' }))
await waitFor(() => expect(global.fetch).toHaveBeenCalledTimes(1))
expect(JSON.parse((global.fetch as jest.Mock).mock.calls[0][1].body)).toEqual({ action: 'continue' })
expect(screen.queryByRole('button', { name: '取回原结果' })).not.toBeInTheDocument()
})
test('a missing request ID is requested explicitly and only a valid original UUID is accepted', async () => {
render(<DetailJobRecovery job={{ ...fixture({ canQuery: true, canContinue: false, needsRequestId: true }), requestId: null }} onUpdated={jest.fn().mockResolvedValue(undefined)} />)
const field = screen.getByRole('textbox', { name: '原客户请求编号' })
expect(screen.getByRole('button', { name: '取回原结果' })).toBeDisabled()
fireEvent.change(field, { target: { value: requestId } })
fireEvent.click(screen.getByRole('button', { name: '取回原结果' }))
await waitFor(() => expect(global.fetch).toHaveBeenCalledTimes(1))
expect(JSON.parse((global.fetch as jest.Mock).mock.calls[0][1].body)).toEqual({ action: 'query', requestId })
})
test('recovery failure preserves the input; refreshing state makes no second recovery submission', async () => {
const updated = jest.fn().mockResolvedValue(undefined)
;(global.fetch as jest.Mock).mockResolvedValue({ ok: false, status: 503, json: async () => ({ error: '查询暂时中断,原请求仍待核查' }) })
render(<DetailJobRecovery job={{ ...fixture({ canQuery: true, canContinue: false, needsRequestId: true }), requestId: null }} onUpdated={updated} />)
fireEvent.change(screen.getByRole('textbox'), { target: { value: requestId } })
fireEvent.click(screen.getByRole('button', { name: '取回原结果' }))
expect(await screen.findByRole('alert')).toHaveTextContent('仍待核查')
expect(screen.getByRole('textbox')).toHaveValue(requestId)
fireEvent.click(screen.getByRole('button', { name: '刷新状态' }))
expect(updated).toHaveBeenCalledTimes(1)
expect(global.fetch).toHaveBeenCalledTimes(1)
})
test('unsaved edits disable recovery rather than replacing the current draft', () => {
render(<DetailJobRecovery job={fixture()} dirty onUpdated={jest.fn()} />)
expect(screen.getByRole('button', { name: '取回原结果' })).toBeDisabled()
expect(screen.getByText(/撤销未保存修改/)).toBeVisible()
expect(global.fetch).not.toHaveBeenCalled()
})