#[cfg(test)]
#[allow(clippy::module_inception)]
mod tests {
use std::collections::HashMap;
use std::sync::Arc;
use crate::lsp::protocol::{DocumentSymbol, LspPosition, LspRange, SymbolKind};
use crate::repo_map::parser;
use super::super::{
DiagnosticsCache,
convert::{path_to_uri, to_repo_symbols, uri_to_path},
server_config::{
extension_to_language_id, find_server_for_language, is_binary_available, known_servers,
},
};
#[test]
fn test_path_to_uri() {
assert_eq!(
path_to_uri("/home/user/project"),
"file:///home/user/project"
);
assert_eq!(path_to_uri("/tmp/a.rs"), "file:///tmp/a.rs");
assert_eq!(path_to_uri("C:/Users/me"), "file:///C:/Users/me");
}
#[test]
fn test_uri_to_path() {
assert_eq!(
uri_to_path("file:///home/user/project"),
"/home/user/project"
);
assert_eq!(uri_to_path("file:///tmp/a.rs"), "/tmp/a.rs");
assert_eq!(uri_to_path("file:///C:/Users/me"), "C:/Users/me");
assert_eq!(uri_to_path("https://example.com"), "https://example.com");
}
#[test]
fn test_known_servers_not_empty() {
let servers = known_servers();
assert!(
servers.len() >= 48,
"Expected at least 48 known servers, got {}",
servers.len()
);
let commands: Vec<&str> = servers.iter().map(|s| s.command.as_str()).collect();
assert!(commands.contains(&"rust-analyzer"));
assert!(commands.contains(&"gopls"));
assert!(commands.contains(&"clangd"));
assert!(commands.contains(&"jdtls"));
assert!(commands.contains(&"solargraph"));
for s in &servers {
assert!(!s.language_id.is_empty());
assert!(!s.command.is_empty());
}
}
#[test]
fn test_extension_to_language_id() {
assert_eq!(extension_to_language_id("rs"), Some("rust"));
assert_eq!(extension_to_language_id("py"), Some("python"));
assert_eq!(extension_to_language_id("ts"), Some("typescript"));
assert_eq!(extension_to_language_id("tsx"), Some("typescript"));
assert_eq!(extension_to_language_id("js"), Some("javascript"));
assert_eq!(extension_to_language_id("go"), Some("go"));
assert_eq!(extension_to_language_id("java"), Some("java"));
assert_eq!(extension_to_language_id("c"), Some("c"));
assert_eq!(extension_to_language_id("cpp"), Some("cpp"));
assert_eq!(extension_to_language_id("cs"), Some("csharp"));
assert_eq!(extension_to_language_id("rb"), Some("ruby"));
assert_eq!(extension_to_language_id("php"), Some("php"));
assert_eq!(extension_to_language_id("swift"), Some("swift"));
assert_eq!(extension_to_language_id("kt"), Some("kotlin"));
assert_eq!(extension_to_language_id("lua"), Some("lua"));
assert_eq!(extension_to_language_id("zig"), Some("zig"));
assert_eq!(extension_to_language_id("ex"), Some("elixir"));
assert_eq!(extension_to_language_id("dart"), Some("dart"));
assert_eq!(extension_to_language_id("sh"), Some("shellscript"));
assert_eq!(extension_to_language_id("vue"), Some("vue"));
assert_eq!(extension_to_language_id("svelte"), Some("svelte"));
assert_eq!(extension_to_language_id("html"), Some("html"));
assert_eq!(extension_to_language_id("css"), Some("css"));
assert_eq!(extension_to_language_id("scss"), Some("scss"));
assert_eq!(extension_to_language_id("json"), Some("json"));
assert_eq!(extension_to_language_id("toml"), Some("toml"));
assert_eq!(extension_to_language_id("hs"), Some("haskell"));
assert_eq!(extension_to_language_id("ml"), Some("ocaml"));
assert_eq!(extension_to_language_id("fs"), Some("fsharp"));
assert_eq!(extension_to_language_id("clj"), Some("clojure"));
assert_eq!(extension_to_language_id("erl"), Some("erlang"));
assert_eq!(extension_to_language_id("nim"), Some("nim"));
assert_eq!(extension_to_language_id("jl"), Some("julia"));
assert_eq!(extension_to_language_id("tf"), Some("terraform"));
assert_eq!(extension_to_language_id("nix"), Some("nix"));
assert_eq!(extension_to_language_id("graphql"), Some("graphql"));
assert_eq!(extension_to_language_id("gql"), Some("graphql"));
assert_eq!(extension_to_language_id("unknown"), None);
}
#[test]
fn test_find_server_for_language_unknown_returns_none() {
assert!(find_server_for_language("brainfuck").is_none());
}
#[test]
fn test_find_server_for_language_returns_available_binary() {
if is_binary_available("rust-analyzer") {
let rust = find_server_for_language("rust").unwrap();
assert_eq!(rust.command, "rust-analyzer");
}
if is_binary_available("gopls") {
let go = find_server_for_language("go").unwrap();
assert_eq!(go.command, "gopls");
}
}
#[test]
fn test_find_server_skips_unavailable_binary() {
assert!(find_server_for_language("brainfuck").is_none());
}
#[test]
fn test_language_normalization_in_known_servers() {
let servers = known_servers();
let langs: Vec<&str> = servers.iter().map(|s| s.language_id.as_str()).collect();
assert!(langs.contains(&"c"), "clangd should be listed under 'c'");
assert!(
langs.contains(&"typescript"),
"ts server should be listed under 'typescript'"
);
assert!(
langs.contains(&"css"),
"css server should be listed under 'css'"
);
assert!(
langs.contains(&"terraform"),
"terraform-ls should be listed under 'terraform'"
);
}
#[test]
fn test_is_binary_available() {
assert!(is_binary_available("ls"));
assert!(!is_binary_available("nonexistent-binary-xyzzy-12345"));
}
#[test]
fn test_to_repo_symbols_conversion() {
let range = LspRange {
start: LspPosition {
line: 4,
character: 0,
},
end: LspPosition {
line: 10,
character: 1,
},
};
let sel = LspRange {
start: LspPosition {
line: 4,
character: 3,
},
end: LspPosition {
line: 4,
character: 7,
},
};
let lsp_symbols = vec![
DocumentSymbol {
name: "MyStruct".into(),
kind: SymbolKind::Struct,
range: range.clone(),
selection_range: sel.clone(),
children: Some(vec![DocumentSymbol {
name: "new".into(),
kind: SymbolKind::Method,
range: LspRange {
start: LspPosition {
line: 6,
character: 4,
},
end: LspPosition {
line: 8,
character: 5,
},
},
selection_range: LspRange {
start: LspPosition {
line: 6,
character: 7,
},
end: LspPosition {
line: 6,
character: 10,
},
},
children: None,
detail: Some("fn new() -> Self".into()),
}]),
detail: None,
},
DocumentSymbol {
name: "main".into(),
kind: SymbolKind::Function,
range: range.clone(),
selection_range: LspRange {
start: LspPosition {
line: 12,
character: 3,
},
end: LspPosition {
line: 12,
character: 7,
},
},
children: None,
detail: Some("fn main()".into()),
},
];
let repo_syms = to_repo_symbols(&lsp_symbols, "src/main.rs");
assert_eq!(repo_syms.len(), 3);
assert_eq!(repo_syms[0].name, "MyStruct");
assert_eq!(repo_syms[0].kind, parser::SymbolKind::Struct);
assert_eq!(repo_syms[0].line, 5);
assert_eq!(repo_syms[1].name, "new");
assert_eq!(repo_syms[1].kind, parser::SymbolKind::Function);
assert_eq!(repo_syms[1].signature.as_deref(), Some("fn new() -> Self"));
assert_eq!(repo_syms[2].name, "main");
assert_eq!(repo_syms[2].kind, parser::SymbolKind::Function);
assert_eq!(repo_syms[2].line, 13);
}
#[test]
fn test_diagnostics_cache_type() {
let cache: DiagnosticsCache = Arc::new(std::sync::Mutex::new(HashMap::new()));
{
let mut c = cache.lock().expect("mutex poisoned");
c.insert("file:///test.rs".into(), vec![]);
}
let c = cache.lock().expect("mutex poisoned");
assert!(c.contains_key("file:///test.rs"));
assert!(c.get("file:///test.rs").unwrap().is_empty());
}
}