mirror of
https://github.com/langgenius/dify.git
synced 2026-09-06 09:14:25 +08:00
86 lines
2.7 KiB
TypeScript
86 lines
2.7 KiB
TypeScript
import type { FetchContext, HttpMethod, ResolvedOptions } from './types.js'
|
|
import { describe, expect, it } from 'vite-plus/test'
|
|
import { backoffDelay, isIdempotentRetryMethod, shouldRetry } from './retry.js'
|
|
|
|
function ctxFor(method: HttpMethod): FetchContext {
|
|
const options: ResolvedOptions = {
|
|
method,
|
|
headers: new Headers(),
|
|
body: undefined,
|
|
timeoutMs: undefined,
|
|
retryAttempts: 0,
|
|
throwOnError: true,
|
|
retryOnRateLimit: false,
|
|
}
|
|
return {
|
|
request: new Request('https://x/y', { method }),
|
|
options,
|
|
attempt: 0,
|
|
meta: new Map(),
|
|
}
|
|
}
|
|
|
|
describe('shouldRetry', () => {
|
|
it('retries retryable status codes on GET', () => {
|
|
const res = new Response(null, { status: 503 })
|
|
expect(shouldRetry(res, ctxFor('GET'))).toBe(true)
|
|
})
|
|
|
|
it('does not retry non-retryable status codes on GET', () => {
|
|
const res = new Response(null, { status: 404 })
|
|
expect(shouldRetry(res, ctxFor('GET'))).toBe(false)
|
|
})
|
|
|
|
it('no longer retries 429 here (it has a dedicated branch in execute())', () => {
|
|
const res = new Response(null, { status: 429 })
|
|
expect(shouldRetry(res, ctxFor('GET'))).toBe(false)
|
|
})
|
|
|
|
it('does not retry POST regardless of status', () => {
|
|
const res = new Response(null, { status: 503 })
|
|
expect(shouldRetry(res, ctxFor('POST'))).toBe(false)
|
|
})
|
|
|
|
it('does not retry PATCH regardless of status', () => {
|
|
const res = new Response(null, { status: 503 })
|
|
expect(shouldRetry(res, ctxFor('PATCH'))).toBe(false)
|
|
})
|
|
|
|
it('retries transport errors on retryable methods', () => {
|
|
expect(shouldRetry(new Error('econnreset'), ctxFor('GET'))).toBe(true)
|
|
expect(shouldRetry(new Error('econnreset'), ctxFor('PUT'))).toBe(true)
|
|
})
|
|
|
|
it('does not retry transport errors on non-retryable methods', () => {
|
|
expect(shouldRetry(new Error('econnreset'), ctxFor('POST'))).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('isIdempotentRetryMethod', () => {
|
|
it('is true for GET/PUT/DELETE and false for POST/PATCH', () => {
|
|
expect(isIdempotentRetryMethod('GET')).toBe(true)
|
|
expect(isIdempotentRetryMethod('PUT')).toBe(true)
|
|
expect(isIdempotentRetryMethod('DELETE')).toBe(true)
|
|
expect(isIdempotentRetryMethod('POST')).toBe(false)
|
|
expect(isIdempotentRetryMethod('PATCH')).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('backoffDelay', () => {
|
|
it('returns 0 for attempts <= 0', () => {
|
|
expect(backoffDelay(0)).toBe(0)
|
|
expect(backoffDelay(-1)).toBe(0)
|
|
})
|
|
|
|
it('grows exponentially from a 300 ms base', () => {
|
|
expect(backoffDelay(1)).toBe(300)
|
|
expect(backoffDelay(2)).toBe(600)
|
|
expect(backoffDelay(3)).toBe(1200)
|
|
expect(backoffDelay(4)).toBe(2400)
|
|
})
|
|
|
|
it('caps at 30 s', () => {
|
|
expect(backoffDelay(20)).toBe(30_000)
|
|
})
|
|
})
|