error_test.rs
1 use crate::harness::{LspTestClient, TempWorkspace}; 2 use serde_json::json; 3 use std::thread; 4 use std::time::Duration; 5 6 #[test] 7 fn test_invalid_params() { 8 let workspace = TempWorkspace::new(); 9 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 10 client.initialize().expect("Initialize failed"); 11 12 let result = client.request( 13 "textDocument/hover", 14 Some(json!({ 15 "invalid": "params" 16 })), 17 ); 18 19 assert!( 20 result.is_err() || result.as_ref().map(|v| v.is_null()).unwrap_or(false), 21 "Invalid params should fail gracefully" 22 ); 23 24 client.shutdown().expect("Shutdown failed"); 25 } 26 27 #[test] 28 fn test_document_not_open() { 29 let workspace = TempWorkspace::new(); 30 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 31 client.initialize().expect("Initialize failed"); 32 33 let hover = client 34 .hover("file:///nonexistent.js", 0, 0) 35 .expect("Request should not fail"); 36 37 assert!( 38 hover.is_null(), 39 "Hover on unopened document should return null" 40 ); 41 42 client.shutdown().expect("Shutdown failed"); 43 } 44 45 #[test] 46 fn test_position_out_of_bounds() { 47 let workspace = TempWorkspace::new(); 48 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 49 client.initialize().expect("Initialize failed"); 50 51 let uri = workspace.file_uri("test.js"); 52 workspace.create_file("test.js", "a"); 53 54 client 55 .open_document(&uri, "javascript", "a") 56 .expect("Failed to open document"); 57 thread::sleep(Duration::from_millis(200)); 58 59 let hover = client 60 .hover(&uri, 100, 100) 61 .expect("Request should not fail"); 62 63 assert!(hover.is_null(), "Out of bounds position should return null"); 64 65 client.shutdown().expect("Shutdown failed"); 66 } 67 68 #[test] 69 fn test_malformed_uri() { 70 let workspace = TempWorkspace::new(); 71 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 72 client.initialize().expect("Initialize failed"); 73 74 let result = client.hover("not-a-valid-uri", 0, 0); 75 76 match result { 77 Ok(hover) => assert!(hover.is_null(), "Malformed URI should return null"), 78 Err(e) => { 79 let err_msg = e.to_string(); 80 assert!( 81 err_msg.contains("-32602") 82 || err_msg.contains("invalid") 83 || err_msg.contains("URL"), 84 "Should be an invalid params error: {}", 85 err_msg 86 ); 87 } 88 } 89 90 client.shutdown().expect("Shutdown failed"); 91 } 92 93 #[test] 94 fn test_empty_document() { 95 let workspace = TempWorkspace::new(); 96 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 97 client.initialize().expect("Initialize failed"); 98 99 let uri = workspace.file_uri("empty.js"); 100 workspace.create_file("empty.js", ""); 101 102 client 103 .open_document(&uri, "javascript", "") 104 .expect("Failed to open document"); 105 thread::sleep(Duration::from_millis(200)); 106 107 let hover = client.hover(&uri, 0, 0).expect("Request should not fail"); 108 assert!(hover.is_null()); 109 110 let completion = client 111 .completion(&uri, 0, 0) 112 .expect("Request should not fail"); 113 assert!(completion.is_null() || completion.as_array().map(|a| a.is_empty()).unwrap_or(false)); 114 115 client.shutdown().expect("Shutdown failed"); 116 } 117 118 #[test] 119 fn test_binary_file_content() { 120 let workspace = TempWorkspace::new(); 121 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 122 client.initialize().expect("Initialize failed"); 123 124 let uri = workspace.file_uri("binary.js"); 125 126 workspace.create_file("binary.js", "\0\0\0"); 127 128 client 129 .open_document(&uri, "javascript", "\0\0\0") 130 .expect("Failed to open document"); 131 thread::sleep(Duration::from_millis(200)); 132 133 let _hover = client.hover(&uri, 0, 0).expect("Request should not fail"); 134 135 client.shutdown().expect("Shutdown failed"); 136 } 137 138 #[test] 139 fn test_very_long_line() { 140 let workspace = TempWorkspace::new(); 141 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 142 client.initialize().expect("Initialize failed"); 143 144 let uri = workspace.file_uri("long.js"); 145 let long_line = "a".repeat(100_000); 146 workspace.create_file("long.js", &long_line); 147 148 client 149 .open_document(&uri, "javascript", &long_line) 150 .expect("Failed to open document"); 151 thread::sleep(Duration::from_millis(300)); 152 153 let _hover = client 154 .hover(&uri, 0, 99999) 155 .expect("Request should not fail"); 156 157 client.shutdown().expect("Shutdown failed"); 158 } 159 160 #[test] 161 fn test_rapid_document_changes() { 162 let workspace = TempWorkspace::new(); 163 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 164 client.initialize().expect("Initialize failed"); 165 166 let uri = workspace.file_uri("rapid.js"); 167 workspace.create_file("rapid.js", ""); 168 169 client 170 .open_document(&uri, "javascript", "") 171 .expect("Failed to open document"); 172 173 for i in 1..=20 { 174 let content = format!("process.env.VAR_{}", i); 175 client 176 .change_document(&uri, i, &content) 177 .expect("Change should not fail"); 178 } 179 180 thread::sleep(Duration::from_millis(500)); 181 182 let _hover = client 183 .hover(&uri, 0, 15) 184 .expect("Hover should work after rapid changes"); 185 186 client.shutdown().expect("Shutdown failed"); 187 } 188 189 #[test] 190 fn test_unicode_in_document() { 191 let workspace = TempWorkspace::new(); 192 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 193 client.initialize().expect("Initialize failed"); 194 195 let uri = workspace.file_uri("unicode.js"); 196 let content = "// 日本語コメント\nconst api = process.env.API_KEY;"; 197 workspace.create_file("unicode.js", content); 198 199 client 200 .open_document(&uri, "javascript", content) 201 .expect("Failed to open document"); 202 thread::sleep(Duration::from_millis(300)); 203 204 let _hover = client.hover(&uri, 1, 15).expect("Request should not fail"); 205 206 client.shutdown().expect("Shutdown failed"); 207 } 208 209 #[test] 210 fn test_deeply_nested_code() { 211 let workspace = TempWorkspace::new(); 212 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 213 client.initialize().expect("Initialize failed"); 214 215 let uri = workspace.file_uri("nested.js"); 216 217 let mut content = String::new(); 218 for _ in 0..50 { 219 content.push_str("if (true) { "); 220 } 221 content.push_str("process.env.DB_URL"); 222 for _ in 0..50 { 223 content.push_str(" }"); 224 } 225 226 workspace.create_file("nested.js", &content); 227 client 228 .open_document(&uri, "javascript", &content) 229 .expect("Failed to open document"); 230 thread::sleep(Duration::from_millis(500)); 231 232 let _hover = client.hover(&uri, 0, 600).expect("Request should not fail"); 233 234 client.shutdown().expect("Shutdown failed"); 235 } 236 237 #[test] 238 fn test_command_with_wrong_arg_types() { 239 let workspace = TempWorkspace::new(); 240 let client = LspTestClient::spawn(workspace.root.clone()).expect("Failed to spawn LSP"); 241 client.initialize().expect("Initialize failed"); 242 243 let result = client.execute_command("ecolog.file.setActive", vec![json!(12345)]); 244 245 assert!( 246 result.is_ok(), 247 "Command should not crash with wrong arg types" 248 ); 249 250 client.shutdown().expect("Shutdown failed"); 251 }