mirror of
https://github.com/katanemo/plano.git
synced 2026-05-21 13:55:15 +02:00
test: add property-based tests and integration tests for retry-on-ratelimit
Add 302 property-based unit tests (proptest, 100+ iterations each) and 13 integration test scenarios covering all retry behaviors. Unit tests cover: - Configuration round-trip parsing, defaults, and validation - Status code range expansion and error classification - Exponential backoff formula, bounds, and scope filtering - Provider selection strategy correctness and fallback ordering - Retry-After state scope behavior and max expiration updates - Cooldown exclusion invariants and initial selection cooldown - Bounded retry (max_attempts + budget enforcement) - Request preservation across retries - Latency trigger sliding window and block state management - Timeout vs high-latency precedence - Error response detail completeness Integration tests (tests/e2e/): - IT-1 through IT-13 covering 429/503 retry, exhaustion, backoff, fallback priority, Retry-After honoring, timeout retry, high-latency failover, streaming preservation, and body preservation
This commit is contained in:
parent
9870be530c
commit
98bf02456a
25 changed files with 10887 additions and 0 deletions
27
tests/e2e/configs/retry_it10_timeout_triggers_retry.yaml
Normal file
27
tests/e2e/configs/retry_it10_timeout_triggers_retry.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
fallback_models: [anthropic/claude-3-5-sonnet]
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
on_timeout:
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
33
tests/e2e/configs/retry_it11_high_latency_failover.yaml
Normal file
33
tests/e2e/configs/retry_it11_high_latency_failover.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
fallback_models: [anthropic/claude-3-5-sonnet]
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
on_high_latency:
|
||||
threshold_ms: 1000
|
||||
measure: "total"
|
||||
min_triggers: 1
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
block_duration_seconds: 60
|
||||
scope: "model"
|
||||
apply_to: "global"
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
23
tests/e2e/configs/retry_it12_streaming.yaml
Normal file
23
tests/e2e/configs/retry_it12_streaming.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
23
tests/e2e/configs/retry_it13_body_preserved.yaml
Normal file
23
tests/e2e/configs/retry_it13_body_preserved.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
23
tests/e2e/configs/retry_it1_basic_429.yaml
Normal file
23
tests/e2e/configs/retry_it1_basic_429.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
23
tests/e2e/configs/retry_it2_503_different_provider.yaml
Normal file
23
tests/e2e/configs/retry_it2_503_different_provider.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [503]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
23
tests/e2e/configs/retry_it3_all_exhausted.yaml
Normal file
23
tests/e2e/configs/retry_it3_all_exhausted.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
17
tests/e2e/configs/retry_it4_no_retry_policy.yaml
Normal file
17
tests/e2e/configs/retry_it4_no_retry_policy.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
# No retry_policy — errors should be returned directly to client
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
27
tests/e2e/configs/retry_it5_max_attempts.yaml
Normal file
27
tests/e2e/configs/retry_it5_max_attempts.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 1
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 1
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
|
||||
- model: mistral/mistral-large
|
||||
base_url: http://host.docker.internal:${MOCK_TERTIARY_PORT}
|
||||
access_key: test-key-tertiary
|
||||
24
tests/e2e/configs/retry_it6_backoff_delay.yaml
Normal file
24
tests/e2e/configs/retry_it6_backoff_delay.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "same_model"
|
||||
default_max_attempts: 3
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "same_model"
|
||||
max_attempts: 3
|
||||
backoff:
|
||||
apply_to: "same_model"
|
||||
base_ms: 500
|
||||
max_ms: 5000
|
||||
jitter: false
|
||||
28
tests/e2e/configs/retry_it7_fallback_priority.yaml
Normal file
28
tests/e2e/configs/retry_it7_fallback_priority.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
fallback_models: [anthropic/claude-3-5-sonnet, mistral/mistral-large]
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 3
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 3
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_FALLBACK1_PORT}
|
||||
access_key: test-key-fallback1
|
||||
|
||||
- model: mistral/mistral-large
|
||||
base_url: http://host.docker.internal:${MOCK_FALLBACK2_PORT}
|
||||
access_key: test-key-fallback2
|
||||
23
tests/e2e/configs/retry_it8_retry_after_honored.yaml
Normal file
23
tests/e2e/configs/retry_it8_retry_after_honored.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
default_strategy: "same_model"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "same_model"
|
||||
max_attempts: 2
|
||||
retry_after_handling:
|
||||
scope: "model"
|
||||
apply_to: "request"
|
||||
max_retry_after_seconds: 300
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
version: v0.3.0
|
||||
|
||||
listeners:
|
||||
- type: model
|
||||
name: model_listener
|
||||
port: 12000
|
||||
|
||||
model_providers:
|
||||
- model: openai/gpt-4o
|
||||
base_url: http://host.docker.internal:${MOCK_PRIMARY_PORT}
|
||||
access_key: test-key-primary
|
||||
default: true
|
||||
retry_policy:
|
||||
fallback_models: [anthropic/claude-3-5-sonnet]
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
retry_after_handling:
|
||||
scope: "model"
|
||||
apply_to: "global"
|
||||
max_retry_after_seconds: 300
|
||||
|
||||
- model: anthropic/claude-3-5-sonnet
|
||||
base_url: http://host.docker.internal:${MOCK_SECONDARY_PORT}
|
||||
access_key: test-key-secondary
|
||||
default: false
|
||||
retry_policy:
|
||||
default_strategy: "different_provider"
|
||||
default_max_attempts: 2
|
||||
on_status_codes:
|
||||
- codes: [429]
|
||||
strategy: "different_provider"
|
||||
max_attempts: 2
|
||||
Loading…
Add table
Add a link
Reference in a new issue