mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
fix: 移除已删除的 Qwen 和 IFlow provider 引用
- 移除测试文件中对 ProviderType::Qwen 和 ProviderType::IFlow 的引用 - 移除 providers/tests.rs 中的 IFlowProvider 相关测试 - 更新 failover 测试使用 OpenAI 替代 Qwen - 添加 ChatMessage 缺失的 reasoning_content 字段 - 启用 macOS 签名证书配置 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,29 +79,29 @@ jobs:
|
||||
- name: Install frontend dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
# macOS 签名证书设置(暂时禁用,证书未配置)
|
||||
# - name: Import Apple Certificate
|
||||
# if: matrix.platform == 'macos-latest'
|
||||
# env:
|
||||
# APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
# APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
# run: |
|
||||
# # 创建临时 keychain
|
||||
# KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
# KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
|
||||
#
|
||||
# # 解码证书
|
||||
# echo -n "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12
|
||||
#
|
||||
# # 创建 keychain
|
||||
# security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
# security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||
# security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
#
|
||||
# # 导入证书
|
||||
# security import $RUNNER_TEMP/certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
# security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
# security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
# macOS 签名证书设置
|
||||
- name: Import Apple Certificate
|
||||
if: matrix.platform == 'macos-latest'
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
# 创建临时 keychain
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
KEYCHAIN_PASSWORD=${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
|
||||
# 解码证书
|
||||
echo -n "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12
|
||||
|
||||
# 创建 keychain
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
|
||||
# 导入证书
|
||||
security import $RUNNER_TEMP/certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Build Tauri app
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
@@ -109,13 +109,10 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CARGO_PROFILE_RELEASE_LTO: thin
|
||||
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 16
|
||||
# Apple 签名和公证环境变量(暂时禁用)
|
||||
# APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
# APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
# APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
# APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
# APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
# APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
# Apple 签名环境变量
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
with:
|
||||
tauriScript: npx tauri
|
||||
tagName: ${{ github.event.inputs.version || github.ref_name }}
|
||||
@@ -143,8 +140,8 @@ jobs:
|
||||
prerelease: false
|
||||
args: --target ${{ matrix.target }}
|
||||
|
||||
# 清理 keychain(暂时禁用)
|
||||
# - name: Cleanup Keychain
|
||||
# if: matrix.platform == 'macos-latest' && always()
|
||||
# run: |
|
||||
# security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
|
||||
# 清理 keychain
|
||||
- name: Cleanup Keychain
|
||||
if: matrix.platform == 'macos-latest' && always()
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
|
||||
|
||||
@@ -17,7 +17,6 @@ fn arb_provider_type() -> impl Strategy<Value = ProviderType> {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
]
|
||||
@@ -698,7 +697,6 @@ fn arb_sync_provider_type() -> impl Strategy<Value = PoolProviderType> {
|
||||
prop_oneof![
|
||||
Just(PoolProviderType::Kiro),
|
||||
Just(PoolProviderType::Gemini),
|
||||
Just(PoolProviderType::Qwen),
|
||||
Just(PoolProviderType::OpenAI),
|
||||
Just(PoolProviderType::Claude),
|
||||
]
|
||||
|
||||
@@ -1157,7 +1157,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
]
|
||||
|
||||
@@ -704,7 +704,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
]
|
||||
|
||||
@@ -1492,7 +1492,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
|
||||
@@ -1112,7 +1112,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
|
||||
@@ -1275,7 +1275,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
|
||||
@@ -1028,7 +1028,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
|
||||
@@ -864,7 +864,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
@@ -872,7 +871,6 @@ mod property_tests {
|
||||
Just(ProviderType::GeminiApiKey),
|
||||
Just(ProviderType::Codex),
|
||||
Just(ProviderType::ClaudeOAuth),
|
||||
Just(ProviderType::IFlow),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -957,7 +957,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
@@ -965,7 +964,6 @@ mod property_tests {
|
||||
Just(ProviderType::GeminiApiKey),
|
||||
Just(ProviderType::Codex),
|
||||
Just(ProviderType::ClaudeOAuth),
|
||||
Just(ProviderType::IFlow),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1789,7 +1789,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
|
||||
@@ -1036,7 +1036,6 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
Just(ProviderType::Antigravity),
|
||||
|
||||
@@ -240,7 +240,7 @@ mod property_tests {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
],
|
||||
"[a-z0-9-]{5,20}", // model
|
||||
)
|
||||
|
||||
@@ -648,7 +648,11 @@ mod tests {
|
||||
ctx.set_provider(ProviderType::Kiro);
|
||||
|
||||
let error = ProviderCallError::failover("Rate limit exceeded", Some(429));
|
||||
let available = vec![ProviderType::Kiro, ProviderType::Gemini, ProviderType::Qwen];
|
||||
let available = vec![
|
||||
ProviderType::Kiro,
|
||||
ProviderType::Gemini,
|
||||
ProviderType::OpenAI,
|
||||
];
|
||||
|
||||
let new_provider = step.handle_failover(&ctx, &error, &available);
|
||||
|
||||
|
||||
@@ -175,7 +175,6 @@ fn arb_provider_type() -> impl Strategy<Value = ProviderType> {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
]
|
||||
|
||||
@@ -6,7 +6,6 @@ use chrono::{Duration, Utc};
|
||||
use proptest::prelude::*;
|
||||
|
||||
use crate::providers::codex::CodexProvider;
|
||||
use crate::providers::iflow::IFlowProvider;
|
||||
use crate::providers::vertex::VertexProvider;
|
||||
|
||||
/// Generate a random lead time in minutes (1 to 30 minutes)
|
||||
@@ -79,10 +78,10 @@ proptest! {
|
||||
}
|
||||
|
||||
/// **Feature: cliproxyapi-parity, Property 2: Token Refresh Timing**
|
||||
/// Test that iFlow OAuth tokens trigger refresh before expiration
|
||||
/// Test that Codex OAuth tokens trigger refresh before expiration (second test)
|
||||
/// **Validates: Requirements 1.2, 2.2**
|
||||
#[test]
|
||||
fn test_iflow_token_refresh_timing(
|
||||
fn test_codex_token_refresh_timing_second(
|
||||
lead_time_mins in arb_lead_time_mins(),
|
||||
time_offset_secs in -3600i64..7200i64,
|
||||
) {
|
||||
@@ -92,8 +91,7 @@ proptest! {
|
||||
// 跳过边界条件,因为时间精度问题可能导致不确定行为
|
||||
prop_assume!(time_offset_secs != lead_time_secs);
|
||||
|
||||
let mut provider = IFlowProvider::new();
|
||||
provider.credentials.auth_type = "oauth".to_string();
|
||||
let mut provider = CodexProvider::new();
|
||||
provider.credentials.access_token = Some("test_token".to_string());
|
||||
|
||||
// Set expiration time relative to now
|
||||
@@ -109,7 +107,7 @@ proptest! {
|
||||
prop_assert_eq!(
|
||||
needs_refresh,
|
||||
expected_needs_refresh,
|
||||
"iFlow: Token with expiry in {} seconds should {} refresh with lead time of {} minutes",
|
||||
"Codex: Token with expiry in {} seconds should {} refresh with lead time of {} minutes",
|
||||
time_offset_secs,
|
||||
if expected_needs_refresh { "need" } else { "not need" },
|
||||
lead_time_mins
|
||||
@@ -157,33 +155,6 @@ proptest! {
|
||||
);
|
||||
}
|
||||
|
||||
/// **Feature: cliproxyapi-parity, Property 2: Token Refresh Timing**
|
||||
/// Test that iFlow cookie auth type never needs OAuth refresh
|
||||
/// **Validates: Requirements 2.2**
|
||||
#[test]
|
||||
fn test_iflow_cookie_auth_no_refresh(
|
||||
lead_time_mins in arb_lead_time_mins(),
|
||||
time_offset_secs in arb_time_offset_secs(),
|
||||
) {
|
||||
let lead_time = Duration::minutes(lead_time_mins);
|
||||
|
||||
let mut provider = IFlowProvider::new();
|
||||
provider.credentials.auth_type = "cookie".to_string();
|
||||
provider.credentials.cookies = Some("session=abc123".to_string());
|
||||
|
||||
// Even with expiry set, cookie auth should not trigger OAuth refresh
|
||||
let now = Utc::now();
|
||||
let expires_at = now + Duration::seconds(time_offset_secs);
|
||||
provider.credentials.expires_at = Some(expires_at.to_rfc3339());
|
||||
|
||||
let needs_refresh = provider.needs_refresh(lead_time);
|
||||
|
||||
prop_assert!(
|
||||
!needs_refresh,
|
||||
"iFlow: Cookie auth type should never need OAuth token refresh"
|
||||
);
|
||||
}
|
||||
|
||||
/// **Feature: cliproxyapi-parity, Property 2: Token Refresh Timing**
|
||||
/// Test that refresh is triggered strictly before expiration time
|
||||
/// This ensures the invariant: if needs_refresh(lead_time) is false,
|
||||
@@ -238,8 +209,7 @@ proptest! {
|
||||
///
|
||||
/// This test verifies that:
|
||||
/// 1. Codex provider correctly identifies GPT models (gpt-*, o1*, o3*, o4*, *codex*)
|
||||
/// 2. iFlow provider correctly identifies iFlow models (iflow*, *iflow*)
|
||||
/// 3. Vertex provider correctly resolves model aliases
|
||||
/// 2. Vertex provider correctly resolves model aliases
|
||||
#[test]
|
||||
fn test_codex_provider_routing_gpt_models(
|
||||
model_suffix in "[a-z0-9\\-]{1,10}",
|
||||
@@ -314,49 +284,6 @@ proptest! {
|
||||
);
|
||||
}
|
||||
|
||||
/// **Feature: cliproxyapi-parity, Property 3: Provider Routing Correctness**
|
||||
/// Test that iFlow provider correctly identifies iFlow models
|
||||
/// **Validates: Requirements 2.3**
|
||||
#[test]
|
||||
fn test_iflow_provider_routing_iflow_models(
|
||||
suffix in "[a-z0-9\\-]{1,10}",
|
||||
) {
|
||||
// Models starting with "iflow" should be supported
|
||||
let iflow_model = format!("iflow-{}", suffix);
|
||||
prop_assert!(
|
||||
IFlowProvider::supports_model(&iflow_model),
|
||||
"iFlow should support model starting with 'iflow': {}",
|
||||
iflow_model
|
||||
);
|
||||
|
||||
// Models containing "iflow" should be supported
|
||||
let containing_model = format!("my-iflow-{}", suffix);
|
||||
prop_assert!(
|
||||
IFlowProvider::supports_model(&containing_model),
|
||||
"iFlow should support model containing 'iflow': {}",
|
||||
containing_model
|
||||
);
|
||||
}
|
||||
|
||||
/// **Feature: cliproxyapi-parity, Property 3: Provider Routing Correctness**
|
||||
/// Test that iFlow provider does NOT support non-iFlow models
|
||||
/// **Validates: Requirements 2.3**
|
||||
#[test]
|
||||
fn test_iflow_provider_routing_non_iflow_models(
|
||||
model in prop_oneof![
|
||||
Just("gpt-4"),
|
||||
Just("claude-3"),
|
||||
Just("gemini-pro"),
|
||||
Just("llama-2"),
|
||||
],
|
||||
) {
|
||||
prop_assert!(
|
||||
!IFlowProvider::supports_model(&model),
|
||||
"iFlow should NOT support non-iFlow model: {}",
|
||||
model
|
||||
);
|
||||
}
|
||||
|
||||
/// **Feature: cliproxyapi-parity, Property 3: Provider Routing Correctness**
|
||||
/// Test that Vertex provider correctly resolves model aliases
|
||||
/// **Validates: Requirements 3.2, 3.3**
|
||||
@@ -972,31 +899,4 @@ mod unit_tests {
|
||||
"Expired token should need refresh"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iflow_needs_refresh_boundary() {
|
||||
let mut provider = IFlowProvider::new();
|
||||
provider.credentials.auth_type = "oauth".to_string();
|
||||
provider.credentials.access_token = Some("test_token".to_string());
|
||||
|
||||
let lead_time = Duration::minutes(5);
|
||||
|
||||
// Token expiring well after lead_time - should NOT need refresh
|
||||
let now = Utc::now();
|
||||
let expires_at = now + Duration::minutes(10);
|
||||
provider.credentials.expires_at = Some(expires_at.to_rfc3339());
|
||||
assert!(
|
||||
!provider.needs_refresh(lead_time),
|
||||
"Token expiring in 10 mins should not need refresh with 5 min lead time"
|
||||
);
|
||||
|
||||
// Token expiring well before lead_time - should need refresh
|
||||
let now = Utc::now();
|
||||
let expires_at = now + Duration::minutes(2);
|
||||
provider.credentials.expires_at = Some(expires_at.to_rfc3339());
|
||||
assert!(
|
||||
provider.needs_refresh(lead_time),
|
||||
"Token expiring in 2 mins should need refresh with 5 min lead time"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +490,11 @@ mod unit_tests {
|
||||
#[test]
|
||||
fn test_handle_failure_quota_exceeded() {
|
||||
let failover = Failover::with_defaults();
|
||||
let available = vec![ProviderType::Kiro, ProviderType::Gemini, ProviderType::Qwen];
|
||||
let available = vec![
|
||||
ProviderType::Kiro,
|
||||
ProviderType::Gemini,
|
||||
ProviderType::OpenAI,
|
||||
];
|
||||
|
||||
let result = failover.handle_failure(
|
||||
ProviderType::Kiro,
|
||||
@@ -569,7 +573,11 @@ mod unit_tests {
|
||||
#[test]
|
||||
fn test_select_alternative() {
|
||||
let failover = Failover::with_defaults();
|
||||
let available = vec![ProviderType::Kiro, ProviderType::Gemini, ProviderType::Qwen];
|
||||
let available = vec![
|
||||
ProviderType::Kiro,
|
||||
ProviderType::Gemini,
|
||||
ProviderType::OpenAI,
|
||||
];
|
||||
|
||||
// 排除 Kiro,应该选择 Gemini
|
||||
let result = failover.select_alternative(ProviderType::Kiro, &available);
|
||||
@@ -583,14 +591,18 @@ mod unit_tests {
|
||||
#[test]
|
||||
fn test_select_alternative_excluding() {
|
||||
let failover = Failover::with_defaults();
|
||||
let available = vec![ProviderType::Kiro, ProviderType::Gemini, ProviderType::Qwen];
|
||||
let available = vec![
|
||||
ProviderType::Kiro,
|
||||
ProviderType::Gemini,
|
||||
ProviderType::OpenAI,
|
||||
];
|
||||
|
||||
let mut excluded = HashSet::new();
|
||||
excluded.insert(ProviderType::Kiro);
|
||||
excluded.insert(ProviderType::Gemini);
|
||||
|
||||
let result = failover.select_alternative_excluding(&excluded, &available);
|
||||
assert_eq!(result, Some(ProviderType::Qwen));
|
||||
assert_eq!(result, Some(ProviderType::OpenAI));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -638,7 +650,11 @@ mod manager_tests {
|
||||
#[test]
|
||||
fn test_failover_manager_tracks_failed_providers() {
|
||||
let mut manager = FailoverManager::with_defaults();
|
||||
let available = vec![ProviderType::Kiro, ProviderType::Gemini, ProviderType::Qwen];
|
||||
let available = vec![
|
||||
ProviderType::Kiro,
|
||||
ProviderType::Gemini,
|
||||
ProviderType::OpenAI,
|
||||
];
|
||||
|
||||
// 第一次失败
|
||||
let result = manager.handle_failure_and_switch(
|
||||
@@ -659,12 +675,12 @@ mod manager_tests {
|
||||
&available,
|
||||
);
|
||||
assert!(result.switched);
|
||||
assert_eq!(result.new_provider, Some(ProviderType::Qwen));
|
||||
assert_eq!(result.new_provider, Some(ProviderType::OpenAI));
|
||||
assert!(manager.is_provider_failed(ProviderType::Gemini));
|
||||
|
||||
// 第三次失败(所有 Provider 都失败了)
|
||||
let result = manager.handle_failure_and_switch(
|
||||
ProviderType::Qwen,
|
||||
ProviderType::OpenAI,
|
||||
Some(429),
|
||||
"Rate limit",
|
||||
&available,
|
||||
@@ -676,7 +692,11 @@ mod manager_tests {
|
||||
#[test]
|
||||
fn test_failover_manager_switch_log() {
|
||||
let mut manager = FailoverManager::with_defaults();
|
||||
let available = vec![ProviderType::Kiro, ProviderType::Gemini, ProviderType::Qwen];
|
||||
let available = vec![
|
||||
ProviderType::Kiro,
|
||||
ProviderType::Gemini,
|
||||
ProviderType::OpenAI,
|
||||
];
|
||||
|
||||
// 触发两次切换
|
||||
manager.handle_failure_and_switch(ProviderType::Kiro, Some(429), "Rate limit", &available);
|
||||
@@ -695,7 +715,7 @@ mod manager_tests {
|
||||
assert_eq!(log[0].failure_type, FailureType::QuotaExceeded);
|
||||
|
||||
assert_eq!(log[1].from_provider, ProviderType::Gemini);
|
||||
assert_eq!(log[1].to_provider, ProviderType::Qwen);
|
||||
assert_eq!(log[1].to_provider, ProviderType::OpenAI);
|
||||
assert_eq!(log[1].failure_type, FailureType::ServiceUnavailable);
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,7 @@ mod tests {
|
||||
)),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
reasoning_content: None,
|
||||
}],
|
||||
temperature: None,
|
||||
max_tokens: None,
|
||||
@@ -199,6 +200,7 @@ mod tests {
|
||||
)),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
reasoning_content: None,
|
||||
}],
|
||||
temperature: None,
|
||||
max_tokens: None,
|
||||
@@ -218,6 +220,7 @@ mod tests {
|
||||
)),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
reasoning_content: None,
|
||||
}],
|
||||
temperature: None,
|
||||
max_tokens: None,
|
||||
|
||||
@@ -15,7 +15,6 @@ fn arb_provider_type() -> impl Strategy<Value = ProviderType> {
|
||||
prop_oneof![
|
||||
Just(ProviderType::Kiro),
|
||||
Just(ProviderType::Gemini),
|
||||
Just(ProviderType::Qwen),
|
||||
Just(ProviderType::OpenAI),
|
||||
Just(ProviderType::Claude),
|
||||
]
|
||||
|
||||
@@ -634,6 +634,7 @@ mod tests {
|
||||
content: Some(MessageContent::Text("Hello".to_string())),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
reasoning_content: None,
|
||||
}],
|
||||
tools: None,
|
||||
stream: false,
|
||||
|
||||
Reference in New Issue
Block a user