build(rust): upgrade reqwest to 0.13 and move MCP out of litellm-http

The workspace moves to reqwest 0.13, so litellm-http no longer carries a
second reqwest just for rmcp. The mcp feature, its pool and its proxy
conversion go away, and the gateway hands rmcp a pooled NoRedirect client
that already carries the TLS, proxy and timeout settings

The Client test helpers collapse into for_test, a no-redirect client
without TLS or proxy settings, and wrap_for_test for the few tests that
need a hand-built reqwest client

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yujong Lee 2026-10-23 09:15:15 -07:00
parent bc18e10f6b
commit 91d4db7d44
49 changed files with 151 additions and 371 deletions

108
litellm-rust/Cargo.lock generated
View file

@ -2329,7 +2329,7 @@ dependencies = [
"hmac 0.13.0",
"http 1.4.2",
"jiff",
"reqwest 0.13.5",
"reqwest",
"rustc_version",
"rustls 0.23.42",
"rustls-pki-types",
@ -2387,7 +2387,7 @@ dependencies = [
"pin-project",
"prost",
"prost-types",
"reqwest 0.13.5",
"reqwest",
"rustc_version",
"serde",
"serde_json",
@ -2883,7 +2883,6 @@ dependencies = [
"tokio",
"tokio-rustls 0.26.4",
"tower-service",
"webpki-roots",
]
[[package]]
@ -3376,7 +3375,7 @@ dependencies = [
"litellm-auth-types",
"litellm-http",
"moka",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -3460,7 +3459,7 @@ dependencies = [
"litellm-cache-response",
"litellm-cache-testing",
"litellm-http",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde_json",
"tokio",
@ -3495,7 +3494,7 @@ dependencies = [
"litellm-cache-testing",
"litellm-http",
"percent-encoding",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde_json",
"tokio",
@ -3522,7 +3521,7 @@ dependencies = [
"litellm-cache-testing",
"litellm-http",
"qdrant-client",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -3598,7 +3597,7 @@ dependencies = [
"litellm-cache",
"litellm-cache-testing",
"litellm-http",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde_json",
"tokio",
@ -3945,9 +3944,7 @@ dependencies = [
"hyper-util",
"litellm-core-utils",
"rcgen",
"reqwest 0.12.28",
"reqwest 0.13.5",
"rmcp",
"reqwest",
"rstest",
"rustls 0.23.42",
"rustls-native-certs",
@ -3980,7 +3977,7 @@ dependencies = [
"litellm-llms",
"litellm-secrets",
"litellm-tracing",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4039,7 +4036,7 @@ dependencies = [
"litellm-router-types",
"litellm-secrets",
"litellm-tracing",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4100,7 +4097,7 @@ dependencies = [
"litellm-llms-types",
"litellm-secrets",
"litellm-tracing",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde_json",
"tokio",
@ -4163,7 +4160,7 @@ dependencies = [
"litellm-python-compat",
"litellm-router-types",
"litellm-secrets",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4245,7 +4242,7 @@ dependencies = [
"pyo3",
"pyo3-async-runtimes",
"redis",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4316,7 +4313,7 @@ dependencies = [
"litellm-secrets-hashicorp",
"litellm-secrets-types",
"moka",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4358,7 +4355,7 @@ dependencies = [
"litellm-http",
"litellm-secrets-types",
"percent-encoding",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4381,7 +4378,7 @@ dependencies = [
"moka",
"percent-encoding",
"rcgen",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4408,7 +4405,7 @@ dependencies = [
"litellm-secrets-types",
"moka",
"percent-encoding",
"reqwest 0.12.28",
"reqwest",
"rstest",
"serde",
"serde_json",
@ -4496,7 +4493,7 @@ version = "0.1.0"
dependencies = [
"flate2",
"futures-util",
"reqwest 0.12.28",
"reqwest",
"rstest",
"semver",
"serde",
@ -5869,49 +5866,6 @@ version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "reqwest"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64 0.22.1",
"bytes",
"futures-core",
"futures-util",
"h2 0.4.15",
"http 1.4.2",
"http-body 1.1.0",
"http-body-util",
"hyper 1.10.1",
"hyper-rustls 0.27.9",
"hyper-util",
"js-sys",
"log",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.42",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-rustls 0.26.4",
"tokio-util",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams 0.4.2",
"web-sys",
"webpki-roots",
]
[[package]]
name = "reqwest"
version = "0.13.5"
@ -5933,6 +5887,7 @@ dependencies = [
"js-sys",
"log",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"quinn",
@ -5952,7 +5907,7 @@ dependencies = [
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams 0.5.0",
"wasm-streams",
"web-sys",
]
@ -5999,7 +5954,7 @@ dependencies = [
"pin-project-lite",
"process-wrap",
"rand 0.10.2",
"reqwest 0.13.5",
"reqwest",
"schemars 1.2.2",
"serde",
"serde_json",
@ -6122,7 +6077,7 @@ dependencies = [
"async-trait",
"bytes",
"http 1.4.2",
"reqwest 0.13.5",
"reqwest",
"rustify_derive",
"serde",
"serde_json",
@ -7154,7 +7109,7 @@ dependencies = [
"memchr",
"parse-display",
"pin-project-lite",
"reqwest 0.13.5",
"reqwest",
"serde",
"serde_json",
"serde_with",
@ -7793,7 +7748,7 @@ dependencies = [
"futures",
"pin-project",
"rand 0.10.2",
"reqwest 0.13.5",
"reqwest",
"serde",
"serde_json",
"time",
@ -8007,7 +7962,7 @@ dependencies = [
"async-trait",
"derive_builder",
"http 1.4.2",
"reqwest 0.13.5",
"reqwest",
"rustify",
"rustify_derive",
"serde",
@ -8154,19 +8109,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "wasm-streams"
version = "0.5.0"

View file

@ -87,7 +87,7 @@ pyo3-async-runtimes = { version = "0.29.0", features = ["tokio-runtime"] }
rand = "0.8"
macro_rules_attribute = "0.2.3"
schemars = "1"
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "rustls-tls", "http2", "stream"] }
reqwest = { version = "0.13", default-features = false, features = ["json", "multipart", "rustls", "http2", "stream", "query"] }
qdrant-client = { version = "1.19.0", default-features = false }
uuid = { version = "1", features = ["v4"] }
rstest = "0.26.1"

View file

@ -10,8 +10,11 @@ disallowed-methods = [
{ path = "reqwest::Client::new", reason = "take litellm_http::Client from HttpClientPool" },
{ path = "reqwest::Client::builder", reason = "HttpClientConfig owns client construction" },
{ path = "reqwest::ClientBuilder::danger_accept_invalid_certs", reason = "set HttpClientConfig::verify instead" },
{ path = "reqwest::ClientBuilder::tls_danger_accept_invalid_certs", reason = "set HttpClientConfig::verify instead" },
{ path = "reqwest::ClientBuilder::tls_danger_accept_invalid_hostnames", reason = "set HttpClientConfig::verify instead" },
{ path = "reqwest::ClientBuilder::identity", reason = "set HttpClientConfig::client_certificate instead" },
{ path = "reqwest::ClientBuilder::use_preconfigured_tls", reason = "HttpClientConfig owns the TLS configuration" },
{ path = "reqwest::ClientBuilder::tls_backend_preconfigured", reason = "HttpClientConfig owns the TLS configuration" },
{ path = "sqlx::query", reason = "use sqlx::query! or query_file! so the SQL is checked against the migrated schema" },
{ path = "sqlx::query_as", reason = "use sqlx::query_as! or query_file_as! so the SQL is checked against the migrated schema" },
{ path = "sqlx::query_scalar", reason = "use sqlx::query_scalar! so the SQL is checked against the migrated schema" },

View file

@ -1080,7 +1080,7 @@ mod tests {
&no_env,
)
.await?;
let client = litellm_http::Client::plain_for_test();
let client = litellm_http::Client::for_test();
let mut failures = Vec::new();
for region in ["us-west-2", "us-east-1"] {

View file

@ -31,7 +31,7 @@ async fn connect(server: &MockServer) -> AzureBlobCache<JsonCodec<Value>> {
None,
ClientOptions {
transport: Some(Transport::new(Arc::new(ReqwestTransport(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
)))),
..ClientOptions::default()
},

View file

@ -97,7 +97,7 @@ async fn cache_exposes_its_configuration(#[future(awt)] server: MockServer) {
path_service_account: Some("/secrets/sa.json".into()),
..support::config(&server, Some("folder"))
},
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
litellm_cache::JsonCodec::<Value>::new(),
);
assert_eq!(cache.bucket_name(), "bucket");

View file

@ -29,7 +29,7 @@ pub fn cache_with_token(
) -> JsonGcsCache {
GcsCache::with_token_source(
config(server, gcs_path),
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
JsonCodec::new(),
token,
)

View file

@ -108,7 +108,7 @@ fn config(base: String, timeout: Option<Duration>) -> OpenAiEmbedderConfig {
async fn posts_embeddings_request_and_parses_vector() {
let server = TestHttpServer::response("200 OK", r#"{"data":[{"embedding":[0.1,0.2]}]}"#).await;
let embedder = OpenAiEmbedder::new(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
config(
format!("{}/", server.base_url()),
Some(Duration::from_secs(1)),
@ -161,7 +161,7 @@ async fn status_timeout_and_body_errors_are_unavailable(
let server =
TestHttpServer::response_after(status, body, Duration::from_millis(delay_ms)).await;
let embedder = OpenAiEmbedder::new(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
config(server.base_url(), timeout),
);
assert_eq!(embedder.async_embed("hello", None).await, expected);
@ -170,7 +170,7 @@ async fn status_timeout_and_body_errors_are_unavailable(
#[rstest]
fn sync_embedding_is_unsupported() {
let embedder = OpenAiEmbedder::new(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
config("http://127.0.0.1:9".to_owned(), None),
);
assert_eq!(

View file

@ -768,7 +768,7 @@ async fn gcs() -> (wiremock::MockServer, Gcs) {
path_service_account: None,
endpoint: server.uri(),
},
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
litellm_cache_response::ResponseCacheCodec,
Arc::new(litellm_cache_gcs::StaticTokenSource("token".into())),
)));

View file

@ -34,7 +34,7 @@ pub fn config(endpoint: &str) -> S3CacheConfig {
pub fn cache_with(config: S3CacheConfig, runtime: Handle) -> JsonS3Cache {
S3Cache::new(
config,
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
JsonCodec::new(),
runtime,
)

View file

@ -19,7 +19,7 @@ url.workspace = true
moka.workspace = true
sha2.workspace = true
tower = { version = "0.5", features = ["util"] }
rmcp = { version = "=3.4.1", default-features = false, features = ["server", "client", "elicitation", "transport-streamable-http-server", "transport-streamable-http-client", "transport-child-process"] }
rmcp = { version = "=3.4.1", default-features = false, features = ["server", "client", "elicitation", "transport-streamable-http-server", "transport-streamable-http-client-reqwest", "transport-child-process"] }
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true

View file

@ -19,7 +19,7 @@ litellm-gateway-auth.workspace = true
litellm-gateway-ui.workspace = true
litellm-auth-types.workspace = true
litellm-config.workspace = true
litellm-http = { workspace = true, features = ["mcp"] }
litellm-http.workspace = true
litellm-llms.workspace = true
litellm-secrets.workspace = true
litellm-tracing.workspace = true

View file

@ -101,11 +101,11 @@ pub async fn build_mcp(
Auth::from_config(config, secrets.clone())
.validate()
.await?;
let client = pool.mcp_client(http)?;
let client = pool.client(http, ClientVariant::NoRedirect)?;
Ok(Some(
litellm_gateway_mcp::ConfiguredGateway::connect(
&config.mcp_servers,
client,
(*client).clone(),
secrets.as_ref(),
shutdown,
)

View file

@ -7,11 +7,8 @@ repository.workspace = true
[features]
test-support = []
mcp = ["dep:rmcp", "dep:reqwest-mcp"]
[dependencies]
rmcp = { version = "=3.4.1", default-features = false, features = ["client", "transport-streamable-http-client-reqwest"], optional = true }
reqwest-mcp = { package = "reqwest", version = "0.13.2", default-features = false, features = ["rustls-no-provider", "json", "stream", "http2"], optional = true }
http.workspace = true
litellm-core-utils.workspace = true
hyper-util.workspace = true

View file

@ -1,5 +1,6 @@
use std::ops::Deref;
/// Only `HttpClientPool` builds one, so every client carries the resolved TLS, proxy and timeout settings.
#[derive(Clone, Debug)]
pub struct Client(reqwest::Client);
@ -9,17 +10,7 @@ impl Client {
}
#[cfg(any(test, feature = "test-support"))]
pub fn for_test(client: reqwest::Client) -> Self {
Self(client)
}
#[cfg(any(test, feature = "test-support"))]
pub fn plain_for_test() -> Self {
Self(reqwest::Client::new())
}
#[cfg(any(test, feature = "test-support"))]
pub fn no_redirect_for_test() -> Self {
pub fn for_test() -> Self {
Self(
reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
@ -27,6 +18,11 @@ impl Client {
.expect("a client without TLS or proxy settings builds"),
)
}
#[cfg(any(test, feature = "test-support"))]
pub fn wrap_for_test(client: reqwest::Client) -> Self {
Self(client)
}
}
impl Deref for Client {

View file

@ -98,7 +98,7 @@ impl TryFrom<&HttpClientConfig> for reqwest::ClientBuilder {
fn try_from(config: &HttpClientConfig) -> Result<Self, Self::Error> {
let base = reqwest::Client::builder()
.use_preconfigured_tls(rustls::ClientConfig::try_from(config)?)
.tls_backend_preconfigured(rustls::ClientConfig::try_from(config)?)
.connect_timeout(config.connect_timeout)
.pool_idle_timeout(config.pool_idle_timeout);
let with_keepalive = match config.tcp_keepalive {

View file

@ -7,8 +7,6 @@
mod client;
mod config;
mod error;
#[cfg(feature = "mcp")]
mod mcp;
pub mod media;
pub mod outbound;
mod pool;

View file

@ -1,76 +0,0 @@
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr},
sync::{Mutex, PoisonError},
time::{Duration, Instant},
};
use crate::{Error, HttpClientConfig};
struct Entry {
client: reqwest_mcp::Client,
built_at: Instant,
}
#[derive(Default)]
pub(super) struct Pool(Mutex<HashMap<HttpClientConfig, Entry>>);
impl Pool {
pub fn client(
&self,
config: &HttpClientConfig,
ttl: Duration,
) -> Result<reqwest_mcp::Client, Error> {
let mut clients = self.0.lock().unwrap_or_else(PoisonError::into_inner);
if let Some(entry) = clients.get(config)
&& entry.built_at.elapsed() < ttl
{
return Ok(entry.client.clone());
}
let client = build(config)?;
clients.insert(
config.clone(),
Entry {
client: client.clone(),
built_at: Instant::now(),
},
);
Ok(client)
}
}
fn build(config: &HttpClientConfig) -> Result<reqwest_mcp::Client, Error> {
let base = reqwest_mcp::Client::builder()
.tls_backend_preconfigured(rustls::ClientConfig::try_from(config)?)
.connect_timeout(config.connect_timeout)
.pool_idle_timeout(config.pool_idle_timeout)
.redirect(reqwest_mcp::redirect::Policy::none());
let keepalive = match config.tcp_keepalive {
Some(value) => base
.tcp_keepalive(value.idle)
.tcp_keepalive_interval(value.interval)
.tcp_keepalive_retries(value.retries),
None => base,
};
let address = if config.force_ipv4 {
keepalive.local_address(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
} else {
keepalive
};
let protocol = if config.http2 {
address
} else {
address.http1_only()
};
let agent = match &config.user_agent {
Some(value) => protocol.user_agent(value),
None => protocol,
};
config
.proxies
.mcp_proxies()
.into_iter()
.fold(agent.no_proxy(), reqwest_mcp::ClientBuilder::proxy)
.build()
.map_err(|error| Error::Client(error.without_url().to_string()))
}

View file

@ -522,7 +522,7 @@ mod tests {
b"HTTP/1.1 200 OK\r\nContent-Type: application/pdf; charset=binary\r\nContent-Length: 3\r\nConnection: close\r\n\r\nabc",
)
.await;
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let media = MediaFetcher::for_test(client)
.fetch(url, policy(3, 0))
.await
@ -538,7 +538,7 @@ mod tests {
b"HTTP/1.1 200 OK\r\nContent-Type: application/pdf\r\nContent-Length: 3\r\nConnection: close\r\n\r\nabc",
)
.await;
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let error = MediaFetcher::for_test(client)
.fetch(url, policy(2, 0))
.await
@ -553,7 +553,7 @@ mod tests {
b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n2\r\nab\r\n2\r\ncd\r\n0\r\n\r\n",
)
.await;
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let error = MediaFetcher::for_test(client)
.fetch(url, policy(3, 0))
.await

View file

@ -29,8 +29,6 @@ pub struct HttpClientPool {
media_resolver: Arc<dyn Resolve>,
ttl: Duration,
clients: Mutex<Clients>,
#[cfg(feature = "mcp")]
mcp: crate::mcp::Pool,
}
impl HttpClientPool {
@ -43,19 +41,9 @@ impl HttpClientPool {
media_resolver,
ttl,
clients: Mutex::default(),
#[cfg(feature = "mcp")]
mcp: crate::mcp::Pool::default(),
}
}
#[cfg(feature = "mcp")]
pub fn mcp_client(
&self,
config: &HttpClientConfig,
) -> Result<impl rmcp::transport::streamable_http_client::StreamableHttpClient, Error> {
self.mcp.client(config, self.ttl)
}
pub fn client(
&self,
config: &HttpClientConfig,
@ -108,7 +96,7 @@ impl HttpClientPool {
}
ClientVariant::Media => builder
.redirect(reqwest::redirect::Policy::none())
.dns_resolver2(Arc::clone(&self.media_resolver)),
.dns_resolver(Arc::clone(&self.media_resolver)),
}
}
}
@ -390,56 +378,4 @@ mod tests {
.is_err()
);
}
#[cfg(feature = "mcp")]
#[rstest::rstest]
#[tokio::test]
async fn mcp_clients_reuse_connections_and_apply_proxy_headers_without_redirects() {
let (address, connections, requests) = serve("HTTP/1.1 302 Found").await;
let pool = pool();
let config = HttpClientConfig {
proxies: proxied_through(&format!("http://{address}")),
..config("mcp-pool-test")
};
for _ in 0..2 {
let response = pool
.mcp
.client(&config, pool.ttl)
.unwrap()
.get("http://upstream.invalid/mcp")
.timeout(Duration::from_secs(2))
.send()
.await
.unwrap();
assert_eq!(response.status(), 302);
response.bytes().await.unwrap();
}
assert_eq!(connections.load(Ordering::SeqCst), 1);
let requests = requests.lock().unwrap();
assert_eq!(requests.len(), 2);
assert!(
requests
.iter()
.all(|request| request.starts_with("GET http://upstream.invalid/mcp HTTP/1.1"))
);
assert!(
requests
.iter()
.all(|request| request.contains("user-agent: mcp-pool-test"))
);
}
#[cfg(feature = "mcp")]
#[rstest::rstest]
#[tokio::test]
async fn mcp_client_uses_host_tls_configuration() {
let directory = tempfile::tempdir().unwrap();
let config = HttpClientConfig {
verify: Verify::CaBundle(directory.path().join("missing.pem")),
..config("mcp-test")
};
assert!(matches!(
pool().mcp_client(&config),
Err(Error::Read { .. })
));
}
}

View file

@ -63,19 +63,6 @@ impl EnvironmentProxies {
.map(|proxy| proxy.no_proxy(no_proxy.clone()))
.collect()
}
#[cfg(feature = "mcp")]
pub(crate) fn mcp_proxies(&self) -> Vec<reqwest_mcp::Proxy> {
let no_proxy = reqwest_mcp::NoProxy::from_string(&self.no);
[
reqwest_mcp::Proxy::http(self.http.as_str()),
reqwest_mcp::Proxy::https(self.https.as_str()),
reqwest_mcp::Proxy::all(self.all.as_str()),
]
.into_iter()
.filter_map(Result::ok)
.map(|proxy| proxy.no_proxy(no_proxy.clone()))
.collect()
}
}
#[cfg(test)]

View file

@ -283,7 +283,7 @@ mod tests {
let (provider, config, request) = prepared(&upstream.uri());
execute(
&Client::plain_for_test(),
&Client::for_test(),
&AuthServices::default(),
config,
provider,
@ -328,7 +328,7 @@ mod tests {
let (provider, config, request) = prepared(&upstream.uri());
let error = execute(
&Client::plain_for_test(),
&Client::for_test(),
&AuthServices::default(),
config,
provider,

View file

@ -239,7 +239,7 @@ async fn chat_cache_identity_follows_resolved_configuration_and_request_callback
.store(usize::from(call >= 2), Ordering::SeqCst);
let cache = ScopedCache::new(cache.clone(), CacheScope::Shared);
ChatCompletionsRoute::new(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
Arc::new(Default::default()),
secrets.clone(),
)

View file

@ -888,7 +888,7 @@ mod tests {
.respond_with(ResponseTemplate::new(200).set_body_raw(body, "text/event-stream"))
.mount(&upstream)
.await;
let response = litellm_http::Client::plain_for_test()
let response = litellm_http::Client::for_test()
.get(upstream.uri())
.send()
.await

View file

@ -121,11 +121,7 @@ mod tests {
};
fn client() -> OcrClient {
OcrClient::for_test(
litellm_http::Client::plain_for_test(),
litellm_http::Client::no_redirect_for_test(),
)
.with_secrets(litellm_inference_testing::no_secrets())
OcrClient::for_test().with_secrets(litellm_inference_testing::no_secrets())
}
fn request(model: &str, base: &str, document: Value, options: Value) -> LiteLLMOcrRequest {

View file

@ -190,16 +190,14 @@ mod tests {
clippy::disallowed_types,
reason = "the pool has no default-header setting to stand in for provider credentials"
)]
let provider_http = litellm_http::Client::for_test(
let provider_http = litellm_http::Client::wrap_for_test(
reqwest::Client::builder()
.default_headers(provider_headers)
.build()
.unwrap(),
);
let client = crate::base_llm::ocr::handler::OcrClient::for_test(
provider_http,
litellm_http::Client::no_redirect_for_test(),
);
let client =
crate::base_llm::ocr::handler::OcrClient::for_test().with_provider_http(provider_http);
let converted = inline_remote_document(
client.document_fetcher(),
OcrDocument::ImageUrl {

View file

@ -86,21 +86,27 @@ impl OcrClient {
}
#[cfg(any(test, feature = "test-support"))]
pub fn for_test(provider_http: Client, no_redirect_http: Client) -> Self {
pub fn for_test() -> Self {
Self {
secrets: Arc::new(
litellm_secrets::source::EnvironmentSecrets::python_compatible(
provider_http.clone(),
),
litellm_secrets::source::EnvironmentSecrets::python_compatible(Client::for_test()),
),
provider_http,
polling_http: no_redirect_http.clone(),
document_fetcher: MediaFetcher::for_test(no_redirect_http),
provider_http: Client::for_test(),
polling_http: Client::for_test(),
document_fetcher: MediaFetcher::for_test(Client::for_test()),
auth: Arc::new(AuthServices::default()),
settings: OcrSettings::default(),
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn with_provider_http(self, provider_http: Client) -> Self {
Self {
provider_http,
..self
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn with_settings(self, settings: OcrSettings) -> Self {
Self { settings, ..self }
@ -322,7 +328,7 @@ mod tests {
let _connection = listener.accept().await.unwrap();
tokio::time::sleep(Duration::from_secs(1)).await;
});
let error = litellm_http::Client::plain_for_test()
let error = litellm_http::Client::for_test()
.get(format!("http://{address}"))
.timeout(Duration::from_millis(10))
.send()
@ -347,7 +353,7 @@ mod tests {
socket.write_all(response.as_bytes()).await.unwrap();
std::future::pending::<()>().await;
});
let response = litellm_http::Client::plain_for_test()
let response = litellm_http::Client::for_test()
.get(format!("http://{address}"))
.send()
.await

View file

@ -566,10 +566,7 @@ mod tests {
let params = ReductoParseV3Config
.map_ocr_params(&overrides, "parse-v3")
.unwrap();
let client = OcrClient::for_test(
litellm_http::Client::plain_for_test(),
litellm_http::Client::no_redirect_for_test(),
);
let client = OcrClient::for_test();
let connection = OcrConnection::default();
let document = serde_json::from_value(
json!({"type":"document_url","document_url":"reducto://ready.pdf"}),

View file

@ -192,7 +192,7 @@ handler.get_secret_from_manager = get_secret_from_manager
KeyManagementSettings::default(),
)),
Arc::new(move |_: &str| fallback.map(str::to_owned)),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
)
.with_failure_policy(FailurePolicy::EnvironmentFallback);
(resolver, locals, handler)

View file

@ -84,7 +84,7 @@ mod tests {
}
async fn resolve(state: Arc<SecretManagerState>, name: &'static str) -> Option<String> {
ResolvedSecrets::from_state(state, litellm_http::Client::plain_for_test())
ResolvedSecrets::from_state(state, litellm_http::Client::for_test())
.resolve(&[name])
.await
.unwrap()
@ -182,7 +182,7 @@ mod tests {
.await;
let source = ResolvedSecrets::from_state(
state(&server, KeyManagementSettings::default()),
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
);
let snapshot = source.resolve(&[declared]).await.unwrap();
assert_eq!(snapshot.get(undeclared), None);
@ -248,7 +248,7 @@ mod tests {
async fn oidc_failures_are_not_converted_to_missing_secrets() {
let result = ResolvedSecrets::from_state(
Arc::new(SecretManagerState::default()),
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
)
.resolve(&["oidc/"])
.await;

View file

@ -11,7 +11,7 @@ use wiremock::{
fn manager(server: &MockServer) -> AzureKeyVault {
AzureKeyVault::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
Arc::new(|name: &str| (name == "AZURE_AD_TOKEN").then(|| "fake".to_owned())),
)
@ -131,7 +131,7 @@ fn new_validates_vault_environment(
#[case] missing_environment: bool,
) {
let result = AzureKeyVault::new(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
Arc::new(move |name: &str| {
(name == "AZURE_KEY_VAULT_URI")
.then(|| uri.map(str::to_owned))
@ -158,7 +158,7 @@ fn new_validates_vault_environment(
#[case::local("http://localhost:8080", "https://localhost/.default")]
fn derives_scope_from_vault_host(#[case] uri: &str, #[case] expected: &str) {
let manager = AzureKeyVault::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
uri.parse().unwrap(),
Arc::new(|_: &str| None),
)
@ -187,7 +187,7 @@ async fn missing_credentials_do_not_request_vault() {
fn manager_without_credentials(server: &MockServer) -> AzureKeyVault {
AzureKeyVault::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
Arc::new(|name: &str| {
(name == "AZURE_CREDENTIAL").then(|| "ClientSecretCredential".to_owned())

View file

@ -10,7 +10,7 @@ use rstest::rstest;
#[ignore]
async fn reads_a_real_secret() {
let environment = Arc::new(ProcessEnvironment);
let manager = AzureKeyVault::new(litellm_http::Client::plain_for_test(), environment).unwrap();
let manager = AzureKeyVault::new(litellm_http::Client::for_test(), environment).unwrap();
let name = std::env::var("AZURE_KEY_VAULT_LIVE_SECRET_NAME").unwrap();
let secret = manager.get_secret(&name).await.unwrap().unwrap();
assert!(matches!(&secret, Secret::String(_)));

View file

@ -77,7 +77,7 @@ async fn authentication_encodes_login(#[case] username: &str, #[case] expected_p
.mount(&server)
.await;
let manager = CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
"acct".into(),
username.into(),
@ -433,7 +433,7 @@ async fn trailing_slash_endpoint_preserves_base_path() {
.await;
let endpoint = format!("{}/prefix/", server.uri()).parse().unwrap();
let manager = CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
endpoint,
"acct".into(),
"admin".into(),

View file

@ -62,7 +62,7 @@ pub(super) fn from_environment(
pub(super) fn manager(server: &MockServer, ttl: Duration) -> CyberArkSecretManager {
CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
"acct".into(),
"admin".into(),

View file

@ -233,7 +233,7 @@ async fn writes_match_python_parity_fixture(parity_fixture: ParityFixture) {
.mount(&server)
.await;
let manager = CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
parity_fixture.account,
parity_fixture.username,
@ -297,7 +297,7 @@ async fn live_conjur_round_trip() {
.as_nanos()
);
let manager = CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
endpoint.clone(),
account.clone(),
username.clone(),
@ -312,7 +312,7 @@ async fn live_conjur_round_trip() {
.await
.unwrap();
let verifier = CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
endpoint.clone(),
account.clone(),
username.clone(),

View file

@ -11,7 +11,7 @@ use wiremock::{
fn manager(server: &MockServer, always_read: bool, ttl: Duration) -> GoogleSecretManager {
GoogleSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
"project".into(),
Arc::new(|name: &str| (name == "VERTEX_AI_API_KEY").then(|| "token".into())),
@ -215,7 +215,7 @@ async fn always_read_and_expired_cache_fetch_again(
fn google_manager_requires_host_license_and_project_configuration() {
assert!(matches!(
GoogleSecretManager::new(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
Arc::new(|_: &str| None),
false
),
@ -223,7 +223,7 @@ fn google_manager_requires_host_license_and_project_configuration() {
));
assert!(matches!(
GoogleSecretManager::new(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
Arc::new(|_: &str| None),
true
),
@ -244,7 +244,7 @@ fn google_manager_rejects_invalid_refresh_intervals(#[case] variable: &'static s
});
assert!(matches!(
GoogleSecretManager::new(litellm_http::Client::plain_for_test(), environment, true),
GoogleSecretManager::new(litellm_http::Client::for_test(), environment, true),
Err(Error::RefreshInterval)
));
}

View file

@ -53,7 +53,7 @@ async fn read_results_follow_the_selected_failure_policy(
},
)),
Arc::new(move |_: &str| environment.map(str::to_owned)),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
)
.with_failure_policy(policy);
let result = resolver
@ -97,7 +97,7 @@ async fn primary_secret_values_other_than_strings_resolve_to_none(
let resolver = SecretResolver::new_python_compatible(
Arc::new(state(&server, settings)),
Arc::new(|_: &str| Some("fallback".into())),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
let text = value.as_str();
assert_eq!(
@ -157,7 +157,7 @@ async fn gating_prediction_matches_actual_lookup(
let resolver = SecretResolver::new_python_compatible(
Arc::new(state),
Arc::new(|_: &str| Some("environment".into())),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
assert_eq!(
resolver

View file

@ -22,7 +22,7 @@ async fn azure_handler_reads_missing_and_failed_secrets() {
.await;
let manager = SecretManager::AzureKeyVault(
AzureKeyVault::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
std::sync::Arc::new(|name: &str| (name == "AZURE_AD_TOKEN").then(|| "fake".to_owned())),
)
@ -81,7 +81,7 @@ async fn successful_azure_responses_do_not_fall_back_when_the_value_is_empty_or_
.mount(&server)
.await;
let manager = AzureKeyVault::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
Arc::new(|name: &str| (name == "AZURE_AD_TOKEN").then(|| "token".into())),
)
@ -92,7 +92,7 @@ async fn successful_azure_responses_do_not_fall_back_when_the_value_is_empty_or_
Default::default(),
)),
Arc::new(|_: &str| Some("environment".into())),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
assert_eq!(
resolver

View file

@ -59,7 +59,7 @@ fn manager(provider: Provider, server: &MockServer) -> SecretManager {
),
Provider::Azure => SecretManager::AzureKeyVault(
AzureKeyVault::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
environment,
)
@ -67,7 +67,7 @@ fn manager(provider: Provider, server: &MockServer) -> SecretManager {
),
Provider::Google => SecretManager::GoogleSecretManager(
GoogleSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
"project".into(),
environment,
@ -84,7 +84,7 @@ fn manager(provider: Provider, server: &MockServer) -> SecretManager {
.unwrap(),
),
Provider::Cyberark => SecretManager::Cyberark(CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
"acct".into(),
"admin".into(),
@ -240,7 +240,7 @@ async fn python_read_failures_preserve_provider_fallback_rules(
KeyManagementSettings::default(),
)),
Arc::new(move |_: &str| environment_value.map(str::to_owned)),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
let expected = if matches!(provider, Provider::Aws) {
None

View file

@ -24,7 +24,7 @@ async fn cyberark_handler_reads_values_and_surfaces_errors() {
.mount(&server)
.await;
let manager = SecretManager::Cyberark(CyberArkSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
"acct".into(),
"admin".into(),

View file

@ -25,7 +25,7 @@ async fn google_resolver_distinguishes_absence_from_failure(#[case] status: u16)
_ => None,
});
let manager = GoogleSecretManager::with_client(
litellm_http::Client::plain_for_test(),
litellm_http::Client::for_test(),
server.uri().parse().unwrap(),
"project".into(),
environment.clone(),
@ -40,7 +40,7 @@ async fn google_resolver_distinguishes_absence_from_failure(#[case] status: u16)
let resolver = SecretResolver::new_python_compatible(
Arc::new(state),
environment,
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
)
.with_failure_policy(FailurePolicy::Propagate);
let result = resolver.get_secret_str("KEY", None).await;

View file

@ -56,7 +56,7 @@ async fn hashicorp_handler_resolves_found_missing_and_failed_values() {
},
)),
Arc::new(|_: &str| None),
litellm_secrets::OidcResolver::new(litellm_http::Client::plain_for_test()),
litellm_secrets::OidcResolver::new(litellm_http::Client::for_test()),
);
assert_eq!(
found_resolver
@ -131,7 +131,7 @@ async fn hashicorp_handler_resolves_found_missing_and_failed_values() {
let failed_resolver = SecretResolver::new_python_compatible(
Arc::new(failed_state),
Arc::new(|_: &str| None),
litellm_secrets::OidcResolver::new(litellm_http::Client::plain_for_test()),
litellm_secrets::OidcResolver::new(litellm_http::Client::for_test()),
)
.with_failure_policy(FailurePolicy::Propagate);
assert!(matches!(

View file

@ -30,7 +30,7 @@ async fn environment_sources_resolve_expected_value(
("CIRCLE_OIDC_TOKEN_V2", "circle-v2"),
]);
assert_eq!(
OidcResolver::new(litellm_http::Client::plain_for_test())
OidcResolver::new(litellm_http::Client::for_test())
.resolve(reference, env.as_ref())
.await
.unwrap()
@ -43,7 +43,7 @@ async fn environment_sources_resolve_expected_value(
#[tokio::test]
async fn environment_sources_bypass_boolean_conversion_and_defaults() {
let env = environment(&[("TOKEN", "true")]);
let oidc = OidcResolver::new(litellm_http::Client::plain_for_test());
let oidc = OidcResolver::new(litellm_http::Client::for_test());
let resolver = SecretResolver::new(Arc::new(SecretManagerState::default()), env, oidc);
assert_eq!(
resolver
@ -94,7 +94,7 @@ async fn github_requests_are_authenticated_cached_and_revalidate_environment() {
),
("ACTIONS_ID_TOKEN_REQUEST_TOKEN", "request-token"),
]);
let oidc = OidcResolver::new(litellm_http::Client::plain_for_test());
let oidc = OidcResolver::new(litellm_http::Client::for_test());
for _ in 0..2 {
assert_eq!(
oidc.resolve("oidc/github/https://service/oidc/path", env.as_ref())
@ -131,7 +131,7 @@ async fn file_allowlist_resolves_symlinks_while_environment_paths_remain_explici
("PATH_TOKEN", private.to_str().unwrap()),
("AZURE_FEDERATED_TOKEN_FILE", token.to_str().unwrap()),
]);
let oidc = OidcResolver::new(litellm_http::Client::plain_for_test());
let oidc = OidcResolver::new(litellm_http::Client::for_test());
assert_eq!(
oidc.resolve(&format!("oidc/file/{}", token.display()), env.as_ref())
.await
@ -213,7 +213,7 @@ async fn google_expiry_caps_cache_and_preserves_audience(
.expect(calls)
.mount(&server)
.await;
let oidc = OidcResolver::new(litellm_http::Client::plain_for_test())
let oidc = OidcResolver::new(litellm_http::Client::for_test())
.with_google_identity_endpoint(server.uri().parse().unwrap())
.with_clock(now);
for _ in 0..2 {
@ -235,7 +235,7 @@ async fn google_expiry_caps_cache_and_preserves_audience(
#[tokio::test]
async fn google_oidc_requires_its_build_feature() {
assert!(matches!(
OidcResolver::new(litellm_http::Client::plain_for_test())
OidcResolver::new(litellm_http::Client::for_test())
.resolve("oidc/google/audience", environment(&[]).as_ref())
.await,
Err(Error::UnsupportedOidc)
@ -246,7 +246,7 @@ async fn google_oidc_requires_its_build_feature() {
#[tokio::test]
async fn azure_oidc_without_a_token_file_requires_its_build_feature() {
assert!(matches!(
OidcResolver::new(litellm_http::Client::plain_for_test())
OidcResolver::new(litellm_http::Client::for_test())
.resolve("oidc/azure/scope", environment(&[]).as_ref())
.await,
Err(Error::UnsupportedOidc)
@ -262,7 +262,7 @@ async fn invalid_references_fail_before_environment_lookup(
#[case] reference: &str,
#[case] unsupported: bool,
) {
let error = OidcResolver::new(litellm_http::Client::plain_for_test())
let error = OidcResolver::new(litellm_http::Client::for_test())
.resolve(reference, &|_: &str| {
panic!("invalid reference reached environment lookup")
})
@ -284,7 +284,7 @@ async fn unreadable_expiry_keeps_python_cache_fallback(#[case] token: &str) {
.expect(1)
.mount(&server)
.await;
let resolver = OidcResolver::new(litellm_http::Client::plain_for_test())
let resolver = OidcResolver::new(litellm_http::Client::for_test())
.with_google_identity_endpoint(server.uri().parse().unwrap());
for _ in 0..2 {
assert_eq!(
@ -336,7 +336,7 @@ async fn azure_oidc_acquires_the_requested_scope_and_preserves_failures(#[case]
})
}
}
let oidc = OidcResolver::new(litellm_http::Client::plain_for_test())
let oidc = OidcResolver::new(litellm_http::Client::for_test())
.with_azure_token_provider(Arc::new(Provider(failed)));
let resolver = SecretResolver::new_python_compatible(
Arc::new(SecretManagerState::default()),
@ -364,7 +364,7 @@ async fn azure_oidc_acquires_the_requested_scope_and_preserves_failures(#[case]
#[tokio::test]
async fn missing_oidc_environment_is_an_error(#[case] reference: &str) {
assert!(matches!(
OidcResolver::new(litellm_http::Client::plain_for_test())
OidcResolver::new(litellm_http::Client::for_test())
.resolve(reference, environment(&[]).as_ref())
.await,
Err(Error::MissingEnvironment)
@ -383,7 +383,7 @@ async fn google_oidc_failures_are_not_cached_or_hidden_by_defaults() {
let resolver = SecretResolver::new_python_compatible(
Arc::new(SecretManagerState::default()),
environment(&[]),
OidcResolver::new(litellm_http::Client::plain_for_test())
OidcResolver::new(litellm_http::Client::for_test())
.with_google_identity_endpoint(server.uri().parse().unwrap()),
);
for _ in 0..2 {
@ -424,7 +424,7 @@ async fn google_tokens_expire_at_the_python_cache_deadline(
.expect(2)
.mount(&server)
.await;
let resolver = OidcResolver::new(litellm_http::Client::plain_for_test())
let resolver = OidcResolver::new(litellm_http::Client::for_test())
.with_google_identity_endpoint(server.uri().parse().unwrap())
.with_clock(|| UNIX_EPOCH + Duration::from_secs(1000));
assert_eq!(
@ -477,7 +477,7 @@ async fn google_cache_uses_payload_expiry_without_requiring_a_jwt_header() {
.expect(2)
.mount(&server)
.await;
let resolver = OidcResolver::new(litellm_http::Client::plain_for_test())
let resolver = OidcResolver::new(litellm_http::Client::for_test())
.with_google_identity_endpoint(server.uri().parse().unwrap())
.with_clock(|| UNIX_EPOCH + Duration::from_secs(1000));
for _ in 0..2 {

View file

@ -12,7 +12,7 @@ fn resolver(value: Option<&str>) -> SecretResolver {
SecretResolver::new_python_compatible(
Arc::new(SecretManagerState::default()),
Arc::new(move |_: &str| value.clone()),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
)
}
@ -35,7 +35,7 @@ async fn native_reads_preserve_strings_and_report_conversion_errors(#[case] mana
let resolver = SecretResolver::new(
Arc::new(state),
Arc::new(move |_: &str| Some(raw.to_owned())),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
assert_eq!(
resolver
@ -75,7 +75,7 @@ async fn native_defaults_apply_to_absence_but_never_hide_provider_failures() {
KeyManagementSettings::default(),
)),
Arc::new(|_: &str| None),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
let result = resolver
.get_secret_str("key", Some(SecretValue::new("default")))
@ -189,7 +189,7 @@ fn managed(reply: Result<Option<Secret>, ()>, environment: Option<&'static str>)
KeyManagementSettings::default(),
)),
Arc::new(move |_: &str| environment.map(str::to_owned)),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
)
.with_failure_policy(FailurePolicy::EnvironmentFallback)
}
@ -281,7 +281,7 @@ async fn prefix_is_removed_once_and_resolved_from_environment() {
let resolver = SecretResolver::new_python_compatible(
Arc::new(state),
Arc::new(|name: &str| (name == "os.environ/KEY").then(|| "value".into())),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
assert_eq!(
resolver
@ -340,7 +340,7 @@ async fn excluded_hosted_keys_keep_the_python_manager_conversion_path(
},
)),
Arc::new(move |_: &str| Some(raw.to_owned())),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
assert_eq!(
resolver.get_secret("KEY", None).await.unwrap(),
@ -373,7 +373,7 @@ async fn azure_callback_absence_preserves_none_but_errors_fall_back(
KeyManagementSettings::default(),
)),
Arc::new(|_: &str| Some("environment".into())),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
assert_eq!(
resolver

View file

@ -15,7 +15,7 @@ mod tests {
#[case] expected: Option<&str>,
) {
unsafe { std::env::set_var(name, value) };
let secret = EnvironmentSecrets::python_compatible(litellm_http::Client::plain_for_test())
let secret = EnvironmentSecrets::python_compatible(litellm_http::Client::for_test())
.resolve(&[name])
.await
.unwrap()
@ -42,7 +42,7 @@ async fn dynamic_names_use_the_same_resolver_and_snapshots_never_do_fresh_lookup
reads.fetch_add(1, Ordering::SeqCst);
(name != "missing").then(|| name.to_owned())
}),
OidcResolver::new(litellm_http::Client::plain_for_test()),
OidcResolver::new(litellm_http::Client::for_test()),
);
let snapshot = source.resolve(&["declared", "missing"]).await.unwrap();
let name = format!("runtime-{}", "key");

View file

@ -77,7 +77,7 @@ async fn database() -> Database {
7,
)
.unwrap(),
client: Client::no_redirect_for_test(),
client: Client::for_test(),
}
}

View file

@ -29,7 +29,7 @@ async fn spend_insert_controls_target_and_retry_settings() {
.mount(&server)
.await;
let config = Config::new("spend_test".into(), &format!("{}?query=DROP&async_insert=0&wait_for_async_insert=0&input_format_skip_unknown_fields=1&database=wrong&readonly=1", server.uri()), 7).unwrap();
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let row = BTreeMap::from([
("request_id".into(), json!("request")),
("start_time".into(), json!(1234)),
@ -77,7 +77,7 @@ async fn spend_insert_preserves_storage_failure(#[case] status: u16) {
.await;
let config = Config::new("spend_test".into(), &server.uri(), 7).unwrap();
let error = insert_rows(
&Client::no_redirect_for_test(),
&Client::for_test(),
config.storage().writer(),
"spend_test",
vec![BTreeMap::from([("request_id".into(), json!("request"))])],
@ -95,7 +95,7 @@ async fn empty_spend_insert_does_not_contact_storage() {
let server = MockServer::start().await;
let config = Config::new("spend_test".into(), &server.uri(), 7).unwrap();
insert_rows(
&Client::no_redirect_for_test(),
&Client::for_test(),
config.storage().writer(),
"spend_test",
vec![],
@ -116,7 +116,7 @@ async fn failed_schema_setup_is_reported() {
.await;
let config = Config::new("spend_test".into(), &server.uri(), 7).unwrap();
assert!(matches!(
ensure_schema(&Client::no_redirect_for_test(), &config).await,
ensure_schema(&Client::for_test(), &config).await,
Err(Error::Storage(
litellm_storage_clickhouse::Error::SchemaFailed(403)
))

View file

@ -48,7 +48,7 @@ async fn database() -> TestResult<ClickHouseDatabase> {
Ok(ClickHouseDatabase {
_container: container,
url,
client: Client::no_redirect_for_test(),
client: Client::for_test(),
})
}
@ -244,7 +244,7 @@ async fn failed_migration_is_not_recorded_and_retains_storage_error(
#[rstest]
fn invalid_database_identifier_is_rejected() {
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let connection = Connection::writer("http://127.0.0.1:1").expect("valid URL");
assert!(matches!(
ClickHouseMigrate::new(
@ -327,7 +327,7 @@ async fn duplicate_ledger_rows_are_tolerated(
#[rstest]
#[tokio::test]
async fn revert_reports_forward_only_error() {
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let connection = Connection::writer("http://127.0.0.1:1").expect("valid URL");
let migration = migration(
1,
@ -369,7 +369,7 @@ async fn applied_int64_versions_accept_numeric_and_quoted_json(
)
.mount(&mock_server)
.await;
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let connection = Connection::writer(&mock_server.uri()).expect("valid URL");
let migrator = migrator(vec![]);
let mut adapter = ClickHouseMigrate::new(
@ -406,7 +406,7 @@ async fn schema_requests_override_unsafe_connection_settings(
mock_server.uri()
);
execute_statement(
&Client::no_redirect_for_test(),
&Client::for_test(),
&Connection::writer(&url).expect("valid URL"),
"CREATE DATABASE IF NOT EXISTS trace_test",
REQUEST_TIMEOUT,
@ -445,7 +445,7 @@ async fn oversized_ledger_response_is_rejected_before_migrations(
)
.mount(&mock_server)
.await;
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let connection = Connection::writer(&mock_server.uri()).expect("valid URL");
let migrator = migrator(vec![]);
let mut adapter = ClickHouseMigrate::new(

View file

@ -13,7 +13,7 @@ async fn insert_rejects_invalid_identifiers(
#[case] table: &str,
#[case] invalid_database: bool,
) {
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let connection = Connection::writer("http://localhost:8123").expect("valid URL");
let result = insert_encoded_rows(&client, &connection, database, table, "token", "{}").await;
@ -24,7 +24,7 @@ async fn insert_rejects_invalid_identifiers(
#[rstest]
#[tokio::test]
async fn read_rejects_empty_sql() {
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let connection = Connection::reader("http://localhost:8123", "litellm").expect("valid URL");
assert!(matches!(
@ -87,7 +87,7 @@ async fn typed_fetch_encodes_parameters_and_validates_rows(
.expect(2)
.mount(&server)
.await;
let client = Client::no_redirect_for_test();
let client = Client::for_test();
let connection = Connection::parse(&server.uri()).unwrap();
let params = QueryParams {
signed: i64::MIN,
@ -131,7 +131,7 @@ async fn server_result_limits_allow_smaller_pages_without_retrying_other_failure
.await;
let connection = Connection::parse(&server.uri()).unwrap();
let error = execute_read(
&Client::no_redirect_for_test(),
&Client::for_test(),
&connection,
"SELECT 1",
&BTreeMap::new(),
@ -174,7 +174,7 @@ async fn insert_timeout_environment_child() {
.mount(&server)
.await;
let result = insert_encoded_rows(
&Client::no_redirect_for_test(),
&Client::for_test(),
&Connection::parse(&server.uri()).unwrap(),
"traces",
"otel_traces",