use reqwest::Client;
use rust_analyzer_cli::daemon::server::start_daemon_server;
use rust_analyzer_cli::lsp::types::*;
use std::net::TcpListener;
use std::path::PathBuf;
use tokio::time::{sleep, Duration};
fn get_free_port() -> u16 {
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind free port");
let port = listener.local_addr().unwrap().port();
drop(listener);
port
}
#[tokio::test]
async fn test_daemon_full_workflow() {
let port = get_free_port();
let workspace = PathBuf::from(".");
let daemon_workspace = workspace.clone();
tokio::spawn(async move {
if let Err(e) = start_daemon_server(daemon_workspace, port).await {
eprintln!("Daemon server error: {}", e);
}
});
sleep(Duration::from_millis(500)).await;
let client = Client::new();
let base_url = format!("http://127.0.0.1:{}", port);
let status_res = client
.get(format!("{}/status", base_url))
.send()
.await
.expect("Failed to send /status request");
assert!(status_res.status().is_success());
let status: DaemonStatusResponse = status_res.json().await.unwrap();
let expected_root = workspace.canonicalize().unwrap().to_string_lossy().to_string();
assert_eq!(
status.workspace_root.trim_start_matches(r"\\?\"),
expected_root.trim_start_matches(r"\\?\")
);
let mut symbol_found = false;
for _ in 0..20 {
let symbol_req = SymbolQueryRequest {
name: "LspClient".to_string(),
kind: "any".to_string(),
exact: false,
};
let symbol_res = client
.post(format!("{}/api/symbol", base_url))
.json(&symbol_req)
.send()
.await
.expect("Failed to send /api/symbol request");
if symbol_res.status().is_success() {
let symbols: Vec<SymbolItem> = symbol_res.json().await.unwrap();
if symbols.iter().any(|s| s.name == "LspClient") {
symbol_found = true;
break;
}
}
sleep(Duration::from_millis(500)).await;
}
assert!(symbol_found, "Expected at least one symbol for LspClient");
let outline_req = OutlineQueryRequest {
file: PathBuf::from("src/main.rs"),
};
let outline_res = client
.post(format!("{}/api/outline", base_url))
.json(&outline_req)
.send()
.await
.expect("Failed to send /api/outline request");
assert!(outline_res.status().is_success());
let outline: Vec<OutlineItem> = outline_res.json().await.unwrap();
assert!(!outline.is_empty(), "Expected outline items for src/main.rs");
assert!(outline.iter().any(|item| item.name == "main"));
let def_req = DefinitionQueryRequest {
file: PathBuf::from("src/main.rs"),
line: 15,
col: 10,
};
let def_res = client
.post(format!("{}/api/definition", base_url))
.json(&def_req)
.send()
.await
.expect("Failed to send /api/definition request");
assert!(def_res.status().is_success());
let body_req = DefinitionQueryRequest {
file: PathBuf::from("src/main.rs"),
line: 15,
col: 10,
};
let body_res = client
.post(format!("{}/api/body", base_url))
.json(&body_req)
.send()
.await
.expect("Failed to send /api/body request");
assert!(body_res.status().is_success());
let body_item: BodyItem = body_res.json().await.unwrap();
assert!(!body_item.body.is_empty(), "Expected non-empty body for main function");
let check_req = CheckQueryRequest { target: None };
let check_res = client
.post(format!("{}/api/check", base_url))
.json(&check_req)
.send()
.await
.expect("Failed to send /api/check request");
assert!(check_res.status().is_success());
let check: CheckResponse = check_res.json().await.unwrap();
assert!(check.success);
}