Fix loopback test networking.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::IpAddr;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -427,7 +428,7 @@ pub fn load_endpoint_api_key(kind: AiEndpointKind) -> EngineResult<Option<String
|
||||
|
||||
pub fn refresh_model_catalog(endpoint: &AiEndpointConfig) -> EngineResult<Vec<AiModelInfo>> {
|
||||
validate_endpoint_access(endpoint)?;
|
||||
let client = build_http_client()?;
|
||||
let client = build_http_client_for_endpoint(&endpoint.url)?;
|
||||
let request = client.get(models_url(&endpoint.url));
|
||||
let response = with_auth(request, endpoint).send()?.error_for_status()?;
|
||||
let body: Value = response.json()?;
|
||||
@@ -494,7 +495,7 @@ pub fn test_chat(endpoint: &AiEndpointConfig, model: &str) -> EngineResult<()> {
|
||||
"stream": false,
|
||||
});
|
||||
with_auth(
|
||||
build_http_client()?
|
||||
build_http_client_for_endpoint(&endpoint.url)?
|
||||
.post(chat_completions_url(&endpoint.url))
|
||||
.json(&payload),
|
||||
endpoint,
|
||||
@@ -535,7 +536,7 @@ pub fn run_one_shot(
|
||||
}
|
||||
}
|
||||
});
|
||||
let client = build_http_client()?;
|
||||
let client = build_http_client_for_endpoint(&endpoint.url)?;
|
||||
let response = with_auth(
|
||||
client
|
||||
.post(chat_completions_url(&endpoint.url))
|
||||
@@ -575,8 +576,28 @@ fn parse_token_usage(body: &Value) -> TokenUsage {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_http_client() -> EngineResult<Client> {
|
||||
Ok(Client::builder().timeout(Duration::from_secs(5)).build()?)
|
||||
fn build_http_client_for_endpoint(endpoint_url: &str) -> EngineResult<Client> {
|
||||
let builder = Client::builder().timeout(Duration::from_secs(5));
|
||||
let builder = if should_bypass_proxy(endpoint_url) {
|
||||
builder.no_proxy()
|
||||
} else {
|
||||
builder
|
||||
};
|
||||
Ok(builder.build()?)
|
||||
}
|
||||
|
||||
fn should_bypass_proxy(endpoint_url: &str) -> bool {
|
||||
let Ok(url) = reqwest::Url::parse(endpoint_url) else {
|
||||
return false;
|
||||
};
|
||||
let Some(host) = url.host_str() else {
|
||||
return false;
|
||||
};
|
||||
host.eq_ignore_ascii_case("localhost")
|
||||
|| host
|
||||
.parse::<IpAddr>()
|
||||
.map(|address| address.is_loopback())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn load_endpoint(conn: &Connection, kind: AiEndpointKind) -> EngineResult<StoredAiEndpointConfig> {
|
||||
@@ -936,7 +957,7 @@ fn store_endpoint_api_key(kind: AiEndpointKind, api_key: &str) -> EngineResult<(
|
||||
TEST_API_KEYS
|
||||
.lock()
|
||||
.map_err(|error| EngineError::Validation(error.to_string()))?
|
||||
.insert(kind.as_str().to_string(), api_key.to_string());
|
||||
.insert(test_api_key_name(kind), api_key.to_string());
|
||||
return Ok(());
|
||||
}
|
||||
endpoint_keyring_entry(kind)?
|
||||
@@ -950,7 +971,7 @@ fn read_endpoint_api_key(kind: AiEndpointKind) -> EngineResult<Option<String>> {
|
||||
.lock()
|
||||
.map_err(|error| EngineError::Validation(error.to_string()))
|
||||
.map(|keys| {
|
||||
keys.get(kind.as_str())
|
||||
keys.get(&test_api_key_name(kind))
|
||||
.cloned()
|
||||
.filter(|key| !key.trim().is_empty())
|
||||
});
|
||||
@@ -968,7 +989,7 @@ fn delete_endpoint_api_key(kind: AiEndpointKind) -> EngineResult<()> {
|
||||
TEST_API_KEYS
|
||||
.lock()
|
||||
.map_err(|error| EngineError::Validation(error.to_string()))?
|
||||
.remove(kind.as_str());
|
||||
.remove(&test_api_key_name(kind));
|
||||
return Ok(());
|
||||
}
|
||||
match endpoint_keyring_entry(kind)?.delete_credential() {
|
||||
@@ -989,6 +1010,10 @@ fn cargo_test_process() -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
fn test_api_key_name(kind: AiEndpointKind) -> String {
|
||||
format!("{:?}:{}", std::thread::current().id(), kind.as_str())
|
||||
}
|
||||
|
||||
fn keyring_error(error: keyring::Error) -> EngineError {
|
||||
EngineError::Validation(error.to_string())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::io::Read;
|
||||
use std::net::IpAddr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, OnceLock, mpsc};
|
||||
use std::time::Duration;
|
||||
@@ -836,16 +837,39 @@ pub fn build_context(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn chat_http_client(endpoint_url: &str) -> EngineResult<Client> {
|
||||
let builder = Client::builder()
|
||||
.connect_timeout(Duration::from_secs(5))
|
||||
.timeout(Duration::from_secs(300));
|
||||
let builder = if should_bypass_proxy(endpoint_url) {
|
||||
builder.no_proxy()
|
||||
} else {
|
||||
builder
|
||||
};
|
||||
Ok(builder.build()?)
|
||||
}
|
||||
|
||||
fn should_bypass_proxy(endpoint_url: &str) -> bool {
|
||||
let Ok(url) = reqwest::Url::parse(endpoint_url) else {
|
||||
return false;
|
||||
};
|
||||
let Some(host) = url.host_str() else {
|
||||
return false;
|
||||
};
|
||||
host.eq_ignore_ascii_case("localhost")
|
||||
|| host
|
||||
.parse::<IpAddr>()
|
||||
.map(|address| address.is_loopback())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn request_completion(
|
||||
endpoint: &AiEndpointConfig,
|
||||
payload: &Value,
|
||||
cancelled: &AtomicBool,
|
||||
mut on_content: impl FnMut(&str),
|
||||
) -> EngineResult<AssembledResponse> {
|
||||
let client = Client::builder()
|
||||
.connect_timeout(Duration::from_secs(5))
|
||||
.timeout(Duration::from_secs(300))
|
||||
.build()?;
|
||||
let client = chat_http_client(&endpoint.url)?;
|
||||
let mut request = client
|
||||
.post(chat_completions_url(&endpoint.url))
|
||||
.json(payload);
|
||||
|
||||
@@ -664,6 +664,13 @@ mod tests {
|
||||
GUARD.get_or_init(|| Mutex::new(()))
|
||||
}
|
||||
|
||||
fn preview_client() -> reqwest::blocking::Client {
|
||||
reqwest::blocking::Client::builder()
|
||||
.no_proxy()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn setup_preview_fixture() -> (tempfile::TempDir, Database) {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join("meta")).unwrap();
|
||||
@@ -1112,7 +1119,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let client = preview_client();
|
||||
let mut body = None;
|
||||
for _ in 0..20 {
|
||||
if let Ok(response) = client
|
||||
@@ -1229,7 +1236,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let client = preview_client();
|
||||
let response = client
|
||||
.get(format!(
|
||||
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/media/../outside.txt"
|
||||
@@ -1269,7 +1276,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let client = preview_client();
|
||||
let media = client
|
||||
.get(format!("http://{PREVIEW_HOST}:{PREVIEW_PORT}/media/ok.txt"))
|
||||
.send()
|
||||
@@ -1362,7 +1369,7 @@ mod tests {
|
||||
"project-1".into(),
|
||||
)
|
||||
.unwrap();
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let client = preview_client();
|
||||
let preview_html = client
|
||||
.get(format!(
|
||||
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/__draft/post-1"
|
||||
@@ -1427,7 +1434,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let client = preview_client();
|
||||
let response = client
|
||||
.get(format!(
|
||||
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/__style-preview?theme=amber&mode=dark"
|
||||
@@ -1487,7 +1494,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let client = preview_client();
|
||||
let response = client
|
||||
.get(format!(
|
||||
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/?theme=amber&mode=light"
|
||||
|
||||
@@ -671,7 +671,10 @@ fn protocol_routes_every_family_and_http_enforces_loopback_origin_and_cors() {
|
||||
);
|
||||
|
||||
let server = mcp::McpHttpServer::start(fixture.database_path.clone(), 0).unwrap();
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.no_proxy()
|
||||
.build()
|
||||
.unwrap();
|
||||
let response = client
|
||||
.post(server.endpoint())
|
||||
.header("accept", "application/json, text/event-stream")
|
||||
|
||||
@@ -123,8 +123,11 @@ mod tests {
|
||||
#[test]
|
||||
fn desktop_handler_queues_supported_menu_actions() {
|
||||
let queued = Arc::new(Mutex::new(Vec::new()));
|
||||
handler(Arc::clone(&queued), String::new())("trigger_menu_action", &[serde_json::json!("new_post")])
|
||||
.unwrap();
|
||||
handler(Arc::clone(&queued), String::new())(
|
||||
"trigger_menu_action",
|
||||
&[serde_json::json!("new_post")],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(queued.lock().unwrap().as_slice(), &[MenuAction::NewPost]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user