mirror of
https://github.com/allinssl/allinssl.git
synced 2025-12-18 10:04:01 +08:00
【同步】前端项目源码
【修复】工作流兼容问题
This commit is contained in:
139
frontend/packages/vue/hooks/test/retry.spec.js
Normal file
139
frontend/packages/vue/hooks/test/retry.spec.js
Normal file
@@ -0,0 +1,139 @@
|
||||
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
|
||||
Reference in New Issue
Block a user