integration_rust.rs
1 mod common; 2 use common::TestFixture; 3 use ecolog_lsp::server::handlers::{compute_diagnostics, handle_completion, handle_hover}; 4 use tower_lsp::lsp_types::{ 5 HoverParams, Position, TextDocumentIdentifier, TextDocumentPositionParams, 6 }; 7 8 #[tokio::test] 9 async fn test_rust_hover_std_env_var() { 10 let fixture = TestFixture::new().await; 11 let uri = fixture.create_file("main.rs", "fn main() { std::env::var(\"DB_URL\"); }"); 12 13 fixture 14 .state 15 .document_manager 16 .open( 17 uri.clone(), 18 "rust".to_string(), 19 "fn main() { std::env::var(\"DB_URL\"); }".to_string(), 20 0, 21 ) 22 .await; 23 24 let hover = handle_hover( 25 HoverParams { 26 text_document_position_params: TextDocumentPositionParams { 27 text_document: TextDocumentIdentifier { uri }, 28 position: Position::new(0, 27), 29 }, 30 work_done_progress_params: Default::default(), 31 }, 32 &fixture.state, 33 ) 34 .await; 35 36 assert!(hover.is_some()); 37 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); 38 } 39 40 #[tokio::test] 41 async fn test_rust_hover_std_env_var_os() { 42 let fixture = TestFixture::new().await; 43 let uri = fixture.create_file("main.rs", "fn main() { std::env::var_os(\"API_KEY\"); }"); 44 45 fixture 46 .state 47 .document_manager 48 .open( 49 uri.clone(), 50 "rust".to_string(), 51 "fn main() { std::env::var_os(\"API_KEY\"); }".to_string(), 52 0, 53 ) 54 .await; 55 56 let hover = handle_hover( 57 HoverParams { 58 text_document_position_params: TextDocumentPositionParams { 59 text_document: TextDocumentIdentifier { uri }, 60 position: Position::new(0, 31), 61 }, 62 work_done_progress_params: Default::default(), 63 }, 64 &fixture.state, 65 ) 66 .await; 67 68 assert!(hover.is_some()); 69 assert!(format!("{:?}", hover.unwrap()).contains("secret_key")); 70 } 71 72 #[tokio::test] 73 async fn test_rust_hover_result_destructuring() { 74 let fixture = TestFixture::new().await; 75 let uri = fixture.create_file( 76 "main.rs", 77 "fn main() { let Ok(val) = std::env::var(\"DB_URL\"); }", 78 ); 79 fixture 80 .state 81 .document_manager 82 .open( 83 uri.clone(), 84 "rust".to_string(), 85 "fn main() { let Ok(val) = std::env::var(\"DB_URL\"); }".to_string(), 86 0, 87 ) 88 .await; 89 90 let hover = handle_hover( 91 HoverParams { 92 text_document_position_params: TextDocumentPositionParams { 93 text_document: TextDocumentIdentifier { uri }, 94 position: Position::new(0, 20), 95 }, 96 work_done_progress_params: Default::default(), 97 }, 98 &fixture.state, 99 ) 100 .await; 101 102 assert!(hover.is_some(), "Expected hover on 'val' binding"); 103 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); 104 } 105 106 #[tokio::test] 107 async fn test_rust_hover_result_destructuring_var_short() { 108 let fixture = TestFixture::new().await; 109 let uri = fixture.create_file( 110 "main.rs", 111 "fn main() { let Ok(val) = env::var(\"DB_URL\"); }", 112 ); 113 fixture 114 .state 115 .document_manager 116 .open( 117 uri.clone(), 118 "rust".to_string(), 119 "fn main() { let Ok(val) = env::var(\"DB_URL\"); }".to_string(), 120 0, 121 ) 122 .await; 123 124 let hover = handle_hover( 125 HoverParams { 126 text_document_position_params: TextDocumentPositionParams { 127 text_document: TextDocumentIdentifier { uri }, 128 position: Position::new(0, 20), 129 }, 130 work_done_progress_params: Default::default(), 131 }, 132 &fixture.state, 133 ) 134 .await; 135 136 assert!(hover.is_some(), "Expected hover on 'val' binding"); 137 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); 138 } 139 140 #[tokio::test] 141 async fn test_rust_hover_if_let() { 142 let fixture = TestFixture::new().await; 143 let uri = fixture.create_file( 144 "main.rs", 145 "fn main() { if let Ok(val) = std::env::var(\"DB_URL\") { println!(\"{}\", val); } }", 146 ); 147 fixture 148 .state 149 .document_manager 150 .open( 151 uri.clone(), 152 "rust".to_string(), 153 "fn main() { if let Ok(val) = std::env::var(\"DB_URL\") { println!(\"{}\", val); } }" 154 .to_string(), 155 0, 156 ) 157 .await; 158 159 let hover = handle_hover( 160 HoverParams { 161 text_document_position_params: TextDocumentPositionParams { 162 text_document: TextDocumentIdentifier { uri }, 163 position: Position::new(0, 23), 164 }, 165 work_done_progress_params: Default::default(), 166 }, 167 &fixture.state, 168 ) 169 .await; 170 171 assert!(hover.is_some(), "Expected hover on 'val' binding in if let"); 172 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); 173 } 174 175 #[tokio::test] 176 async fn test_rust_hover_option_destructuring() { 177 let fixture = TestFixture::new().await; 178 let uri = fixture.create_file( 179 "main.rs", 180 "fn main() { let Some(val) = std::env::var(\"DB_URL\").ok(); }", 181 ); 182 fixture 183 .state 184 .document_manager 185 .open( 186 uri.clone(), 187 "rust".to_string(), 188 "fn main() { let Some(val) = std::env::var(\"DB_URL\").ok(); }".to_string(), 189 0, 190 ) 191 .await; 192 193 let hover = handle_hover( 194 HoverParams { 195 text_document_position_params: TextDocumentPositionParams { 196 text_document: TextDocumentIdentifier { uri }, 197 position: Position::new(0, 22), 198 }, 199 work_done_progress_params: Default::default(), 200 }, 201 &fixture.state, 202 ) 203 .await; 204 205 assert!( 206 hover.is_some(), 207 "Expected hover on 'val' binding with Some destructuring" 208 ); 209 } 210 211 #[tokio::test] 212 async fn test_rust_hover_match_destructuring() { 213 let fixture = TestFixture::new().await; 214 let uri = fixture.create_file("main.rs", "fn main() { match std::env::var(\"DB_URL\") { Ok(val) => println!(\"{}\", val), _ => () } }"); 215 fixture.state.document_manager.open(uri.clone(), "rust".to_string(), 216 "fn main() { match std::env::var(\"DB_URL\") { Ok(val) => println!(\"{}\", val), _ => () } }".to_string(), 0).await; 217 218 let hover = handle_hover( 219 HoverParams { 220 text_document_position_params: TextDocumentPositionParams { 221 text_document: TextDocumentIdentifier { uri }, 222 position: Position::new(0, 48), 223 }, 224 work_done_progress_params: Default::default(), 225 }, 226 &fixture.state, 227 ) 228 .await; 229 230 assert!( 231 hover.is_some(), 232 "Expected hover on 'val' binding in match arm" 233 ); 234 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); 235 } 236 237 #[tokio::test] 238 async fn test_rust_diagnostics_result_destructuring_undefined() { 239 let fixture = TestFixture::new().await; 240 let uri = fixture.create_file( 241 "main.rs", 242 "fn main() { let Ok(val) = std::env::var(\"MISSING_VAR\"); println!(\"{}\", val); }", 243 ); 244 fixture 245 .state 246 .document_manager 247 .open( 248 uri.clone(), 249 "rust".to_string(), 250 "fn main() { let Ok(val) = std::env::var(\"MISSING_VAR\"); println!(\"{}\", val); }" 251 .to_string(), 252 0, 253 ) 254 .await; 255 256 let diags = compute_diagnostics(&uri, &fixture.state).await; 257 258 assert!(!diags.is_empty()); 259 assert!(diags.iter().any(|d| d.message.contains("not defined"))); 260 } 261 262 #[tokio::test] 263 async fn test_rust_diagnostics_if_let_undefined() { 264 let fixture = TestFixture::new().await; 265 let uri = fixture.create_file( 266 "main.rs", 267 "fn main() { if let Ok(val) = std::env::var(\"MISSING_VAR\") { println!(\"{}\", val); } }", 268 ); 269 fixture.state.document_manager.open(uri.clone(), "rust".to_string(), 270 "fn main() { if let Ok(val) = std::env::var(\"MISSING_VAR\") { println!(\"{}\", val); } }".to_string(), 0).await; 271 272 let diags = compute_diagnostics(&uri, &fixture.state).await; 273 274 assert!(!diags.is_empty()); 275 assert!(diags.iter().any(|d| d.message.contains("not defined"))); 276 } 277 278 #[tokio::test] 279 async fn test_rust_diagnostics_match_destructuring_undefined() { 280 let fixture = TestFixture::new().await; 281 let uri = fixture.create_file("main.rs", "fn main() { match std::env::var(\"MISSING_VAR\") { Ok(val) => println!(\"{}\", val), _ => () } }"); 282 fixture.state.document_manager.open(uri.clone(), "rust".to_string(), 283 "fn main() { match std::env::var(\"MISSING_VAR\") { Ok(val) => println!(\"{}\", val), _ => () } }".to_string(), 0).await; 284 285 let diags = compute_diagnostics(&uri, &fixture.state).await; 286 287 assert!(!diags.is_empty()); 288 assert!(diags.iter().any(|d| d.message.contains("not defined"))); 289 } 290 291 #[tokio::test] 292 async fn test_rust_completion() { 293 let fixture = TestFixture::new().await; 294 let uri = fixture.create_file("main.rs", "fn main() { std::env::var(\"\"); }"); 295 296 fixture 297 .state 298 .document_manager 299 .open( 300 uri.clone(), 301 "rust".to_string(), 302 "fn main() { std::env::var(\"\"); }".to_string(), 303 0, 304 ) 305 .await; 306 307 let completion = handle_completion( 308 tower_lsp::lsp_types::CompletionParams { 309 text_document_position: TextDocumentPositionParams { 310 text_document: TextDocumentIdentifier { uri }, 311 position: Position::new(0, 27), 312 }, 313 work_done_progress_params: Default::default(), 314 partial_result_params: Default::default(), 315 context: None, 316 }, 317 &fixture.state, 318 ) 319 .await; 320 321 assert!(completion.is_some()); 322 assert!(completion.unwrap().iter().any(|i| i.label == "PORT")); 323 } 324 325 #[tokio::test] 326 async fn test_rust_dotenv_macro_mock() { 327 let fixture = TestFixture::new().await; 328 let uri = fixture.create_file("main.rs", "fn main() { dotenv!(\"DEBUG\"); }"); 329 330 fixture 331 .state 332 .document_manager 333 .open( 334 uri.clone(), 335 "rust".to_string(), 336 "fn main() { dotenv!(\"DEBUG\"); }".to_string(), 337 0, 338 ) 339 .await; 340 341 let hover = handle_hover( 342 HoverParams { 343 text_document_position_params: TextDocumentPositionParams { 344 text_document: TextDocumentIdentifier { uri }, 345 position: Position::new(0, 24), 346 }, 347 work_done_progress_params: Default::default(), 348 }, 349 &fixture.state, 350 ) 351 .await; 352 353 if hover.is_some() { 354 assert!(format!("{:?}", hover.unwrap()).contains("true")); 355 } 356 } 357 358 // ═══════════════════════════════════════════════════════════════════════════ 359 // New Pattern Tests: dotenv/dotenvy, struct init, const/static, method chains 360 // ═══════════════════════════════════════════════════════════════════════════ 361 362 #[tokio::test] 363 async fn test_rust_hover_dotenv_var() { 364 let fixture = TestFixture::new().await; 365 let content = r#"fn main() { let db = dotenv::var("DB_URL"); }"#; 366 let uri = fixture.create_file("main.rs", content); 367 368 fixture 369 .state 370 .document_manager 371 .open(uri.clone(), "rust".to_string(), content.to_string(), 0) 372 .await; 373 374 let hover = handle_hover( 375 HoverParams { 376 text_document_position_params: TextDocumentPositionParams { 377 text_document: TextDocumentIdentifier { uri }, 378 position: Position::new(0, 36), 379 }, 380 work_done_progress_params: Default::default(), 381 }, 382 &fixture.state, 383 ) 384 .await; 385 386 assert!(hover.is_some(), "Expected hover for dotenv::var"); 387 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); //")); 388 } 389 390 #[tokio::test] 391 async fn test_rust_hover_dotenvy_var() { 392 let fixture = TestFixture::new().await; 393 let content = r#"fn main() { let db = dotenvy::var("DB_URL"); }"#; 394 let uri = fixture.create_file("main.rs", content); 395 396 fixture 397 .state 398 .document_manager 399 .open(uri.clone(), "rust".to_string(), content.to_string(), 0) 400 .await; 401 402 let hover = handle_hover( 403 HoverParams { 404 text_document_position_params: TextDocumentPositionParams { 405 text_document: TextDocumentIdentifier { uri }, 406 position: Position::new(0, 37), 407 }, 408 work_done_progress_params: Default::default(), 409 }, 410 &fixture.state, 411 ) 412 .await; 413 414 assert!(hover.is_some(), "Expected hover for dotenvy::var"); 415 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); //")); 416 } 417 418 #[tokio::test] 419 async fn test_rust_hover_method_chain_unwrap() { 420 let fixture = TestFixture::new().await; 421 let content = r#"fn main() { let db = env::var("DB_URL").unwrap(); }"#; 422 let uri = fixture.create_file("main.rs", content); 423 424 fixture 425 .state 426 .document_manager 427 .open(uri.clone(), "rust".to_string(), content.to_string(), 0) 428 .await; 429 430 let hover = handle_hover( 431 HoverParams { 432 text_document_position_params: TextDocumentPositionParams { 433 text_document: TextDocumentIdentifier { uri }, 434 position: Position::new(0, 33), 435 }, 436 work_done_progress_params: Default::default(), 437 }, 438 &fixture.state, 439 ) 440 .await; 441 442 assert!(hover.is_some(), "Expected hover for method chain unwrap"); 443 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); //")); 444 } 445 446 #[tokio::test] 447 async fn test_rust_hover_try_expression() { 448 let fixture = TestFixture::new().await; 449 let content = r#"fn get_db() -> Result<String, std::env::VarError> { let db = env::var("DB_URL")?; Ok(db) }"#; 450 let uri = fixture.create_file("main.rs", content); 451 452 fixture 453 .state 454 .document_manager 455 .open(uri.clone(), "rust".to_string(), content.to_string(), 0) 456 .await; 457 458 let hover = handle_hover( 459 HoverParams { 460 text_document_position_params: TextDocumentPositionParams { 461 text_document: TextDocumentIdentifier { uri }, 462 position: Position::new(0, 73), 463 }, 464 work_done_progress_params: Default::default(), 465 }, 466 &fixture.state, 467 ) 468 .await; 469 470 assert!(hover.is_some(), "Expected hover for try expression"); 471 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); //")); 472 } 473 474 #[tokio::test] 475 async fn test_rust_hover_const_env_macro() { 476 let fixture = TestFixture::new().await; 477 let content = r#"const DB: &str = env!("DB_URL"); fn main() {}"#; 478 let uri = fixture.create_file("main.rs", content); 479 480 fixture 481 .state 482 .document_manager 483 .open(uri.clone(), "rust".to_string(), content.to_string(), 0) 484 .await; 485 486 let hover = handle_hover( 487 HoverParams { 488 text_document_position_params: TextDocumentPositionParams { 489 text_document: TextDocumentIdentifier { uri }, 490 position: Position::new(0, 24), 491 }, 492 work_done_progress_params: Default::default(), 493 }, 494 &fixture.state, 495 ) 496 .await; 497 498 assert!(hover.is_some(), "Expected hover for const env! macro"); 499 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); //")); 500 } 501 502 #[tokio::test] 503 async fn test_rust_hover_static_env_macro() { 504 let fixture = TestFixture::new().await; 505 let content = r#"static DB: &str = env!("DB_URL"); fn main() {}"#; 506 let uri = fixture.create_file("main.rs", content); 507 508 fixture 509 .state 510 .document_manager 511 .open(uri.clone(), "rust".to_string(), content.to_string(), 0) 512 .await; 513 514 let hover = handle_hover( 515 HoverParams { 516 text_document_position_params: TextDocumentPositionParams { 517 text_document: TextDocumentIdentifier { uri }, 518 position: Position::new(0, 25), 519 }, 520 work_done_progress_params: Default::default(), 521 }, 522 &fixture.state, 523 ) 524 .await; 525 526 assert!(hover.is_some(), "Expected hover for static env! macro"); 527 assert!(format!("{:?}", hover.unwrap()).contains("postgres://")); //")); 528 }