/ tests / integration_rust.rs
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  }