use std::time::Duration;
use lsp_types::{CodeActionOrCommand, Position, Range};
mod integration;
use integration::TestServer;
const SHORT_TIMEOUT: Duration = Duration::from_secs(2);
#[test]
fn test_code_action_provides_explain_action_for_diagnostic() {
let mut server = TestServer::start();
let content = "// TODO: implement this\nfn main() {}\n";
let uri = server.create_file("src/main.rs", content);
server.send_did_open(&uri, "rust", 1, content);
let diagnostics = server.collect_diagnostics_for_uri(&uri, SHORT_TIMEOUT);
if diagnostics.is_empty() {
return;
}
let range = diagnostics[0].range;
let actions = server.send_code_action_request(&uri, range, &diagnostics);
let explain_action = actions.iter().find(|action| match action {
CodeActionOrCommand::CodeAction(ca) => ca.title.to_lowercase().contains("explain"),
CodeActionOrCommand::Command(cmd) => cmd.title.to_lowercase().contains("explain"),
});
if let Some(action) = explain_action {
match action {
CodeActionOrCommand::CodeAction(ca) => {
assert!(
ca.title.contains("Explain"),
"Expected explain action title to contain 'Explain', got: {}",
ca.title,
);
if let Some(cmd) = &ca.command {
assert_eq!(cmd.command, "diffguard.explainRule");
}
}
CodeActionOrCommand::Command(cmd) => {
assert_eq!(cmd.command, "diffguard.explainRule");
}
}
}
}
#[test]
fn test_code_action_provides_open_docs_when_rule_has_url() {
let mut server = TestServer::start();
let content = "// TODO: fix\nfn main() {}\n";
let uri = server.create_file("src/main.rs", content);
server.send_did_open(&uri, "rust", 1, content);
let diagnostics = server.collect_diagnostics_for_uri(&uri, SHORT_TIMEOUT);
if diagnostics.is_empty() {
return;
}
let range = diagnostics[0].range;
let actions = server.send_code_action_request(&uri, range, &diagnostics);
let docs_action = actions.iter().find(|action| match action {
CodeActionOrCommand::CodeAction(ca) => {
ca.title.to_lowercase().contains("docs") || ca.title.to_lowercase().contains("url")
}
CodeActionOrCommand::Command(cmd) => {
cmd.title.to_lowercase().contains("docs") || cmd.title.to_lowercase().contains("url")
}
});
if let Some(action) = docs_action {
match action {
CodeActionOrCommand::CodeAction(ca) => {
if let Some(cmd) = &ca.command {
assert_eq!(cmd.command, "diffguard.showRuleUrl");
if let Some(args) = &cmd.arguments {
assert!(
!args.is_empty(),
"Expected URL arguments for showRuleUrl command",
);
}
}
}
CodeActionOrCommand::Command(cmd) => {
assert_eq!(cmd.command, "diffguard.showRuleUrl");
}
}
}
}
#[test]
fn test_execute_explain_rule_returns_rule_details() {
let mut server = TestServer::start();
let response =
server.send_execute_command("diffguard.explainRule", vec![serde_json::json!("no-todo")]);
assert!(
response.error.is_none(),
"Expected no error for explainRule, got: {:?}",
response.error,
);
if let Some(result) = &response.result {
let rule_id = result.get("ruleId").and_then(|v| v.as_str());
let found = result.get("found").and_then(|v| v.as_bool());
let message = result.get("message").and_then(|v| v.as_str());
if found == Some(true) {
assert!(message.is_some(), "Expected message for found rule");
assert_eq!(rule_id, Some("no-todo"));
}
}
}
#[test]
fn test_execute_explain_rule_returns_not_found_for_unknown() {
let mut server = TestServer::start();
let response = server.send_execute_command(
"diffguard.explainRule",
vec![serde_json::json!("nonexistent-rule-xyz")],
);
assert!(
response.error.is_none(),
"Expected no error for explainRule with unknown rule, got: {:?}",
response.error,
);
if let Some(result) = &response.result {
let found = result.get("found").and_then(|v| v.as_bool());
assert_eq!(found, Some(false), "Expected found=false for unknown rule");
}
}
#[test]
fn test_execute_reload_config_succeeds() {
let mut server = TestServer::start();
let response = server.send_execute_command("diffguard.reloadConfig", vec![]);
assert!(
response.error.is_none(),
"Expected no error for reloadConfig, got: {:?}",
response.error,
);
if let Some(result) = &response.result {
let ok = result.get("ok").and_then(|v| v.as_bool());
assert!(ok.is_some(), "Expected 'ok' field in reloadConfig response");
}
}
#[test]
fn test_execute_show_rule_url_returns_url() {
let mut server = TestServer::start();
let test_url = "https://example.com/rules/no-todo";
let response = server.send_execute_command(
"diffguard.showRuleUrl",
vec![serde_json::json!(test_url), serde_json::json!("no-todo")],
);
assert!(
response.error.is_none(),
"Expected no error for showRuleUrl, got: {:?}",
response.error,
);
if let Some(result) = &response.result {
let url = result.get("url").and_then(|v| v.as_str());
assert_eq!(url, Some(test_url), "Expected URL to match");
}
}
#[test]
fn test_did_change_configuration_triggers_reload() {
let mut server = TestServer::start();
let content = "// TODO: test\nfn main() {}\n";
let uri = server.create_file("src/main.rs", content);
server.send_did_open(&uri, "rust", 1, content);
let _ = server.collect_diagnostics_for_uri(&uri, SHORT_TIMEOUT);
server.send_did_change_configuration(serde_json::json!({
"diffguard": {
"maxFindings": 10
}
}));
let _after_config_diags = server.collect_diagnostics_for_uri(&uri, SHORT_TIMEOUT);
}
#[test]
fn test_code_actions_empty_when_no_diagnostics() {
let mut server = TestServer::start();
let content = "fn main() {\n println!(\"hello\");\n}\n";
let uri = server.create_file("src/main.rs", content);
server.send_did_open(&uri, "rust", 1, content);
let diagnostics = server.collect_diagnostics_for_uri(&uri, SHORT_TIMEOUT);
let actions = server.send_code_action_request(
&uri,
Range::new(Position::new(0, 0), Position::new(0, 10)),
&diagnostics,
);
assert!(
actions.is_empty(),
"Expected no code actions for clean file, got: {:?}",
actions,
);
}
#[test]
fn test_execute_command_invalid_returns_error() {
let mut server = TestServer::start();
let response = server.send_execute_command("diffguard.nonexistentCommand", vec![]);
assert!(
response.error.is_some(),
"Expected error for invalid command, got success",
);
}