mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-12 06:00:20 +08:00
fix: resolve workspace lint and regression test failures
This commit is contained in:
@@ -729,117 +729,6 @@ fn update_normalization_codex_capabilities_digest(
|
||||
update_normalization_string_vec_digest(digest, &capabilities.supported_service_tiers);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod continuation_fingerprint_tests {
|
||||
use http::HeaderValue;
|
||||
use serde_json::json;
|
||||
|
||||
use super::ResponsesWebSocketBodyNormalization;
|
||||
use crate::ai_serving::OpenAiResponsesReasoningReplayPolicy;
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_is_stable_for_json_object_key_order() {
|
||||
let first = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_model_directive_patch_for_tests(json!({"reasoning": {"effort": "high"}, "x": 1}));
|
||||
let second = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_model_directive_patch_for_tests(json!({"x": 1, "reasoning": {"effort": "high"}}));
|
||||
assert_eq!(
|
||||
first.continuation_fingerprint(),
|
||||
second.continuation_fingerprint()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_changes_with_effective_contract() {
|
||||
let base = ResponsesWebSocketBodyNormalization::for_tests("provider-model");
|
||||
let changed_policy = base.clone().with_reasoning_replay_policy_for_tests(
|
||||
OpenAiResponsesReasoningReplayPolicy::DeepSeekOpaque,
|
||||
);
|
||||
assert_ne!(
|
||||
base.continuation_fingerprint(),
|
||||
changed_policy.continuation_fingerprint()
|
||||
);
|
||||
|
||||
let changed_patch = base
|
||||
.clone()
|
||||
.with_model_directive_patch_for_tests(json!({"reasoning": {"effort": "low"}}));
|
||||
assert_ne!(
|
||||
base.continuation_fingerprint(),
|
||||
changed_patch.continuation_fingerprint()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_ignores_unrelated_volatile_request_headers() {
|
||||
let body_rules = json!([{
|
||||
"action": "set",
|
||||
"path": "store",
|
||||
"value": false,
|
||||
"condition": {
|
||||
"source": "request_headers",
|
||||
"path": "x-contract",
|
||||
"op": "eq",
|
||||
"value": "enabled"
|
||||
}
|
||||
}]);
|
||||
let mut first = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_body_rules_for_tests(body_rules);
|
||||
first
|
||||
.request_headers
|
||||
.insert("x-contract", HeaderValue::from_static("enabled"));
|
||||
first
|
||||
.request_headers
|
||||
.insert("x-request-id", HeaderValue::from_static("request-1"));
|
||||
first
|
||||
.request_headers
|
||||
.insert("cf-ray", HeaderValue::from_static("edge-1"));
|
||||
let mut second = first.clone();
|
||||
second
|
||||
.request_headers
|
||||
.insert("x-request-id", HeaderValue::from_static("request-2"));
|
||||
second
|
||||
.request_headers
|
||||
.insert("cf-ray", HeaderValue::from_static("edge-2"));
|
||||
|
||||
assert_eq!(
|
||||
first.continuation_fingerprint(),
|
||||
second.continuation_fingerprint(),
|
||||
"headers that no body-rule condition reads must not invalidate a persisted continuation"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_tracks_headers_used_by_body_rule_conditions() {
|
||||
let body_rules = json!([{
|
||||
"action": "set",
|
||||
"path": "store",
|
||||
"value": false,
|
||||
"condition": {
|
||||
"source": "request_headers",
|
||||
"path": "X-Contract",
|
||||
"op": "eq",
|
||||
"value": "enabled"
|
||||
}
|
||||
}]);
|
||||
let mut first = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_body_rules_for_tests(body_rules.clone());
|
||||
first
|
||||
.request_headers
|
||||
.insert("x-contract", HeaderValue::from_static("enabled"));
|
||||
let mut second = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_body_rules_for_tests(body_rules);
|
||||
second
|
||||
.request_headers
|
||||
.insert("x-contract", HeaderValue::from_static("disabled"));
|
||||
|
||||
assert_ne!(
|
||||
first.continuation_fingerprint(),
|
||||
second.continuation_fingerprint(),
|
||||
"a header that controls an effective body-rule condition remains part of the contract"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds one upstream decision for a Responses WebSocket turn. The session
|
||||
/// reuses this decision for same-model turns and invokes the planner again when
|
||||
/// a later `response.create` changes the public model.
|
||||
@@ -1058,3 +947,114 @@ async fn release_responses_websocket_planning_lease(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod continuation_fingerprint_tests {
|
||||
use http::HeaderValue;
|
||||
use serde_json::json;
|
||||
|
||||
use super::ResponsesWebSocketBodyNormalization;
|
||||
use crate::ai_serving::OpenAiResponsesReasoningReplayPolicy;
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_is_stable_for_json_object_key_order() {
|
||||
let first = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_model_directive_patch_for_tests(json!({"reasoning": {"effort": "high"}, "x": 1}));
|
||||
let second = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_model_directive_patch_for_tests(json!({"x": 1, "reasoning": {"effort": "high"}}));
|
||||
assert_eq!(
|
||||
first.continuation_fingerprint(),
|
||||
second.continuation_fingerprint()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_changes_with_effective_contract() {
|
||||
let base = ResponsesWebSocketBodyNormalization::for_tests("provider-model");
|
||||
let changed_policy = base.clone().with_reasoning_replay_policy_for_tests(
|
||||
OpenAiResponsesReasoningReplayPolicy::DeepSeekOpaque,
|
||||
);
|
||||
assert_ne!(
|
||||
base.continuation_fingerprint(),
|
||||
changed_policy.continuation_fingerprint()
|
||||
);
|
||||
|
||||
let changed_patch = base
|
||||
.clone()
|
||||
.with_model_directive_patch_for_tests(json!({"reasoning": {"effort": "low"}}));
|
||||
assert_ne!(
|
||||
base.continuation_fingerprint(),
|
||||
changed_patch.continuation_fingerprint()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_ignores_unrelated_volatile_request_headers() {
|
||||
let body_rules = json!([{
|
||||
"action": "set",
|
||||
"path": "store",
|
||||
"value": false,
|
||||
"condition": {
|
||||
"source": "request_headers",
|
||||
"path": "x-contract",
|
||||
"op": "eq",
|
||||
"value": "enabled"
|
||||
}
|
||||
}]);
|
||||
let mut first = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_body_rules_for_tests(body_rules);
|
||||
first
|
||||
.request_headers
|
||||
.insert("x-contract", HeaderValue::from_static("enabled"));
|
||||
first
|
||||
.request_headers
|
||||
.insert("x-request-id", HeaderValue::from_static("request-1"));
|
||||
first
|
||||
.request_headers
|
||||
.insert("cf-ray", HeaderValue::from_static("edge-1"));
|
||||
let mut second = first.clone();
|
||||
second
|
||||
.request_headers
|
||||
.insert("x-request-id", HeaderValue::from_static("request-2"));
|
||||
second
|
||||
.request_headers
|
||||
.insert("cf-ray", HeaderValue::from_static("edge-2"));
|
||||
|
||||
assert_eq!(
|
||||
first.continuation_fingerprint(),
|
||||
second.continuation_fingerprint(),
|
||||
"headers that no body-rule condition reads must not invalidate a persisted continuation"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalization_fingerprint_tracks_headers_used_by_body_rule_conditions() {
|
||||
let body_rules = json!([{
|
||||
"action": "set",
|
||||
"path": "store",
|
||||
"value": false,
|
||||
"condition": {
|
||||
"source": "request_headers",
|
||||
"path": "X-Contract",
|
||||
"op": "eq",
|
||||
"value": "enabled"
|
||||
}
|
||||
}]);
|
||||
let mut first = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_body_rules_for_tests(body_rules.clone());
|
||||
first
|
||||
.request_headers
|
||||
.insert("x-contract", HeaderValue::from_static("enabled"));
|
||||
let mut second = ResponsesWebSocketBodyNormalization::for_tests("provider-model")
|
||||
.with_body_rules_for_tests(body_rules);
|
||||
second
|
||||
.request_headers
|
||||
.insert("x-contract", HeaderValue::from_static("disabled"));
|
||||
|
||||
assert_ne!(
|
||||
first.continuation_fingerprint(),
|
||||
second.continuation_fingerprint(),
|
||||
"a header that controls an effective body-rule condition remains part of the contract"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4639,8 +4639,8 @@ async fn execute_execution_runtime_stream_inner(
|
||||
frame_stream,
|
||||
false,
|
||||
provider_pool_in_flight_guard.take(),
|
||||
retry_scope_out.as_deref_mut(),
|
||||
retry_fallback_out.as_deref_mut(),
|
||||
retry_scope_out,
|
||||
retry_fallback_out,
|
||||
Some(remote_fallback_observation),
|
||||
)
|
||||
.await;
|
||||
@@ -9942,14 +9942,15 @@ mod tests {
|
||||
assert!(execution.prefetched_body.is_empty());
|
||||
assert_eq!(upstream_hits.load(Ordering::SeqCst), 2);
|
||||
assert_eq!(task_registration_hits.load(Ordering::SeqCst), 1);
|
||||
let authorizations = observed_authorization
|
||||
.lock()
|
||||
.expect("authorization mutex should lock");
|
||||
assert_eq!(authorizations.len(), 2);
|
||||
assert_eq!(authorizations[0], initial_authorization);
|
||||
assert!(authorizations[1].starts_with("AgentAssertion "));
|
||||
assert_ne!(authorizations[1], authorizations[0]);
|
||||
drop(authorizations);
|
||||
{
|
||||
let authorizations = observed_authorization
|
||||
.lock()
|
||||
.expect("authorization mutex should lock");
|
||||
assert_eq!(authorizations.len(), 2);
|
||||
assert_eq!(authorizations[0], initial_authorization);
|
||||
assert!(authorizations[1].starts_with("AgentAssertion "));
|
||||
assert_ne!(authorizations[1], authorizations[0]);
|
||||
}
|
||||
let replayed = collect_direct_execution_body(execution)
|
||||
.await
|
||||
.expect("retried response body should read");
|
||||
|
||||
@@ -5310,7 +5310,7 @@ pub(crate) fn build_execution_response_body(
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use aether_contracts::tunnel::{
|
||||
TUNNEL_RELAY_AUTH_NONCE_HEADER, TUNNEL_RELAY_AUTH_PAYLOAD_HEADER,
|
||||
@@ -6264,16 +6264,14 @@ mod tests {
|
||||
TestEnvVarGuard { key, previous }
|
||||
}
|
||||
|
||||
fn direct_reqwest_env_lock() -> MutexGuard<'static, ()> {
|
||||
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
LOCK.get_or_init(|| Mutex::new(()))
|
||||
.lock()
|
||||
.expect("direct reqwest env lock")
|
||||
fn direct_reqwest_env_lock() -> &'static tokio::sync::Mutex<()> {
|
||||
static LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());
|
||||
&LOCK
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_client_cache_key_includes_transport_profile() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let timeouts = ExecutionTimeouts {
|
||||
connect_ms: Some(5_000),
|
||||
..ExecutionTimeouts::default()
|
||||
@@ -6377,7 +6375,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_client_cache_evicts_least_recently_used_entry_at_capacity() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _capacity = set_test_env_var(super::DIRECT_REQWEST_CACHE_MAX_ENTRIES_ENV, "2");
|
||||
let cache_key = |suffix| {
|
||||
super::direct_reqwest_client_cache_key(
|
||||
@@ -6475,7 +6473,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_client_cache_key_splits_origin_only_when_enabled() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let profile = ResolvedTransportProfile {
|
||||
profile_id: "mock-h2c-origin".into(),
|
||||
backend: TRANSPORT_BACKEND_REQWEST_RUSTLS.into(),
|
||||
@@ -6662,14 +6660,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_h2c_client_shards_respect_explicit_env() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _shards = set_test_env_var(super::DIRECT_H2C_CLIENT_SHARDS_ENV, "7");
|
||||
assert_eq!(super::direct_h2c_client_shard_count(), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_h2c_adaptive_window_respects_explicit_env() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
{
|
||||
let _adaptive = set_test_env_var(super::DIRECT_H2C_ADAPTIVE_WINDOW_ENV, "0");
|
||||
assert!(!super::direct_h2c_adaptive_window_enabled());
|
||||
@@ -6737,7 +6735,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_h2c_prewarm_urls_parse_env_list() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _urls = set_test_env_var(
|
||||
super::DIRECT_H2C_PREWARM_URLS_ENV,
|
||||
" http://127.0.0.1:18184/v1/chat/completions,;http://127.0.0.1:18185/v1/chat/completions\nhttp://127.0.0.1:18186/v1/chat/completions ",
|
||||
@@ -6755,7 +6753,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_h2c_prewarm_cache_keys_dedup_by_origin() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let urls = vec![
|
||||
"http://127.0.0.1:18184/v1/chat/completions".to_string(),
|
||||
"http://127.0.0.1:18184/v1/responses".to_string(),
|
||||
@@ -6781,7 +6779,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_h2c_client_cache_splits_by_origin_and_shards() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _shards = set_test_env_var(super::DIRECT_H2C_CLIENT_SHARDS_ENV, "3");
|
||||
super::DIRECT_H2C_CLIENT_CACHE
|
||||
.lock()
|
||||
@@ -6806,7 +6804,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_initial_client_shards_are_bounded_by_target() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
assert_eq!(super::direct_reqwest_initial_client_shard_count(1), 1);
|
||||
assert_eq!(super::direct_reqwest_initial_client_shard_count(2), 2);
|
||||
assert_eq!(
|
||||
@@ -6817,7 +6815,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_initial_client_shards_cap_large_sync_env() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _sync = set_test_env_var(super::DIRECT_REQWEST_SYNC_WARM_CLIENTS_ENV, "128");
|
||||
assert_eq!(
|
||||
super::direct_reqwest_initial_client_shard_count(128),
|
||||
@@ -6827,7 +6825,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_prewarm_client_shards_default_to_initial() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
assert_eq!(super::direct_reqwest_prewarm_client_shard_count(1), 1);
|
||||
assert_eq!(
|
||||
super::direct_reqwest_prewarm_client_shard_count(96),
|
||||
@@ -6837,7 +6835,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_prewarm_client_shards_do_not_exceed_request_path_cap() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _sync = set_test_env_var(super::DIRECT_REQWEST_SYNC_WARM_CLIENTS_ENV, "4");
|
||||
let _prewarm = set_test_env_var(super::DIRECT_REQWEST_PREWARM_SYNC_CLIENTS_ENV, "128");
|
||||
|
||||
@@ -6846,7 +6844,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_prewarm_populates_cache_for_plan() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _shards = set_test_env_var(super::DIRECT_REQWEST_H2_CLIENT_SHARDS_ENV, "4");
|
||||
let profile = ResolvedTransportProfile {
|
||||
profile_id: "mock-h2c-prewarm".into(),
|
||||
@@ -6908,7 +6906,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_prewarm_plan_keeps_large_sync_env_off_request_path() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _shards = set_test_env_var(super::DIRECT_REQWEST_H2_CLIENT_SHARDS_ENV, "128");
|
||||
let _sync = set_test_env_var(super::DIRECT_REQWEST_SYNC_WARM_CLIENTS_ENV, "4");
|
||||
let _prewarm = set_test_env_var(super::DIRECT_REQWEST_PREWARM_SYNC_CLIENTS_ENV, "128");
|
||||
@@ -6968,7 +6966,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_prewarm_skips_h2c_fast_path() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _fast_path = set_test_env_var(super::DIRECT_H2C_FAST_PATH_ENV, "1");
|
||||
let profile = ResolvedTransportProfile {
|
||||
profile_id: "mock-h2c-fast-path-prewarm-skip".into(),
|
||||
@@ -7021,7 +7019,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn direct_reqwest_cache_metrics_expose_ready_state() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().blocking_lock();
|
||||
let _shards = set_test_env_var(super::DIRECT_REQWEST_H2_CLIENT_SHARDS_ENV, "1");
|
||||
let profile = ResolvedTransportProfile {
|
||||
profile_id: "mock-h2c-ready-metrics".into(),
|
||||
@@ -8605,7 +8603,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn direct_sync_execution_runtime_supports_tunnel_relay() {
|
||||
let _env_lock = direct_reqwest_env_lock();
|
||||
let _env_lock = direct_reqwest_env_lock().lock().await;
|
||||
let _relay_secret = set_test_env_var("AETHER_TUNNEL_RELAY_AUTH_SECRET", RELAY_TEST_SECRET);
|
||||
let listener = crate::test_support::bind_loopback_listener()
|
||||
.await
|
||||
@@ -8772,7 +8770,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn direct_sync_execution_runtime_rejects_short_tunnel_relay_secret_before_send() {
|
||||
let _env_lock = direct_reqwest_env_lock();
|
||||
let _env_lock = direct_reqwest_env_lock().lock().await;
|
||||
let _relay_secret = set_test_env_var("AETHER_TUNNEL_RELAY_AUTH_SECRET", &"x".repeat(31));
|
||||
let execution_runtime = DirectSyncExecutionRuntime::new();
|
||||
let error = execution_runtime
|
||||
@@ -8813,7 +8811,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn direct_sync_execution_runtime_requires_tunnel_relay_secret_before_send() {
|
||||
let _env_lock = direct_reqwest_env_lock();
|
||||
let _env_lock = direct_reqwest_env_lock().lock().await;
|
||||
let _relay_secret = unset_test_env_var("AETHER_TUNNEL_RELAY_AUTH_SECRET");
|
||||
let execution_runtime = DirectSyncExecutionRuntime::new();
|
||||
let error = execution_runtime
|
||||
@@ -9140,7 +9138,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn direct_sync_execution_runtime_forwards_http1_only_control_to_tunnel_relay() {
|
||||
let _env_lock = direct_reqwest_env_lock();
|
||||
let _env_lock = direct_reqwest_env_lock().lock().await;
|
||||
let _relay_secret = set_test_env_var("AETHER_TUNNEL_RELAY_AUTH_SECRET", RELAY_TEST_SECRET);
|
||||
let listener = crate::test_support::bind_loopback_listener()
|
||||
.await
|
||||
@@ -9356,7 +9354,7 @@ mod tests {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn direct_sync_execution_runtime_uses_h2c_prior_knowledge_on_wire() {
|
||||
let _guard = direct_reqwest_env_lock();
|
||||
let _guard = direct_reqwest_env_lock().lock().await;
|
||||
let _shards = set_test_env_var(super::DIRECT_REQWEST_H2_CLIENT_SHARDS_ENV, "1");
|
||||
let listener = crate::test_support::bind_loopback_listener()
|
||||
.await
|
||||
|
||||
@@ -339,57 +339,6 @@ async fn build_admin_oauth_test_payload(
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
is_fixed_linuxdo_oauth_origin, resolve_public_admin_oauth_endpoint,
|
||||
validate_public_admin_oauth_resolved_addrs,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::test]
|
||||
async fn oauth_test_endpoint_rejects_loopback_https_targets_before_connecting() {
|
||||
let url = reqwest::Url::parse("https://127.0.0.1/oauth/token").expect("URL");
|
||||
|
||||
assert!(resolve_public_admin_oauth_endpoint(&url).await.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linuxdo_builtin_origin_allows_only_benchmarking_addresses() {
|
||||
let fixed = reqwest::Url::parse("https://connect.linux.do/oauth2/token")
|
||||
.expect("LinuxDo URL should parse");
|
||||
let fake = SocketAddr::from(([198, 18, 75, 234], 443));
|
||||
assert!(is_fixed_linuxdo_oauth_origin(&fixed));
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(&fixed, &[fake], true).is_ok());
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(&fixed, &[fake], false).is_err());
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(
|
||||
&fixed,
|
||||
&[fake, SocketAddr::from(([127, 0, 0, 1], 443))],
|
||||
true,
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_or_non_default_oauth_origins_reject_benchmarking_addresses() {
|
||||
let fake = SocketAddr::from(([198, 18, 75, 234], 443));
|
||||
for raw_url in [
|
||||
"https://oauth.example.test/token",
|
||||
"https://connect.linux.do:8443/oauth2/token",
|
||||
"https://connect.linuxdo.org/oauth2/token",
|
||||
"https://connect.linux.do.evil.test/oauth2/token",
|
||||
"https://connect.linux.do/oauth2/token?tenant=unexpected",
|
||||
] {
|
||||
let url = reqwest::Url::parse(raw_url).expect("test URL should parse");
|
||||
assert!(
|
||||
!is_fixed_linuxdo_oauth_origin(&url),
|
||||
"must not trust {raw_url}"
|
||||
);
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(&url, &[fake], true).is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn maybe_build_local_admin_oauth_response(
|
||||
state: &AdminAppState<'_>,
|
||||
request_context: &AdminRequestContext<'_>,
|
||||
@@ -689,3 +638,54 @@ pub(crate) async fn maybe_build_local_admin_oauth_response(
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
is_fixed_linuxdo_oauth_origin, resolve_public_admin_oauth_endpoint,
|
||||
validate_public_admin_oauth_resolved_addrs,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::test]
|
||||
async fn oauth_test_endpoint_rejects_loopback_https_targets_before_connecting() {
|
||||
let url = reqwest::Url::parse("https://127.0.0.1/oauth/token").expect("URL");
|
||||
|
||||
assert!(resolve_public_admin_oauth_endpoint(&url).await.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linuxdo_builtin_origin_allows_only_benchmarking_addresses() {
|
||||
let fixed = reqwest::Url::parse("https://connect.linux.do/oauth2/token")
|
||||
.expect("LinuxDo URL should parse");
|
||||
let fake = SocketAddr::from(([198, 18, 75, 234], 443));
|
||||
assert!(is_fixed_linuxdo_oauth_origin(&fixed));
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(&fixed, &[fake], true).is_ok());
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(&fixed, &[fake], false).is_err());
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(
|
||||
&fixed,
|
||||
&[fake, SocketAddr::from(([127, 0, 0, 1], 443))],
|
||||
true,
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_or_non_default_oauth_origins_reject_benchmarking_addresses() {
|
||||
let fake = SocketAddr::from(([198, 18, 75, 234], 443));
|
||||
for raw_url in [
|
||||
"https://oauth.example.test/token",
|
||||
"https://connect.linux.do:8443/oauth2/token",
|
||||
"https://connect.linuxdo.org/oauth2/token",
|
||||
"https://connect.linux.do.evil.test/oauth2/token",
|
||||
"https://connect.linux.do/oauth2/token?tenant=unexpected",
|
||||
] {
|
||||
let url = reqwest::Url::parse(raw_url).expect("test URL should parse");
|
||||
assert!(
|
||||
!is_fixed_linuxdo_oauth_origin(&url),
|
||||
"must not trust {raw_url}"
|
||||
);
|
||||
assert!(validate_public_admin_oauth_resolved_addrs(&url, &[fake], true).is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,46 +278,6 @@ async fn build_batch_delete_global_models_response(
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod batch_boundary_tests {
|
||||
use super::{normalize_admin_global_model_batch_ids, MAX_ADMIN_GLOBAL_MODEL_BATCH_ITEMS};
|
||||
|
||||
#[test]
|
||||
fn global_model_batch_ids_are_bounded_and_deduplicated() {
|
||||
assert_eq!(
|
||||
normalize_admin_global_model_batch_ids(
|
||||
vec![
|
||||
"model-2".to_string(),
|
||||
"model-1".to_string(),
|
||||
" model-2 ".to_string(),
|
||||
" ".to_string(),
|
||||
],
|
||||
"ids",
|
||||
)
|
||||
.expect("valid ids"),
|
||||
vec![
|
||||
"model-2".to_string(),
|
||||
"model-1".to_string(),
|
||||
" ".to_string(),
|
||||
]
|
||||
);
|
||||
assert!(normalize_admin_global_model_batch_ids(
|
||||
(0..=MAX_ADMIN_GLOBAL_MODEL_BATCH_ITEMS)
|
||||
.map(|index| format!("model-{index}"))
|
||||
.collect(),
|
||||
"ids",
|
||||
)
|
||||
.is_err());
|
||||
assert!(normalize_admin_global_model_batch_ids(
|
||||
(0..MAX_ADMIN_GLOBAL_MODEL_BATCH_ITEMS)
|
||||
.map(|index| format!("provider-{index}"))
|
||||
.collect(),
|
||||
"provider_ids",
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
async fn build_assign_to_providers_response(
|
||||
state: &AdminAppState<'_>,
|
||||
request_context: &AdminRequestContext<'_>,
|
||||
@@ -357,3 +317,43 @@ async fn build_assign_to_providers_response(
|
||||
&global_model_id,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod batch_boundary_tests {
|
||||
use super::{normalize_admin_global_model_batch_ids, MAX_ADMIN_GLOBAL_MODEL_BATCH_ITEMS};
|
||||
|
||||
#[test]
|
||||
fn global_model_batch_ids_are_bounded_and_deduplicated() {
|
||||
assert_eq!(
|
||||
normalize_admin_global_model_batch_ids(
|
||||
vec![
|
||||
"model-2".to_string(),
|
||||
"model-1".to_string(),
|
||||
" model-2 ".to_string(),
|
||||
" ".to_string(),
|
||||
],
|
||||
"ids",
|
||||
)
|
||||
.expect("valid ids"),
|
||||
vec![
|
||||
"model-2".to_string(),
|
||||
"model-1".to_string(),
|
||||
" ".to_string(),
|
||||
]
|
||||
);
|
||||
assert!(normalize_admin_global_model_batch_ids(
|
||||
(0..=MAX_ADMIN_GLOBAL_MODEL_BATCH_ITEMS)
|
||||
.map(|index| format!("model-{index}"))
|
||||
.collect(),
|
||||
"ids",
|
||||
)
|
||||
.is_err());
|
||||
assert!(normalize_admin_global_model_batch_ids(
|
||||
(0..MAX_ADMIN_GLOBAL_MODEL_BATCH_ITEMS)
|
||||
.map(|index| format!("provider-{index}"))
|
||||
.collect(),
|
||||
"provider_ids",
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,43 +66,6 @@ fn admin_provider_oauth_kiro_refresh_error(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod refresh_error_tests {
|
||||
use super::admin_provider_oauth_kiro_refresh_error;
|
||||
use crate::handlers::admin::request::AdminKiroAuthConfig;
|
||||
use aether_oauth::core::OAuthError;
|
||||
|
||||
#[test]
|
||||
fn kiro_refresh_error_does_not_reflect_upstream_body() {
|
||||
let auth_config = AdminKiroAuthConfig {
|
||||
auth_method: None,
|
||||
refresh_token: None,
|
||||
expires_at: None,
|
||||
profile_arn: None,
|
||||
region: None,
|
||||
auth_region: None,
|
||||
api_region: None,
|
||||
client_id: None,
|
||||
client_secret: None,
|
||||
machine_id: None,
|
||||
kiro_version: None,
|
||||
system_version: None,
|
||||
node_version: None,
|
||||
access_token: None,
|
||||
};
|
||||
let detail = admin_provider_oauth_kiro_refresh_error(
|
||||
&auth_config,
|
||||
OAuthError::HttpStatus {
|
||||
status_code: 502,
|
||||
body_excerpt: "authorization=Bearer upstream-secret".to_string(),
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(detail, "social refresh 失败: HTTP 502");
|
||||
assert!(!detail.contains("upstream-secret"));
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn refresh_admin_provider_oauth_kiro_auth_config(
|
||||
state: &AdminAppState<'_>,
|
||||
auth_config: &AdminKiroAuthConfig,
|
||||
@@ -240,3 +203,40 @@ pub(super) async fn fetch_admin_provider_oauth_kiro_email(
|
||||
aether_admin::provider::quota::parse_kiro_usage_response(&payload, current_unix_secs())?;
|
||||
json_non_empty_string(metadata.get("email"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod refresh_error_tests {
|
||||
use super::admin_provider_oauth_kiro_refresh_error;
|
||||
use crate::handlers::admin::request::AdminKiroAuthConfig;
|
||||
use aether_oauth::core::OAuthError;
|
||||
|
||||
#[test]
|
||||
fn kiro_refresh_error_does_not_reflect_upstream_body() {
|
||||
let auth_config = AdminKiroAuthConfig {
|
||||
auth_method: None,
|
||||
refresh_token: None,
|
||||
expires_at: None,
|
||||
profile_arn: None,
|
||||
region: None,
|
||||
auth_region: None,
|
||||
api_region: None,
|
||||
client_id: None,
|
||||
client_secret: None,
|
||||
machine_id: None,
|
||||
kiro_version: None,
|
||||
system_version: None,
|
||||
node_version: None,
|
||||
access_token: None,
|
||||
};
|
||||
let detail = admin_provider_oauth_kiro_refresh_error(
|
||||
&auth_config,
|
||||
OAuthError::HttpStatus {
|
||||
status_code: 502,
|
||||
body_excerpt: "authorization=Bearer upstream-secret".to_string(),
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(detail, "social refresh 失败: HTTP 502");
|
||||
assert!(!detail.contains("upstream-secret"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3228,7 +3228,7 @@ mod tests {
|
||||
async fn request_body_buffer_caps_decompressed_body_at_shared_budget() {
|
||||
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
|
||||
encoder
|
||||
.write_all(&vec![b'a'; 128])
|
||||
.write_all(&[b'a'; 128])
|
||||
.expect("test gzip body should encode");
|
||||
let encoded = encoder.finish().expect("test gzip body should finish");
|
||||
assert!(
|
||||
|
||||
@@ -168,52 +168,6 @@ fn wallet_public_refund_payload(mut payload: serde_json::Value) -> serde_json::V
|
||||
payload
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::wallet_refund_payload_from_record;
|
||||
use aether_data::repository::wallet::StoredAdminWalletRefund;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn public_refund_projection_excludes_payout_proof_and_upstream_payload() {
|
||||
let record = StoredAdminWalletRefund {
|
||||
id: "refund-1".to_string(),
|
||||
refund_no: "rf_1".to_string(),
|
||||
wallet_id: "wallet-1".to_string(),
|
||||
user_id: Some("user-1".to_string()),
|
||||
payment_order_id: Some("order-1".to_string()),
|
||||
source_type: "payment_order".to_string(),
|
||||
source_id: Some("order-1".to_string()),
|
||||
refund_mode: "original_channel".to_string(),
|
||||
amount_usd: 10.0,
|
||||
status: "processing".to_string(),
|
||||
reason: Some("requested".to_string()),
|
||||
failure_reason: None,
|
||||
gateway_refund_id: Some("gateway-refund-1".to_string()),
|
||||
payout_method: None,
|
||||
payout_reference: None,
|
||||
payout_proof: Some(json!({
|
||||
"gateway_refund": {
|
||||
"id": "gateway-refund-1",
|
||||
"payload": {"payer": "sensitive", "credential": "secret"}
|
||||
}
|
||||
})),
|
||||
requested_by: Some("user-1".to_string()),
|
||||
approved_by: Some("admin-1".to_string()),
|
||||
processed_by: Some("admin-1".to_string()),
|
||||
created_at_unix_ms: 1,
|
||||
updated_at_unix_secs: 1,
|
||||
processed_at_unix_secs: Some(1),
|
||||
completed_at_unix_secs: None,
|
||||
};
|
||||
|
||||
let payload = wallet_refund_payload_from_record(&record);
|
||||
assert!(payload.get("payout_proof").is_none());
|
||||
assert_eq!(payload["status"], "processing");
|
||||
assert_eq!(payload["gateway_refund_id"], "gateway-refund-1");
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn handle_wallet_refunds_list(
|
||||
state: &AppState,
|
||||
request_context: &GatewayPublicRequestContext,
|
||||
@@ -659,3 +613,49 @@ pub(super) async fn handle_wallet_create_refund(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::wallet_refund_payload_from_record;
|
||||
use aether_data::repository::wallet::StoredAdminWalletRefund;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn public_refund_projection_excludes_payout_proof_and_upstream_payload() {
|
||||
let record = StoredAdminWalletRefund {
|
||||
id: "refund-1".to_string(),
|
||||
refund_no: "rf_1".to_string(),
|
||||
wallet_id: "wallet-1".to_string(),
|
||||
user_id: Some("user-1".to_string()),
|
||||
payment_order_id: Some("order-1".to_string()),
|
||||
source_type: "payment_order".to_string(),
|
||||
source_id: Some("order-1".to_string()),
|
||||
refund_mode: "original_channel".to_string(),
|
||||
amount_usd: 10.0,
|
||||
status: "processing".to_string(),
|
||||
reason: Some("requested".to_string()),
|
||||
failure_reason: None,
|
||||
gateway_refund_id: Some("gateway-refund-1".to_string()),
|
||||
payout_method: None,
|
||||
payout_reference: None,
|
||||
payout_proof: Some(json!({
|
||||
"gateway_refund": {
|
||||
"id": "gateway-refund-1",
|
||||
"payload": {"payer": "sensitive", "credential": "secret"}
|
||||
}
|
||||
})),
|
||||
requested_by: Some("user-1".to_string()),
|
||||
approved_by: Some("admin-1".to_string()),
|
||||
processed_by: Some("admin-1".to_string()),
|
||||
created_at_unix_ms: 1,
|
||||
updated_at_unix_secs: 1,
|
||||
processed_at_unix_secs: Some(1),
|
||||
completed_at_unix_secs: None,
|
||||
};
|
||||
|
||||
let payload = wallet_refund_payload_from_record(&record);
|
||||
assert!(payload.get("payout_proof").is_none());
|
||||
assert_eq!(payload["status"], "processing");
|
||||
assert_eq!(payload["gateway_refund_id"], "gateway-refund-1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ mod tests {
|
||||
assert!(controls.is_err());
|
||||
|
||||
let template = "{{value}}".repeat(100_000);
|
||||
let variables = BTreeMap::from([(String::from("value"), String::from("x".repeat(64)))]);
|
||||
let variables = BTreeMap::from([(String::from("value"), "x".repeat(64))]);
|
||||
let error = render_admin_email_template_html(&template, &variables)
|
||||
.expect_err("rendered output must remain bounded");
|
||||
assert!(format!("{error:?}").contains("exceeds"));
|
||||
|
||||
@@ -199,10 +199,7 @@ pub(crate) fn normalize_ldap_transport_server_url(raw: &str, use_starttls: bool)
|
||||
// Gateway unit/integration fixtures use an in-process mock endpoint. Keep
|
||||
// this exception behind the gateway test configuration; production code
|
||||
// always uses the strict parser without custom schemes.
|
||||
return aether_admin::system::normalize_ldap_transport_server_url_for_tests(
|
||||
raw,
|
||||
use_starttls,
|
||||
);
|
||||
aether_admin::system::normalize_ldap_transport_server_url_for_tests(raw, use_starttls)
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ pub(crate) fn local_auth_jwt_secret() -> Result<String, String> {
|
||||
Err(std::env::VarError::NotPresent) => {
|
||||
#[cfg(test)]
|
||||
{
|
||||
return Ok(TEST_JWT_SECRET.to_string());
|
||||
Ok(TEST_JWT_SECRET.to_string())
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
||||
@@ -3083,7 +3083,7 @@ mod tests {
|
||||
.await
|
||||
.expect("stale LKG read must not wait for retention lock");
|
||||
assert_eq!(stale.snapshot(TEST_PROVIDER_ID, TEST_KEY_ID), Some(&seeded));
|
||||
assert_eq!(stale.stale_targets(), &[target.clone()]);
|
||||
assert_eq!(stale.stale_targets(), std::slice::from_ref(&target));
|
||||
assert_eq!(runtime.execution_count(), 1);
|
||||
|
||||
assert!(runtime
|
||||
@@ -3111,7 +3111,7 @@ mod tests {
|
||||
let load = load_one(&runtime, &client_version).await;
|
||||
|
||||
assert_eq!(load.snapshot(TEST_PROVIDER_ID, TEST_KEY_ID), Some(&seeded));
|
||||
assert_eq!(load.stale_targets(), &[target.clone()]);
|
||||
assert_eq!(load.stale_targets(), std::slice::from_ref(&target));
|
||||
assert!(runtime
|
||||
.state
|
||||
.kv_get(&catalog_lkg_key(&target, client_version.as_str()))
|
||||
@@ -3142,7 +3142,7 @@ mod tests {
|
||||
|
||||
let load = load_one(&runtime, &client_version).await;
|
||||
assert_eq!(load.snapshot(TEST_PROVIDER_ID, TEST_KEY_ID), Some(&seeded));
|
||||
assert_eq!(load.stale_targets(), &[target.clone()]);
|
||||
assert_eq!(load.stale_targets(), std::slice::from_ref(&target));
|
||||
assert_eq!(runtime.execution_count(), 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -561,7 +561,6 @@ mod tests {
|
||||
pattern: "invalid.*parameter".to_string(),
|
||||
status_codes: [400].into_iter().collect(),
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
stop_status_codes: [200, 403].into_iter().collect(),
|
||||
continue_status_codes: [400].into_iter().collect(),
|
||||
|
||||
@@ -3764,18 +3764,19 @@ mod tests {
|
||||
.await;
|
||||
|
||||
assert_eq!(normal_batch.len(), 1);
|
||||
let retry_states = metrics
|
||||
.retry_states
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||
assert_eq!(
|
||||
retry_states
|
||||
.get(&(0, RequestCandidateQueueLane::Normal))
|
||||
.map(|state| state.attempt),
|
||||
Some(1)
|
||||
);
|
||||
assert!(!retry_states.contains_key(&(0, RequestCandidateQueueLane::Active)));
|
||||
drop(retry_states);
|
||||
{
|
||||
let retry_states = metrics
|
||||
.retry_states
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||
assert_eq!(
|
||||
retry_states
|
||||
.get(&(0, RequestCandidateQueueLane::Normal))
|
||||
.map(|state| state.attempt),
|
||||
Some(1)
|
||||
);
|
||||
assert!(!retry_states.contains_key(&(0, RequestCandidateQueueLane::Active)));
|
||||
}
|
||||
assert!(request_candidate_retry_is_ready(
|
||||
&metrics,
|
||||
0,
|
||||
|
||||
@@ -274,7 +274,13 @@ mod tests {
|
||||
"priority_mode": "provider",
|
||||
"scheduling_mode": "cache_affinity",
|
||||
"keep_priority_on_conversion": false,
|
||||
"sticky_key_attempts": DEFAULT_STICKY_KEY_ATTEMPTS
|
||||
"sticky_key_attempts": DEFAULT_STICKY_KEY_ATTEMPTS,
|
||||
"max_transfer_count": 0,
|
||||
"max_transfer_timeout_seconds": 0,
|
||||
"failover_rules": {
|
||||
"success_failover_patterns": [],
|
||||
"error_stop_patterns": []
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -323,7 +329,13 @@ mod tests {
|
||||
"priority_mode": "provider",
|
||||
"scheduling_mode": "cache_affinity",
|
||||
"keep_priority_on_conversion": false,
|
||||
"sticky_key_attempts": DEFAULT_STICKY_KEY_ATTEMPTS
|
||||
"sticky_key_attempts": DEFAULT_STICKY_KEY_ATTEMPTS,
|
||||
"max_transfer_count": 0,
|
||||
"max_transfer_timeout_seconds": 0,
|
||||
"failover_rules": {
|
||||
"success_failover_patterns": [],
|
||||
"error_stop_patterns": []
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -196,11 +196,12 @@ impl AppState {
|
||||
{
|
||||
let session = session.into();
|
||||
#[cfg(test)]
|
||||
if self.auth_session_store.is_some() && self.auth_user_store.is_some() {
|
||||
if let (Some(session_store), Some(user_store)) = (
|
||||
self.auth_session_store.as_ref(),
|
||||
self.auth_user_store.as_ref(),
|
||||
) {
|
||||
let existing = {
|
||||
self.auth_user_store
|
||||
.as_ref()
|
||||
.expect("checked auth user store")
|
||||
user_store
|
||||
.lock()
|
||||
.expect("auth user store should lock")
|
||||
.get(&session.user_id)
|
||||
@@ -217,12 +218,7 @@ impl AppState {
|
||||
let Some(existing) = existing else {
|
||||
return Ok(None);
|
||||
};
|
||||
let mut users = self
|
||||
.auth_user_store
|
||||
.as_ref()
|
||||
.expect("checked auth user store")
|
||||
.lock()
|
||||
.expect("auth user store should lock");
|
||||
let mut users = user_store.lock().expect("auth user store should lock");
|
||||
let user = users.entry(session.user_id.clone()).or_insert(existing);
|
||||
if user.password_hash.as_deref() != Some(expected_password_hash)
|
||||
|| !user.auth_source.eq_ignore_ascii_case("local")
|
||||
@@ -238,10 +234,7 @@ impl AppState {
|
||||
.or(session.last_seen_at)
|
||||
.unwrap_or_else(chrono::Utc::now);
|
||||
user.last_login_at = Some(now);
|
||||
let mut sessions = self
|
||||
.auth_session_store
|
||||
.as_ref()
|
||||
.expect("checked auth session store")
|
||||
let mut sessions = session_store
|
||||
.lock()
|
||||
.expect("auth session store should lock");
|
||||
for existing in sessions.values_mut() {
|
||||
|
||||
@@ -802,7 +802,7 @@ impl AppState {
|
||||
}
|
||||
return Ok(Some(LdapAuthProvisioningResult {
|
||||
user,
|
||||
owned_wallet_id: initialized.created.then(|| initialized.wallet.id),
|
||||
owned_wallet_id: initialized.created.then_some(initialized.wallet.id),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{
|
||||
any, build_router_with_state, build_state_with_execution_runtime_override, json, start_server,
|
||||
Arc, Body, Bytes, HeaderValue, Infallible, Json, Mutex, Request, Response, Router, StatusCode,
|
||||
TRACE_ID_HEADER,
|
||||
LOCAL_EXECUTION_RUNTIME_MISS_REASON_HEADER, TRACE_ID_HEADER,
|
||||
};
|
||||
|
||||
use aether_crypto::{encrypt_python_fernet_plaintext, DEVELOPMENT_ENCRYPTION_KEY};
|
||||
@@ -626,7 +626,7 @@ async fn gateway_returns_error_body_when_prefetch_detects_embedded_stream_error_
|
||||
auth_repository,
|
||||
candidate_selection_repository,
|
||||
provider_catalog_repository,
|
||||
request_candidate_repository,
|
||||
Arc::clone(&request_candidate_repository),
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
),
|
||||
),
|
||||
@@ -649,17 +649,41 @@ async fn gateway_returns_error_body_when_prefetch_detects_embedded_stream_error_
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||
assert_eq!(
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_TYPE)
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
Some("text/event-stream")
|
||||
Some("application/json")
|
||||
);
|
||||
let body_text = response.text().await.expect("response body should read");
|
||||
assert!(body_text.contains("\"rate_limit_error\""));
|
||||
assert!(body_text.contains("\"slow down\""));
|
||||
assert_eq!(
|
||||
response
|
||||
.headers()
|
||||
.get(LOCAL_EXECUTION_RUNTIME_MISS_REASON_HEADER)
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
Some("execution_runtime_candidates_exhausted")
|
||||
);
|
||||
let body_json: serde_json::Value = response.json().await.expect("response body should parse");
|
||||
assert_eq!(body_json["error"]["type"], "http_error");
|
||||
let stored_candidates = request_candidate_repository
|
||||
.list_by_request_id("trace-openai-chat-stream-prefetch-error-123")
|
||||
.await
|
||||
.expect("request candidate trace should read");
|
||||
let failed_candidate = stored_candidates
|
||||
.iter()
|
||||
.find(|candidate| candidate.status == RequestCandidateStatus::Failed)
|
||||
.expect("prefetched error should mark the attempted candidate as failed");
|
||||
assert!(stored_candidates
|
||||
.iter()
|
||||
.all(|candidate| candidate.status != RequestCandidateStatus::Success));
|
||||
assert_eq!(failed_candidate.status_code, Some(429));
|
||||
assert_eq!(
|
||||
failed_candidate.error_type.as_deref(),
|
||||
Some("rate_limit_error")
|
||||
);
|
||||
assert_eq!(failed_candidate.error_message.as_deref(), Some("slow down"));
|
||||
assert!(failed_candidate.finished_at_unix_ms.is_some());
|
||||
assert_eq!(*public_hits.lock().expect("mutex should lock"), 0);
|
||||
|
||||
gateway_handle.abort();
|
||||
|
||||
@@ -349,7 +349,7 @@ async fn gateway_executes_gemini_chat_stream_via_local_decision_gate_with_local_
|
||||
});
|
||||
let frames = concat!(
|
||||
"{\"type\":\"headers\",\"payload\":{\"kind\":\"headers\",\"status_code\":200,\"headers\":{\"content-type\":\"text/event-stream\"}}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"candidates\\\":[]}\\n\\n\"}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"candidates\\\":[{\\\"content\\\":{\\\"parts\\\":[{\\\"text\\\":\\\"ok\\\"}]},\\\"finishReason\\\":\\\"STOP\\\"}]}\\n\\n\"}}\n",
|
||||
"{\"type\":\"telemetry\",\"payload\":{\"kind\":\"telemetry\",\"telemetry\":{\"elapsed_ms\":33,\"upstream_bytes\":26}}}\n",
|
||||
"{\"type\":\"eof\",\"payload\":{\"kind\":\"eof\"}}\n"
|
||||
);
|
||||
@@ -419,7 +419,7 @@ async fn gateway_executes_gemini_chat_stream_via_local_decision_gate_with_local_
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
strip_sse_keepalive_comments(&response.text().await.expect("body should read")),
|
||||
"data: {\"candidates\":[]}\n\n"
|
||||
"data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"ok\"}]},\"finishReason\":\"STOP\"}]}\n\n"
|
||||
);
|
||||
|
||||
let seen_execution_runtime_request = seen_execution_runtime
|
||||
|
||||
@@ -326,7 +326,7 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_with_local_s
|
||||
});
|
||||
let frames = concat!(
|
||||
"{\"type\":\"headers\",\"payload\":{\"kind\":\"headers\",\"status_code\":200,\"headers\":{\"content-type\":\"text/event-stream\"}}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"candidates\\\":[]}\\n\\n\"}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"candidates\\\":[{\\\"content\\\":{\\\"parts\\\":[{\\\"text\\\":\\\"ok\\\"}]},\\\"finishReason\\\":\\\"STOP\\\"}]}\\n\\n\"}}\n",
|
||||
"{\"type\":\"telemetry\",\"payload\":{\"kind\":\"telemetry\",\"telemetry\":{\"elapsed_ms\":34,\"upstream_bytes\":26}}}\n",
|
||||
"{\"type\":\"eof\",\"payload\":{\"kind\":\"eof\"}}\n"
|
||||
);
|
||||
@@ -396,7 +396,7 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_with_local_s
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
strip_sse_keepalive_comments(&response.text().await.expect("body should read")),
|
||||
"data: {\"candidates\":[]}\n\n"
|
||||
"data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"ok\"}]},\"finishReason\":\"STOP\"}]}\n\n"
|
||||
);
|
||||
|
||||
let seen_execution_runtime_request = seen_execution_runtime
|
||||
@@ -846,7 +846,7 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
});
|
||||
let frames = concat!(
|
||||
"{\"type\":\"headers\",\"payload\":{\"kind\":\"headers\",\"status_code\":200,\"headers\":{\"content-type\":\"text/event-stream\"}}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"response\\\":{\\\"candidates\\\":[]},\\\"remainingCredits\\\":42,\\\"consumedCredits\\\":1,\\\"traceId\\\":\\\"trace-upstream-1\\\"}\\n\\n\"}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"response\\\":{\\\"candidates\\\":[{\\\"content\\\":{\\\"parts\\\":[{\\\"text\\\":\\\"ok\\\"}]},\\\"finishReason\\\":\\\"STOP\\\"}]},\\\"remainingCredits\\\":42,\\\"consumedCredits\\\":1,\\\"traceId\\\":\\\"trace-upstream-1\\\"}\\n\\n\"}}\n",
|
||||
"{\"type\":\"telemetry\",\"payload\":{\"kind\":\"telemetry\",\"telemetry\":{\"elapsed_ms\":34,\"upstream_bytes\":26}}}\n",
|
||||
"{\"type\":\"eof\",\"payload\":{\"kind\":\"eof\"}}\n"
|
||||
);
|
||||
@@ -934,7 +934,7 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
strip_sse_keepalive_comments(&response.text().await.expect("body should read")),
|
||||
"data: {\"candidates\":[]}\n\n"
|
||||
"data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"ok\"}]},\"finishReason\":\"STOP\"}]}\n\n"
|
||||
);
|
||||
|
||||
let seen_refresh_request = seen_refresh
|
||||
@@ -1354,7 +1354,7 @@ async fn gateway_executes_vertex_ai_gemini_cli_stream_via_local_decision_gate_wi
|
||||
});
|
||||
let frames = concat!(
|
||||
"{\"type\":\"headers\",\"payload\":{\"kind\":\"headers\",\"status_code\":200,\"headers\":{\"content-type\":\"text/event-stream\"}}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"candidates\\\":[]}\\n\\n\"}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"data: {\\\"candidates\\\":[{\\\"content\\\":{\\\"parts\\\":[{\\\"text\\\":\\\"ok\\\"}]},\\\"finishReason\\\":\\\"STOP\\\"}]}\\n\\n\"}}\n",
|
||||
"{\"type\":\"telemetry\",\"payload\":{\"kind\":\"telemetry\",\"telemetry\":{\"elapsed_ms\":34,\"upstream_bytes\":26}}}\n",
|
||||
"{\"type\":\"eof\",\"payload\":{\"kind\":\"eof\"}}\n"
|
||||
);
|
||||
@@ -1422,7 +1422,7 @@ async fn gateway_executes_vertex_ai_gemini_cli_stream_via_local_decision_gate_wi
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
strip_sse_keepalive_comments(&response.text().await.expect("body should read")),
|
||||
"data: {\"candidates\":[]}\n\n"
|
||||
"data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"ok\"}]},\"finishReason\":\"STOP\"}]}\n\n"
|
||||
);
|
||||
|
||||
let seen_execution_runtime_request = seen_execution_runtime
|
||||
|
||||
@@ -96,12 +96,11 @@ fn aether_data_backend_pool_modules_do_not_own_maintenance_sql() {
|
||||
#[test]
|
||||
fn wallet_maintenance_sql_is_partitioned_by_driver() {
|
||||
let facade = read_workspace_file("crates/aether-data/runtime/src/backend/wallet.rs");
|
||||
for module in ["mod postgres;"] {
|
||||
assert!(
|
||||
facade.contains(module),
|
||||
"wallet facade should declare {module}"
|
||||
);
|
||||
}
|
||||
let module = "mod postgres;";
|
||||
assert!(
|
||||
facade.contains(module),
|
||||
"wallet facade should declare {module}"
|
||||
);
|
||||
for forbidden in [
|
||||
"sqlx::",
|
||||
"PostgresBackend",
|
||||
@@ -115,24 +114,22 @@ fn wallet_maintenance_sql_is_partitioned_by_driver() {
|
||||
);
|
||||
}
|
||||
|
||||
for (driver, backend) in [("postgres", "PostgresBackend")] {
|
||||
let path = format!("crates/aether-data/runtime/src/backend/wallet/{driver}.rs");
|
||||
let source = read_workspace_file(&path);
|
||||
assert!(source.contains(&format!("impl {backend}")));
|
||||
assert!(source.contains("aggregate_wallet_daily_usage"));
|
||||
assert!(source.contains("sqlx::query"));
|
||||
}
|
||||
let (driver, backend) = ("postgres", "PostgresBackend");
|
||||
let path = format!("crates/aether-data/runtime/src/backend/wallet/{driver}.rs");
|
||||
let source = read_workspace_file(&path);
|
||||
assert!(source.contains(&format!("impl {backend}")));
|
||||
assert!(source.contains("aggregate_wallet_daily_usage"));
|
||||
assert!(source.contains("sqlx::query"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_maintenance_is_partitioned_for_each_driver() {
|
||||
let facade = read_workspace_file("crates/aether-data/runtime/src/backend/maintenance.rs");
|
||||
for module in ["mod postgres;"] {
|
||||
assert!(
|
||||
facade.contains(module),
|
||||
"maintenance facade should declare {module}"
|
||||
);
|
||||
}
|
||||
let module = "mod postgres;";
|
||||
assert!(
|
||||
facade.contains(module),
|
||||
"maintenance facade should declare {module}"
|
||||
);
|
||||
for forbidden in [
|
||||
"impl PostgresBackend",
|
||||
"VACUUM ANALYZE",
|
||||
@@ -156,12 +153,11 @@ fn table_maintenance_is_partitioned_for_each_driver() {
|
||||
#[test]
|
||||
fn system_driver_database_operations_are_partitioned() {
|
||||
let facade = read_workspace_file("crates/aether-data/runtime/src/backend/system.rs");
|
||||
for module in ["mod postgres;"] {
|
||||
assert!(
|
||||
facade.contains(module),
|
||||
"system facade should declare {module}"
|
||||
);
|
||||
}
|
||||
let module = "mod postgres;";
|
||||
assert!(
|
||||
facade.contains(module),
|
||||
"system facade should declare {module}"
|
||||
);
|
||||
for forbidden in [
|
||||
"impl PostgresBackend",
|
||||
"fn map_postgres_stats_daily_aggregate(",
|
||||
@@ -1521,12 +1517,11 @@ fn lifecycle_migrations_are_partitioned_by_driver() {
|
||||
types.contains(required),
|
||||
"migrate/types.rs should own {required}"
|
||||
);
|
||||
for forbidden in ["PgPool"] {
|
||||
assert!(
|
||||
!types.contains(forbidden),
|
||||
"migrate/types.rs should remain driver-independent from {forbidden}"
|
||||
);
|
||||
}
|
||||
let forbidden = "PgPool";
|
||||
assert!(
|
||||
!types.contains(forbidden),
|
||||
"migrate/types.rs should remain driver-independent from {forbidden}"
|
||||
);
|
||||
|
||||
let postgres =
|
||||
read_workspace_file("crates/aether-data/runtime/src/lifecycle/migrate/postgres.rs");
|
||||
@@ -1905,12 +1900,11 @@ fn gateway_system_config_types_are_owned_by_aether_data() {
|
||||
}
|
||||
let data_backends =
|
||||
read_workspace_file("crates/aether-data/runtime/src/backend/maintenance.rs");
|
||||
for pattern in ["postgres.list_system_config_entries().await"] {
|
||||
assert!(
|
||||
data_backends.contains(pattern),
|
||||
"aether-data backends should own driver-specific system config dispatch {pattern}"
|
||||
);
|
||||
}
|
||||
let pattern = "postgres.list_system_config_entries().await";
|
||||
assert!(
|
||||
data_backends.contains(pattern),
|
||||
"aether-data backends should own driver-specific system config dispatch {pattern}"
|
||||
);
|
||||
for pattern in [
|
||||
"|(key, value, description, updated_at_unix_secs)|",
|
||||
"Ok((0, 0, 0, 0))",
|
||||
|
||||
@@ -3705,9 +3705,9 @@ async fn gateway_completes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
||||
scores[0].hard_state.schedulable(),
|
||||
"OAuth completion should replace AuthInvalid with a schedulable score"
|
||||
);
|
||||
let decrypted_api_key = decrypt_persisted_provider_api_key(&persisted);
|
||||
let decrypted_api_key = decrypt_persisted_provider_api_key(persisted);
|
||||
assert_eq!(decrypted_api_key, "new-codex-access-token");
|
||||
let decrypted_auth_config = decrypt_persisted_provider_auth_config(&persisted);
|
||||
let decrypted_auth_config = decrypt_persisted_provider_auth_config(persisted);
|
||||
let auth_config: serde_json::Value =
|
||||
serde_json::from_str(&decrypted_auth_config).expect("auth config json should parse");
|
||||
assert_eq!(auth_config["provider_type"], "codex");
|
||||
|
||||
@@ -3834,12 +3834,12 @@ async fn gateway_overwrites_oauth_provider_key_credentials_from_admin_system_imp
|
||||
Some(&Value::Null)
|
||||
);
|
||||
assert_eq!(
|
||||
decrypt_test_provider_catalog_credential(&key, ProviderCatalogCredentialField::ApiKey,),
|
||||
decrypt_test_provider_catalog_credential(key, ProviderCatalogCredentialField::ApiKey,),
|
||||
"oauth-access-token-new"
|
||||
);
|
||||
|
||||
let auth_config =
|
||||
decrypt_test_provider_catalog_credential(&key, ProviderCatalogCredentialField::AuthConfig);
|
||||
decrypt_test_provider_catalog_credential(key, ProviderCatalogCredentialField::AuthConfig);
|
||||
let auth_config: Value =
|
||||
serde_json::from_str(&auth_config).expect("oauth auth config json should parse");
|
||||
assert_eq!(auth_config["provider_type"], "codex");
|
||||
|
||||
@@ -2659,9 +2659,9 @@ fn set_test_env_var(key: &'static str, value: &str) -> TestEnvVarGuard {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn payment_callback_env_lock() -> &'static std::sync::Mutex<()> {
|
||||
static LOCK: std::sync::OnceLock<std::sync::Mutex<()>> = std::sync::OnceLock::new();
|
||||
LOCK.get_or_init(|| std::sync::Mutex::new(()))
|
||||
fn payment_callback_env_lock() -> &'static tokio::sync::Mutex<()> {
|
||||
static LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());
|
||||
&LOCK
|
||||
}
|
||||
|
||||
const TEST_PAYMENT_CALLBACK_SECRET: &str = "test-callback-secret-0123456789abcdef";
|
||||
@@ -11875,9 +11875,7 @@ async fn gateway_does_not_report_logout_success_when_session_revoke_is_rejected(
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_handles_payment_callback_route_locally_without_proxying_upstream() {
|
||||
let _env_lock = payment_callback_env_lock()
|
||||
.lock()
|
||||
.expect("payment callback test env lock should not be poisoned");
|
||||
let _env_lock = payment_callback_env_lock().lock().await;
|
||||
let _secret_guard = set_test_env_var("PAYMENT_CALLBACK_SECRET", TEST_PAYMENT_CALLBACK_SECRET);
|
||||
let now = Utc::now();
|
||||
let user = StoredUserAuthRecord::new(
|
||||
@@ -12020,9 +12018,7 @@ async fn gateway_handles_payment_callback_route_locally_without_proxying_upstrea
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_rejects_payment_callback_with_mismatched_payment_method_locally() {
|
||||
let _env_lock = payment_callback_env_lock()
|
||||
.lock()
|
||||
.expect("payment callback test env lock should not be poisoned");
|
||||
let _env_lock = payment_callback_env_lock().lock().await;
|
||||
let _secret_guard = set_test_env_var("PAYMENT_CALLBACK_SECRET", TEST_PAYMENT_CALLBACK_SECRET);
|
||||
let now = Utc::now();
|
||||
let user = StoredUserAuthRecord::new(
|
||||
|
||||
@@ -1076,7 +1076,7 @@ async fn gateway_forwards_public_request_to_remote_tunnel_owner_before_fallback_
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![sample_provider_with_request_timeout(
|
||||
"provider-owner",
|
||||
Some(0.1),
|
||||
Some(2.0),
|
||||
)],
|
||||
vec![sample_endpoint("endpoint-owner", "provider-owner")],
|
||||
vec![sample_bound_key(
|
||||
|
||||
@@ -1100,7 +1100,7 @@ async fn sync_transport_error_policy_stops_or_retries_candidates_end_to_end_impl
|
||||
let mut second_candidate = sample_local_openai_candidate_row();
|
||||
second_candidate.key_id = "key-openai-usage-local-2".to_string();
|
||||
second_candidate.key_name = "secondary".to_string();
|
||||
second_candidate.key_internal_priority = second_candidate.key_internal_priority - 1;
|
||||
second_candidate.key_internal_priority -= 1;
|
||||
let candidate_selection_repository =
|
||||
Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
|
||||
sample_local_openai_candidate_row(),
|
||||
|
||||
@@ -77,7 +77,6 @@ mod tests {
|
||||
status_codes: [400, 413].into_iter().collect(),
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
assert!(validate_routing_failover_rules(&rules).is_ok());
|
||||
}
|
||||
|
||||
@@ -1310,16 +1310,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn profile_values_depend_only_on_seed_sequence_and_domain() {
|
||||
let mut config = Config::default();
|
||||
config.seed = 0xfeed_beef;
|
||||
config.first_byte_delay = Duration::from_millis(100);
|
||||
config.first_byte_jitter = Duration::from_millis(50);
|
||||
config.chunk_delay = Duration::from_millis(30);
|
||||
config.chunk_delay_jitter = Duration::from_millis(10);
|
||||
config.payload_bytes = 128;
|
||||
config.payload_bytes_jitter = 64;
|
||||
config.fault_truncate_stream_bps = BASIS_POINTS;
|
||||
config.chunks = 9;
|
||||
let config = Config {
|
||||
seed: 0xfeed_beef,
|
||||
first_byte_delay: Duration::from_millis(100),
|
||||
first_byte_jitter: Duration::from_millis(50),
|
||||
chunk_delay: Duration::from_millis(30),
|
||||
chunk_delay_jitter: Duration::from_millis(10),
|
||||
payload_bytes: 128,
|
||||
payload_bytes_jitter: 64,
|
||||
fault_truncate_stream_bps: BASIS_POINTS,
|
||||
chunks: 9,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let first = request_profile(&config, 1234);
|
||||
let unrelated = request_profile(&config, 9999);
|
||||
@@ -1345,11 +1347,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn fault_buckets_are_disjoint_and_ordered() {
|
||||
let mut config = Config::default();
|
||||
config.fault_429_bps = 100;
|
||||
config.fault_500_bps = 200;
|
||||
config.fault_timeout_bps = 300;
|
||||
config.fault_truncate_stream_bps = 400;
|
||||
let config = Config {
|
||||
fault_429_bps: 100,
|
||||
fault_500_bps: 200,
|
||||
fault_timeout_bps: 300,
|
||||
fault_truncate_stream_bps: 400,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(select_fault(&config, 0), Fault::Status429);
|
||||
assert_eq!(select_fault(&config, 99), Fault::Status429);
|
||||
@@ -1435,12 +1439,14 @@ mod tests {
|
||||
let bind = listener
|
||||
.local_addr()
|
||||
.expect("test listener should have a local address");
|
||||
let mut config = Config::default();
|
||||
config.binds = vec![bind];
|
||||
config.chunks = 0;
|
||||
config.chunk_delay = Duration::ZERO;
|
||||
config.fault_truncate_stream_bps = BASIS_POINTS;
|
||||
config.seed = 0x1234_5678;
|
||||
let config = Config {
|
||||
binds: vec![bind],
|
||||
chunks: 0,
|
||||
chunk_delay: Duration::ZERO,
|
||||
fault_truncate_stream_bps: BASIS_POINTS,
|
||||
seed: 0x1234_5678,
|
||||
..Default::default()
|
||||
};
|
||||
let metrics = Arc::new(Metrics::for_binds(&config.binds));
|
||||
let router = build_router(config, Arc::clone(&metrics), bind);
|
||||
let server = tokio::spawn(async move {
|
||||
|
||||
@@ -1399,10 +1399,13 @@ async fn run_mock_upstream(
|
||||
}
|
||||
}
|
||||
}
|
||||
AxumWsMessage::Ping(payload) => {
|
||||
if socket.send(AxumWsMessage::Pong(payload)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
AxumWsMessage::Ping(payload)
|
||||
if socket
|
||||
.send(AxumWsMessage::Pong(payload.clone()))
|
||||
.await
|
||||
.is_err() =>
|
||||
{
|
||||
break;
|
||||
}
|
||||
AxumWsMessage::Close(_) => break,
|
||||
_ => {}
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ type MappingTestState = {
|
||||
|
||||
const endpoints = [
|
||||
{ id: 'chat', api_format: 'openai:chat', base_url: 'https://example.com', is_active: true, active_keys: 1 },
|
||||
{ id: 'claude', api_format: 'claude:chat', base_url: 'https://example.com', is_active: true, active_keys: 1 },
|
||||
{ id: 'claude', api_format: 'claude:messages', base_url: 'https://example.com', is_active: true, active_keys: 1 },
|
||||
] as MappingTabProps['endpoints']
|
||||
|
||||
function createTestKey(overrides: Partial<EndpointAPIKey>): EndpointAPIKey {
|
||||
@@ -94,7 +94,7 @@ function createTestKey(overrides: Partial<EndpointAPIKey>): EndpointAPIKey {
|
||||
|
||||
const testKeys = [
|
||||
createTestKey({ id: 'chat-key', name: 'Chat Key', api_key_masked: 'sk-****chat', api_formats: ['openai:chat'], internal_priority: 0 }),
|
||||
createTestKey({ id: 'claude-key', name: 'Claude Key', api_formats: ['claude:chat'], internal_priority: 1 }),
|
||||
createTestKey({ id: 'claude-key', name: 'Claude Key', api_formats: ['claude:messages'], internal_priority: 1 }),
|
||||
createTestKey({ id: 'disabled-key', is_active: false, internal_priority: 2 }),
|
||||
]
|
||||
const mounted: Array<{ app: App; root: HTMLElement }> = []
|
||||
|
||||
Reference in New Issue
Block a user