mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 18:07:47 +08:00
feat(security): harden gateway boundaries and usage policies
Consolidate subscription usage policy enforcement, privacy-safe persistence, and gateway security hardening into one reviewable change. Includes bounded HTTP and execution envelopes, header and protocol guards, DNS and relay validation, authentication and secret projection hardening, secure backup/install paths, and regression coverage.
This commit is contained in:
@@ -20,8 +20,8 @@ use serde_json::json;
|
||||
|
||||
use super::super::{
|
||||
build_router_with_state, build_state_with_execution_runtime_override,
|
||||
sample_admin_provider_model, sample_endpoint, sample_key, sample_provider, start_server,
|
||||
AppState,
|
||||
sample_admin_provider_model, sample_bound_auth_config, sample_bound_key, sample_endpoint,
|
||||
sample_provider, start_server, AppState,
|
||||
};
|
||||
use crate::constants::{
|
||||
GATEWAY_HEADER, TRUSTED_ADMIN_SESSION_ID_HEADER, TRUSTED_ADMIN_USER_ID_HEADER,
|
||||
@@ -216,7 +216,7 @@ async fn gateway_handles_admin_provider_query_models_fetches_upstream_for_select
|
||||
None,
|
||||
)
|
||||
.expect("endpoint transport should build")],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-selected",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -342,7 +342,7 @@ async fn gateway_handles_admin_provider_query_models_fetches_windsurf_model_conf
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-windsurf", "Windsurf", 10);
|
||||
provider.provider_type = "windsurf".to_string();
|
||||
let mut windsurf_key = sample_key(
|
||||
let mut windsurf_key = sample_bound_key(
|
||||
"key-windsurf-selected",
|
||||
"provider-windsurf",
|
||||
"openai:chat",
|
||||
@@ -487,7 +487,7 @@ async fn gateway_handles_admin_provider_query_models_with_openai_responses_endpo
|
||||
None,
|
||||
)
|
||||
.expect("endpoint transport should build")],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-responses",
|
||||
"provider-openai",
|
||||
"openai:responses",
|
||||
@@ -600,7 +600,7 @@ async fn gateway_recovers_codex_slug_only_models_from_an_empty_legacy_cache_impl
|
||||
"openai:responses",
|
||||
"https://chatgpt.com/backend-api/codex",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-codex-dynamic",
|
||||
"provider-codex-dynamic",
|
||||
"openai:responses",
|
||||
@@ -743,7 +743,7 @@ async fn gateway_handles_admin_provider_query_models_falls_back_to_codex_preset_
|
||||
"openai:responses",
|
||||
"https://chatgpt.com/backend-api/codex",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-codex-invalidated",
|
||||
"provider-codex",
|
||||
"openai:responses",
|
||||
@@ -782,7 +782,13 @@ async fn gateway_handles_admin_provider_query_models_falls_back_to_codex_preset_
|
||||
.as_str()
|
||||
.expect("Codex fallback warning should be present");
|
||||
assert!(warning.contains("Codex 动态模型目录不可用"));
|
||||
assert!(warning.contains("invalidated"));
|
||||
// Model-fetch diagnostics are intentionally projected to a credential-safe
|
||||
// category before being returned from the admin endpoint. The raw
|
||||
// upstream invalidation text must not cross the response boundary.
|
||||
assert!(
|
||||
warning.contains("authorization failed (status 403)"),
|
||||
"warning={warning}"
|
||||
);
|
||||
let model_ids = payload["data"]["models"]
|
||||
.as_array()
|
||||
.expect("models should be an array")
|
||||
@@ -900,7 +906,7 @@ async fn gateway_handles_admin_provider_query_models_respecting_key_api_formats_
|
||||
)
|
||||
.expect("endpoint transport should build"),
|
||||
],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-cli",
|
||||
"provider-openai",
|
||||
"openai:responses",
|
||||
@@ -1036,13 +1042,13 @@ async fn gateway_handles_admin_provider_query_models_aggregating_active_keys_imp
|
||||
)
|
||||
.expect("endpoint transport should build")],
|
||||
vec![
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-1",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
"sk-test-1",
|
||||
),
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-2",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -1131,7 +1137,7 @@ async fn gateway_handles_admin_provider_query_models_for_fixed_provider_without_
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-codex-oauth",
|
||||
"provider-codex",
|
||||
"openai:responses",
|
||||
@@ -1251,7 +1257,7 @@ async fn gateway_handles_admin_provider_query_test_model_locally_with_trusted_ad
|
||||
"openai:chat",
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-primary",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -1365,7 +1371,7 @@ async fn gateway_handles_admin_provider_query_embedding_model_test_impl() {
|
||||
"openai:embedding",
|
||||
"https://api.siliconflow.example",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-siliconflow-embedding",
|
||||
"provider-siliconflow",
|
||||
"openai:embedding",
|
||||
@@ -1486,7 +1492,7 @@ async fn gateway_handles_admin_provider_query_doubao_text_embedding_model_test_i
|
||||
"doubao:embedding",
|
||||
"https://ark.volces.example/api/v3",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-doubao-embedding",
|
||||
"provider-doubao",
|
||||
"doubao:embedding",
|
||||
@@ -1608,7 +1614,7 @@ async fn gateway_handles_admin_provider_query_gemini_embedding_model_test_impl()
|
||||
"gemini:embedding",
|
||||
"https://generativelanguage.googleapis.com/v1beta",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-gemini-embedding",
|
||||
"provider-gemini",
|
||||
"gemini:embedding",
|
||||
@@ -1726,7 +1732,7 @@ async fn gateway_handles_admin_provider_query_vertex_gemini_embedding_model_test
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-vertex-ai", "Vertex AI", 10);
|
||||
provider.provider_type = "vertex_ai".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-vertex-gemini-embedding",
|
||||
"provider-vertex-ai",
|
||||
"gemini:embedding",
|
||||
@@ -1873,7 +1879,7 @@ async fn gateway_handles_admin_provider_query_jina_embedding_model_test_impl() {
|
||||
"jina:embedding",
|
||||
"https://api.jina.example",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-jina-embedding",
|
||||
"provider-jina-embedding",
|
||||
"jina:embedding",
|
||||
@@ -1995,7 +2001,7 @@ async fn gateway_handles_admin_provider_query_openai_rerank_model_test_impl() {
|
||||
"openai:rerank",
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-rerank",
|
||||
"provider-openai-rerank",
|
||||
"openai:rerank",
|
||||
@@ -2123,7 +2129,7 @@ async fn gateway_handles_admin_provider_query_rerank_model_test_impl() {
|
||||
"jina:rerank",
|
||||
"https://api.jina.example",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-jina-rerank",
|
||||
"provider-jina",
|
||||
"jina:rerank",
|
||||
@@ -2254,7 +2260,7 @@ async fn gateway_maps_admin_provider_model_before_model_list_test_request_impl()
|
||||
"openai:chat",
|
||||
"https://api.minimax.example",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-minimax-primary",
|
||||
"provider-minimax",
|
||||
"openai:chat",
|
||||
@@ -2498,7 +2504,7 @@ async fn gateway_streams_codex_openai_responses_upstream_for_admin_pool_model_te
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![endpoint],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-codex-primary",
|
||||
"provider-codex",
|
||||
"openai:responses",
|
||||
@@ -2656,20 +2662,18 @@ async fn gateway_executes_codex_search_admin_pool_model_test_with_search_contrac
|
||||
"https://chatgpt.com/backend-api/codex",
|
||||
);
|
||||
endpoint.config = Some(json!({"upstream_stream_policy": "force_stream"}));
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-codex-search",
|
||||
"provider-codex-search",
|
||||
"openai:search",
|
||||
"codex-search-access-token",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{"provider_type":"codex","account_id":"account-search-admin","is_fedramp":true}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-codex-search",
|
||||
"key-codex-search",
|
||||
r#"{"provider_type":"codex","account_id":"account-search-admin","is_fedramp":true}"#,
|
||||
));
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![endpoint],
|
||||
@@ -2796,24 +2800,22 @@ async fn gateway_routes_grok_responses_admin_pool_model_test_through_grok_runtim
|
||||
let mut provider = sample_provider("provider-grok", "Grok", 10);
|
||||
provider.provider_type = "grok".to_string();
|
||||
provider.config = Some(json!({"pool_advanced": {}}));
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-grok-oauth",
|
||||
"provider-grok",
|
||||
"openai:responses",
|
||||
"__placeholder__",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-grok",
|
||||
"key-grok-oauth",
|
||||
r#"{
|
||||
"provider_type":"grok",
|
||||
"sso_token":"grok-sso",
|
||||
"sso_rw_token":"grok-rw"
|
||||
}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
));
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![sample_endpoint(
|
||||
@@ -2933,7 +2935,7 @@ async fn gateway_streams_windsurf_connect_upstream_for_admin_model_test_impl() {
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-windsurf", "Windsurf", 10);
|
||||
provider.provider_type = "windsurf".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-windsurf-primary",
|
||||
"provider-windsurf",
|
||||
"openai:chat",
|
||||
@@ -3045,7 +3047,7 @@ async fn gateway_uses_pool_scheduler_order_for_admin_pool_model_test_impl() {
|
||||
]
|
||||
}
|
||||
}));
|
||||
let mut free_key = sample_key(
|
||||
let mut free_key = sample_bound_key(
|
||||
"key-codex-free",
|
||||
"provider-codex",
|
||||
"openai:responses",
|
||||
@@ -3060,7 +3062,7 @@ async fn gateway_uses_pool_scheduler_order_for_admin_pool_model_test_impl() {
|
||||
"usage_ratio": 0.1
|
||||
}
|
||||
}));
|
||||
let mut plus_key = sample_key(
|
||||
let mut plus_key = sample_bound_key(
|
||||
"key-codex-plus",
|
||||
"provider-codex",
|
||||
"openai:responses",
|
||||
@@ -3253,13 +3255,13 @@ async fn gateway_handles_admin_provider_query_test_model_failover_locally_with_t
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-first",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
"sk-test-first",
|
||||
),
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-second",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -3382,17 +3384,17 @@ async fn gateway_handles_admin_provider_query_test_model_for_kiro_locally_impl()
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-kiro", "Kiro", 10);
|
||||
provider.provider_type = "kiro".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-kiro-primary",
|
||||
"provider-kiro",
|
||||
"claude:messages",
|
||||
"__placeholder__",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-kiro",
|
||||
"key-kiro-primary",
|
||||
r#"{
|
||||
"provider_type":"kiro",
|
||||
"auth_method":"idc",
|
||||
"access_token":"cached-kiro-token",
|
||||
@@ -3403,9 +3405,7 @@ async fn gateway_handles_admin_provider_query_test_model_for_kiro_locally_impl()
|
||||
"client_id":"client-id",
|
||||
"client_secret":"client-secret"
|
||||
}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
));
|
||||
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
@@ -3518,17 +3518,17 @@ async fn gateway_uses_kiro_mapped_model_name_for_explicit_model_mapping_test_imp
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-kiro", "Kiro", 10);
|
||||
provider.provider_type = "kiro".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-kiro-primary",
|
||||
"provider-kiro",
|
||||
"claude:messages",
|
||||
"__placeholder__",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-kiro",
|
||||
"key-kiro-primary",
|
||||
r#"{
|
||||
"provider_type":"kiro",
|
||||
"auth_method":"idc",
|
||||
"access_token":"cached-kiro-token",
|
||||
@@ -3539,9 +3539,7 @@ async fn gateway_uses_kiro_mapped_model_name_for_explicit_model_mapping_test_imp
|
||||
"client_id":"client-id",
|
||||
"client_secret":"client-secret"
|
||||
}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
));
|
||||
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
@@ -3728,12 +3726,12 @@ async fn gateway_handles_admin_provider_query_test_model_failover_for_kiro_local
|
||||
let mut provider = sample_provider("provider-kiro", "Kiro", 10);
|
||||
provider.provider_type = "kiro".to_string();
|
||||
let build_key = |id: &str| {
|
||||
let mut key = sample_key(id, "provider-kiro", "claude:messages", "__placeholder__");
|
||||
let mut key = sample_bound_key(id, "provider-kiro", "claude:messages", "__placeholder__");
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-kiro",
|
||||
id,
|
||||
r#"{
|
||||
"provider_type":"kiro",
|
||||
"auth_method":"idc",
|
||||
"access_token":"cached-kiro-token",
|
||||
@@ -3744,9 +3742,7 @@ async fn gateway_handles_admin_provider_query_test_model_failover_for_kiro_local
|
||||
"client_id":"client-id",
|
||||
"client_secret":"client-secret"
|
||||
}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
));
|
||||
key
|
||||
};
|
||||
|
||||
@@ -3890,12 +3886,12 @@ async fn gateway_retries_kiro_failover_after_http_error_without_message_impl() {
|
||||
let mut provider = sample_provider("provider-kiro", "Kiro", 10);
|
||||
provider.provider_type = "kiro".to_string();
|
||||
let build_key = |id: &str| {
|
||||
let mut key = sample_key(id, "provider-kiro", "claude:messages", "__placeholder__");
|
||||
let mut key = sample_bound_key(id, "provider-kiro", "claude:messages", "__placeholder__");
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-kiro",
|
||||
id,
|
||||
r#"{
|
||||
"provider_type":"kiro",
|
||||
"auth_method":"idc",
|
||||
"access_token":"cached-kiro-token",
|
||||
@@ -3906,9 +3902,7 @@ async fn gateway_retries_kiro_failover_after_http_error_without_message_impl() {
|
||||
"client_id":"client-id",
|
||||
"client_secret":"client-secret"
|
||||
}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
));
|
||||
key
|
||||
};
|
||||
|
||||
@@ -4055,7 +4049,7 @@ async fn gateway_handles_non_kiro_multi_model_failover_locally_impl() {
|
||||
"openai:chat",
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-primary",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -4235,7 +4229,7 @@ async fn gateway_handles_openai_responses_test_model_locally_impl() {
|
||||
"openai:responses",
|
||||
"https://tiger.bookapi.cc/codex",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-cli",
|
||||
"provider-openai",
|
||||
"openai:responses",
|
||||
@@ -4367,7 +4361,7 @@ async fn gateway_handles_openai_image_test_model_locally_impl() {
|
||||
"openai:image",
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-image",
|
||||
"provider-openai",
|
||||
"openai:image",
|
||||
@@ -4431,7 +4425,7 @@ async fn gateway_reports_transport_unsupported_reason_for_non_kiro_provider_impl
|
||||
"gemini:generate_content",
|
||||
"https://cloudcode-pa.googleapis.com",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-antigravity-gemini",
|
||||
"provider-antigravity",
|
||||
"gemini:generate_content",
|
||||
@@ -4582,26 +4576,24 @@ async fn gateway_handles_antigravity_endpoint_test_model_locally_impl() {
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-antigravity", "Antigravity", 10);
|
||||
provider.provider_type = "antigravity".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-antigravity-gemini",
|
||||
"provider-antigravity",
|
||||
"gemini:generate_content",
|
||||
"cached-antigravity-token",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-antigravity",
|
||||
"key-antigravity-gemini",
|
||||
r#"{
|
||||
"provider_type":"antigravity",
|
||||
"project_id":"project-ant-123",
|
||||
"client_version":"1.2.3",
|
||||
"session_id":"sess-ant-123",
|
||||
"refresh_token":"rt-ant-123"
|
||||
}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
));
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![sample_endpoint(
|
||||
@@ -4764,20 +4756,18 @@ async fn gateway_hydrates_antigravity_project_id_from_load_code_assist_for_test_
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-antigravity", "Antigravity", 10);
|
||||
provider.provider_type = "antigravity".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-antigravity-gemini",
|
||||
"provider-antigravity",
|
||||
"gemini:generate_content",
|
||||
"cached-antigravity-token",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{"provider_type":"antigravity","refresh_token":"rt-antigravity-123"}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-antigravity",
|
||||
"key-antigravity-gemini",
|
||||
r#"{"provider_type":"antigravity","refresh_token":"rt-antigravity-123"}"#,
|
||||
));
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![sample_endpoint(
|
||||
@@ -4906,13 +4896,13 @@ async fn gateway_prefers_supported_non_kiro_endpoint_when_api_format_is_omitted_
|
||||
),
|
||||
],
|
||||
vec![
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-cli",
|
||||
"provider-openai",
|
||||
"openai:responses",
|
||||
"sk-test-cli",
|
||||
),
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-chat",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -5020,7 +5010,7 @@ async fn gateway_prefers_transport_supported_non_kiro_endpoint_when_api_format_i
|
||||
"https://api.openai.example/v1",
|
||||
),
|
||||
],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-chat",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -5123,7 +5113,7 @@ async fn gateway_prefers_supported_non_kiro_endpoint_with_compatible_key_when_ap
|
||||
"https://api.openai.example/v1",
|
||||
),
|
||||
],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-chat",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -5225,7 +5215,7 @@ async fn gateway_uses_compatible_cli_endpoint_when_api_format_is_omitted_impl()
|
||||
"https://api.openai.example/v1",
|
||||
),
|
||||
],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-cli",
|
||||
"provider-openai",
|
||||
"openai:responses",
|
||||
@@ -5325,7 +5315,7 @@ async fn gateway_uses_runnable_cli_endpoint_after_chat_preference_when_api_forma
|
||||
"openai:responses",
|
||||
"https://api.openai.example/v1",
|
||||
);
|
||||
let mut shared_key = sample_key(
|
||||
let mut shared_key = sample_bound_key(
|
||||
"key-openai-shared",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -5426,7 +5416,7 @@ async fn gateway_handles_openai_responses_test_model_failover_locally_impl() {
|
||||
"openai:responses",
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-cli",
|
||||
"provider-openai",
|
||||
"openai:responses",
|
||||
@@ -5527,7 +5517,7 @@ async fn gateway_handles_claude_cli_test_model_locally_impl() {
|
||||
"claude:messages",
|
||||
"https://api.anthropic.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-claude-cli",
|
||||
"provider-claude",
|
||||
"claude:messages",
|
||||
@@ -5622,7 +5612,7 @@ async fn gateway_uses_compatible_claude_cli_endpoint_when_api_format_is_omitted_
|
||||
"claude:messages",
|
||||
"https://api.anthropic.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-claude-cli",
|
||||
"provider-claude",
|
||||
"claude:messages",
|
||||
@@ -5718,7 +5708,7 @@ async fn gateway_handles_claude_cli_test_model_failover_locally_impl() {
|
||||
"claude:messages",
|
||||
"https://api.anthropic.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-claude-cli",
|
||||
"provider-claude",
|
||||
"claude:messages",
|
||||
@@ -5822,7 +5812,7 @@ async fn gateway_handles_gemini_cli_test_model_locally_impl() {
|
||||
"gemini:generate_content",
|
||||
"https://generativelanguage.googleapis.com",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-gemini-cli",
|
||||
"provider-gemini",
|
||||
"gemini:generate_content",
|
||||
@@ -5931,20 +5921,18 @@ async fn gateway_handles_gemini_cli_test_model_with_oauth_header_fallback_impl()
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-gemini", "Gemini", 10);
|
||||
provider.provider_type = "gemini_cli".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-gemini-cli",
|
||||
"provider-gemini",
|
||||
"gemini:generate_content",
|
||||
"cached-gemini-cli-token",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{"provider_type":"gemini_cli","project_id":"project-1"}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-gemini",
|
||||
"key-gemini-cli",
|
||||
r#"{"provider_type":"gemini_cli","project_id":"project-1"}"#,
|
||||
));
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![sample_endpoint(
|
||||
@@ -6086,20 +6074,18 @@ async fn gateway_hydrates_gemini_cli_project_id_from_load_code_assist_for_test_m
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-gemini", "Gemini", 10);
|
||||
provider.provider_type = "gemini_cli".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-gemini-cli",
|
||||
"provider-gemini",
|
||||
"gemini:generate_content",
|
||||
"cached-gemini-cli-token",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{"provider_type":"gemini_cli","refresh_token":"rt-gemini-cli-123"}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-gemini",
|
||||
"key-gemini-cli",
|
||||
r#"{"provider_type":"gemini_cli","refresh_token":"rt-gemini-cli-123"}"#,
|
||||
));
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![sample_endpoint(
|
||||
@@ -6219,7 +6205,7 @@ async fn gateway_uses_compatible_gemini_cli_endpoint_when_api_format_is_omitted_
|
||||
"gemini:generate_content",
|
||||
"https://generativelanguage.googleapis.com",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-gemini-cli",
|
||||
"provider-gemini",
|
||||
"gemini:generate_content",
|
||||
@@ -6315,7 +6301,7 @@ async fn gateway_handles_gemini_cli_test_model_failover_locally_impl() {
|
||||
"gemini:generate_content",
|
||||
"https://generativelanguage.googleapis.com",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-gemini-cli",
|
||||
"provider-gemini",
|
||||
"gemini:generate_content",
|
||||
@@ -6431,20 +6417,18 @@ async fn gateway_unwraps_gemini_cli_v1internal_response_for_failover_model_test_
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let mut provider = sample_provider("provider-gemini-cli", "Gemini CLI", 10);
|
||||
provider.provider_type = "gemini_cli".to_string();
|
||||
let mut key = sample_key(
|
||||
let mut key = sample_bound_key(
|
||||
"key-gemini-cli",
|
||||
"provider-gemini-cli",
|
||||
"gemini:generate_content",
|
||||
"cached-gemini-cli-token",
|
||||
);
|
||||
key.auth_type = "oauth".to_string();
|
||||
key.encrypted_auth_config = Some(
|
||||
aether_crypto::encrypt_python_fernet_plaintext(
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
r#"{"provider_type":"gemini_cli","project_id":"project-1"}"#,
|
||||
)
|
||||
.expect("auth config should encrypt"),
|
||||
);
|
||||
key.encrypted_auth_config = Some(sample_bound_auth_config(
|
||||
"provider-gemini-cli",
|
||||
"key-gemini-cli",
|
||||
r#"{"provider_type":"gemini_cli","project_id":"project-1"}"#,
|
||||
));
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
vec![sample_endpoint(
|
||||
@@ -6546,7 +6530,7 @@ async fn gateway_handles_admin_provider_query_test_model_failover_with_single_mo
|
||||
"openai:chat",
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![sample_key(
|
||||
vec![sample_bound_key(
|
||||
"key-openai-alias",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -6667,13 +6651,13 @@ async fn gateway_retries_non_kiro_failover_after_http_error_without_message_impl
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-first",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
"sk-test-first",
|
||||
),
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-second",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -6794,13 +6778,13 @@ async fn gateway_retries_non_kiro_failover_after_success_status_without_body_imp
|
||||
"https://api.openai.example/v1",
|
||||
)],
|
||||
vec![
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-first",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
"sk-test-first",
|
||||
),
|
||||
sample_key(
|
||||
sample_bound_key(
|
||||
"key-openai-second",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
@@ -6850,7 +6834,9 @@ async fn gateway_retries_non_kiro_failover_after_success_status_without_body_imp
|
||||
assert_eq!(attempts[0]["status_code"], json!(200));
|
||||
assert_eq!(
|
||||
attempts[0]["error_message"],
|
||||
json!("Provider returned HTTP 200 without a model-test response body")
|
||||
// Attempt diagnostics intentionally expose only the status class;
|
||||
// detailed provider response text is not returned to the admin UI.
|
||||
json!("HTTP 200")
|
||||
);
|
||||
assert_eq!(attempts[1]["status"], json!("success"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user