mirror of https://github.com/allinssl/allinssl
140 lines
4.6 KiB
JavaScript
140 lines
4.6 KiB
JavaScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
import useRetry from '../src/retry'
|
|
describe('useRetry', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers() // 使用假定时器
|
|
})
|
|
afterEach(() => {
|
|
vi.clearAllTimers() // 清除所有定时器
|
|
vi.useRealTimers() // 恢复真实定时器
|
|
})
|
|
it('should execute successfully on first try', async () => {
|
|
const mockFn = vi.fn().mockResolvedValue('success') // 模拟成功
|
|
const { run, loading, error } = useRetry(mockFn)
|
|
const promise = run()
|
|
expect(loading.value).toBe(true)
|
|
expect(error.value).toBe(null)
|
|
const result = await promise
|
|
expect(result).toBe('success')
|
|
expect(loading.value).toBe(false)
|
|
expect(error.value).toBe(null)
|
|
expect(mockFn).toHaveBeenCalledTimes(1)
|
|
})
|
|
it('should retry on failure and succeed eventually', async () => {
|
|
const mockError = new Error('test error')
|
|
const mockFn = vi
|
|
.fn()
|
|
.mockRejectedValueOnce(mockError)
|
|
.mockRejectedValueOnce(mockError)
|
|
.mockResolvedValue('success')
|
|
const { run, loading, error } = useRetry(mockFn, {
|
|
retries: 3,
|
|
delay: 1000,
|
|
})
|
|
const promise = run()
|
|
expect(loading.value).toBe(true)
|
|
expect(error.value).toBe(null)
|
|
// First failure
|
|
await vi.advanceTimersByTime(0)
|
|
expect(error.value).toBe(mockError)
|
|
expect(loading.value).toBe(true)
|
|
// Second failure
|
|
await vi.advanceTimersByTime(1000)
|
|
expect(error.value).toBe(mockError)
|
|
expect(loading.value).toBe(true)
|
|
// Success on third try
|
|
await vi.advanceTimersByTime(1000)
|
|
const result = await promise
|
|
expect(result).toBe('success')
|
|
expect(loading.value).toBe(false)
|
|
expect(error.value).toBe(null)
|
|
expect(mockFn).toHaveBeenCalledTimes(3)
|
|
})
|
|
it('should throw after max retries', async () => {
|
|
const mockError = new Error('persistent error')
|
|
const mockFn = vi.fn().mockRejectedValue(mockError)
|
|
const { run, loading, error } = useRetry(mockFn, {
|
|
retries: 2,
|
|
delay: 500,
|
|
})
|
|
const promise = run()
|
|
expect(loading.value).toBe(true)
|
|
expect(error.value).toBe(null)
|
|
// First attempt
|
|
await vi.advanceTimersByTime(0)
|
|
expect(error.value).toBe(mockError)
|
|
expect(loading.value).toBe(true)
|
|
// First retry
|
|
await vi.advanceTimersByTime(500)
|
|
expect(error.value).toBe(mockError)
|
|
expect(loading.value).toBe(true)
|
|
// Second retry
|
|
await vi.advanceTimersByTime(500)
|
|
await expect(promise).rejects.toThrow(mockError)
|
|
expect(loading.value).toBe(false)
|
|
expect(error.value).toBe(mockError)
|
|
expect(mockFn).toHaveBeenCalledTimes(3)
|
|
})
|
|
it('should use default options when not provided', async () => {
|
|
const mockError = new Error('temporary error')
|
|
const mockFn = vi
|
|
.fn()
|
|
.mockRejectedValueOnce(mockError)
|
|
.mockRejectedValueOnce(mockError)
|
|
.mockRejectedValueOnce(mockError)
|
|
.mockResolvedValue('success')
|
|
const { run, loading, error } = useRetry(mockFn)
|
|
const promise = run()
|
|
expect(loading.value).toBe(true)
|
|
// First attempt
|
|
await vi.advanceTimersByTime(0)
|
|
expect(error.value).toBe(mockError)
|
|
// First retry
|
|
await vi.advanceTimersByTime(1000)
|
|
expect(error.value).toBe(mockError)
|
|
// Second retry
|
|
await vi.advanceTimersByTime(1000)
|
|
expect(error.value).toBe(mockError)
|
|
// Third retry
|
|
await vi.advanceTimersByTime(1000)
|
|
const result = await promise
|
|
expect(result).toBe('success')
|
|
expect(loading.value).toBe(false)
|
|
expect(error.value).toBe(null)
|
|
expect(mockFn).toHaveBeenCalledTimes(4)
|
|
})
|
|
it('should reset error state on each run', async () => {
|
|
const mockError = new Error('first run error')
|
|
const mockFn = vi.fn().mockRejectedValueOnce(mockError).mockResolvedValueOnce('second run success')
|
|
const { run, error } = useRetry(mockFn, { retries: 0 })
|
|
// First run fails
|
|
await expect(run()).rejects.toThrow(mockError)
|
|
expect(error.value).toBe(mockError)
|
|
// Second run succeeds
|
|
const result = await run()
|
|
expect(result).toBe('second run success')
|
|
expect(error.value).toBe(null)
|
|
})
|
|
it('should prevent concurrent executions', async () => {
|
|
const mockFn = vi.fn(async () => {
|
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
return 'success'
|
|
})
|
|
const { run, loading } = useRetry(mockFn)
|
|
// First execution
|
|
const promise1 = run()
|
|
expect(loading.value).toBe(true)
|
|
// Second execution during first one
|
|
const promise2 = run()
|
|
// Should reuse the first execution
|
|
expect(mockFn).toHaveBeenCalledTimes(1)
|
|
await vi.advanceTimersByTime(1000)
|
|
const [result1, result2] = await Promise.all([promise1, promise2])
|
|
expect(result1).toBe('success')
|
|
expect(result2).toBe('success')
|
|
expect(mockFn).toHaveBeenCalledTimes(1)
|
|
expect(loading.value).toBe(false)
|
|
})
|
|
})
|
|
//# sourceMappingURL=retry.spec.js.map
|