#![allow(dead_code)]
#![allow(clippy::await_holding_lock)]
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
use lingshu_tools::tools::web::search::registry::{reset_registry_for_tests, test_registry_lock};
use lingshu_tools::{AppConfigRef, ToolContext};
use lingshu_types::Platform;
use tokio_util::sync::CancellationToken;
pub const DEFAULT_SEARXNG_DOCKER_URL: &str = "http://127.0.0.1:8888";
pub fn searxng_json_api_ready(base_url: &str) -> bool {
searxng_result_count(base_url, "rust").unwrap_or(0) > 0
}
pub fn searxng_result_count(base_url: &str, query: &str) -> Option<usize> {
let url = format!(
"{}/search?q={}&format=json",
base_url.trim_end_matches('/'),
query.replace(' ', "+")
);
let output = Command::new("curl")
.args(["-sf", "--max-time", "10", &url])
.output()
.ok()?;
if !output.status.success() {
return None;
}
let body = String::from_utf8_lossy(&output.stdout);
let parsed: serde_json::Value = serde_json::from_str(&body).ok()?;
Some(parsed.get("results")?.as_array()?.len())
}
pub fn registry_guard() -> std::sync::MutexGuard<'static, ()> {
let lock = test_registry_lock();
reset_registry_for_tests();
lock
}
pub fn test_ctx() -> ToolContext {
ToolContext {
task_id: "web-search-e2e".into(),
cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
session_id: "web-search-e2e-session".into(),
user_task: Some("web search e2e".into()),
cancel: CancellationToken::new(),
config: AppConfigRef::default(),
state_db: None,
platform: Platform::Cli,
process_table: None,
provider: None,
tool_registry: None,
delegate_depth: 0,
delegate_agent_id: None,
delegate_parent_id: None,
sub_agent_runner: None,
delegation_event_tx: None,
clarify_tx: None,
approval_tx: None,
on_skills_changed: None,
gateway_sender: None,
origin_chat: None,
session_key: None,
todo_store: None,
current_tool_call_id: None,
current_tool_name: None,
injected_messages: None,
tool_progress_tx: None,
watch_notification_tx: None,
mutation_turn: None,
lsp_gate: None,
kanban_task_id: None,
}
}
pub fn ctx_with_config(cfg: AppConfigRef) -> ToolContext {
let mut ctx = test_ctx();
ctx.config = cfg;
ctx
}
pub struct EdgecrabHomeGuard {
previous: Option<String>,
}
impl EdgecrabHomeGuard {
pub fn set(path: &std::path::Path) -> Self {
let previous = std::env::var("EDGECRAB_HOME").ok();
unsafe { std::env::set_var("EDGECRAB_HOME", path) };
Self { previous }
}
}
impl Drop for EdgecrabHomeGuard {
fn drop(&mut self) {
unsafe { std::env::remove_var("EDGECRAB_HOME") };
if let Some(value) = &self.previous {
unsafe { std::env::set_var("EDGECRAB_HOME", value) };
}
}
}
pub fn lingshu_home_guard(path: &std::path::Path) -> EdgecrabHomeGuard {
EdgecrabHomeGuard::set(path)
}
pub fn register_mock(
name: &'static str,
mode: lingshu_tools::tools::web::search::backends::mock::MockMode,
) {
use lingshu_tools::register_web_search_backend;
use lingshu_tools::tools::web::search::backends::mock::MockBackend;
register_web_search_backend(Arc::new(MockBackend::new(name, mode)));
}
pub fn register_ddgs_mock_success() {
use lingshu_tools::tools::web::search::backend::SearchResult;
use lingshu_tools::tools::web::search::backends::mock::MockMode;
register_mock(
"ddgs",
MockMode::Success(vec![SearchResult::new(
1,
"Mock DDGS Hit",
"https://example.com/ddgs",
"mock snippet",
"ddgs",
)]),
);
}
pub fn register_ddgs_mock_fail() {
use lingshu_tools::tools::web::search::backends::mock::MockMode;
register_mock("ddgs", MockMode::Network);
}
pub fn many_mock_results(
n: usize,
source: &str,
) -> Vec<lingshu_tools::tools::web::search::backend::SearchResult> {
use lingshu_tools::tools::web::search::backend::SearchResult;
(1..=n)
.map(|i| {
SearchResult::new(
i,
format!("Hit {i}"),
format!("https://example.com/{i}"),
format!("snippet {i}"),
source,
)
})
.collect()
}
pub fn apply_searxng_docker_env(url: &str) {
unsafe {
std::env::set_var("SEARXNG_URL", url);
std::env::set_var("EDGECRAB_E2E_SSRF_ALLOW_LOCALHOST", "1");
}
}
pub fn searxng_docker_url_if_ready() -> Option<String> {
let url = std::env::var("SEARXNG_URL")
.ok()
.filter(|v| !v.trim().is_empty())
.unwrap_or_else(|| DEFAULT_SEARXNG_DOCKER_URL.to_string());
if searxng_json_api_ready(&url) {
Some(url)
} else {
None
}
}