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::{Duration, sleep};
const FIXTURE_FILE: &str = "tests/code_example.rs";
const FIXTURE_DEFINITION_LINE: u32 = 151;
const FIXTURE_DEFINITION_COL: u32 = 5;
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(env!("CARGO_MANIFEST_DIR"));
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: "ExampleStruct".to_string(),
kind: "any".to_string(),
exact: true,
};
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 == "ExampleStruct") {
symbol_found = true;
break;
}
}
sleep(Duration::from_millis(500)).await;
}
assert!(
symbol_found,
"Expected at least one symbol for ExampleStruct"
);
let outline_req = OutlineQueryRequest {
file: PathBuf::from(FIXTURE_FILE),
};
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 code_example"
);
assert!(outline.iter().any(|item| item.name == "ExampleStruct"));
assert!(outline.iter().any(|item| item.name == "long_example"));
let def_req = DefinitionQueryRequest {
file: PathBuf::from(FIXTURE_FILE),
line: FIXTURE_DEFINITION_LINE,
col: FIXTURE_DEFINITION_COL,
};
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 definitions: Vec<DefinitionItem> = def_res.json().await.unwrap();
assert!(
!definitions.is_empty(),
"Expected definition for long_example"
);
assert!(
definitions
.iter()
.any(|item| item.line < FIXTURE_DEFINITION_LINE)
);
let body_req = BodyQueryRequest {
file: PathBuf::from(FIXTURE_FILE),
line: FIXTURE_DEFINITION_LINE,
col: FIXTURE_DEFINITION_COL,
max_lines: 2,
};
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 long_example"
);
assert_eq!(body_item.body.lines().count(), 2);
assert!(body_item.total_lines > 100);
assert!(body_item.is_truncated);
let default_body_res = client
.post(format!("{}/api/body", base_url))
.json(&serde_json::json!({
"file": FIXTURE_FILE,
"line": FIXTURE_DEFINITION_LINE,
"col": FIXTURE_DEFINITION_COL,
}))
.send()
.await
.expect("Failed to send default /api/body request");
assert!(default_body_res.status().is_success());
let default_body: BodyItem = default_body_res.json().await.unwrap();
assert_eq!(default_body.body.lines().count(), 100);
assert!(default_body.is_truncated);
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);
}