/ tests / interpolation_test.rs
interpolation_test.rs
  1  mod common;
  2  
  3  use common::TestFixture;
  4  use ecolog_lsp::server::handlers::{handle_execute_command, handle_hover};
  5  use serde_json::json;
  6  use std::fs::File;
  7  use std::io::Write;
  8  use tower_lsp::lsp_types::{
  9      ExecuteCommandParams, HoverParams, Position, TextDocumentIdentifier, TextDocumentPositionParams,
 10  };
 11  
 12  async fn set_interpolation(fixture: &TestFixture, enabled: bool) -> Option<serde_json::Value> {
 13      let params = ExecuteCommandParams {
 14          command: "ecolog.interpolation.set".to_string(),
 15          arguments: vec![json!(enabled)],
 16          work_done_progress_params: Default::default(),
 17      };
 18      handle_execute_command(params, &fixture.state).await
 19  }
 20  
 21  async fn get_interpolation(fixture: &TestFixture) -> Option<serde_json::Value> {
 22      let params = ExecuteCommandParams {
 23          command: "ecolog.interpolation.get".to_string(),
 24          arguments: vec![],
 25          work_done_progress_params: Default::default(),
 26      };
 27      handle_execute_command(params, &fixture.state).await
 28  }
 29  
 30  async fn get_hover(
 31      fixture: &TestFixture,
 32      uri: &tower_lsp::lsp_types::Url,
 33      line: u32,
 34      col: u32,
 35  ) -> Option<tower_lsp::lsp_types::Hover> {
 36      handle_hover(
 37          HoverParams {
 38              text_document_position_params: TextDocumentPositionParams {
 39                  text_document: TextDocumentIdentifier { uri: uri.clone() },
 40                  position: Position::new(line, col),
 41              },
 42              work_done_progress_params: Default::default(),
 43          },
 44          &fixture.state,
 45      )
 46      .await
 47  }
 48  
 49  fn extract_hover_value(hover: &tower_lsp::lsp_types::Hover) -> Option<String> {
 50      match &hover.contents {
 51          tower_lsp::lsp_types::HoverContents::Markup(markup) => {
 52              let content = &markup.value;
 53              if let Some(start) = content.find("**Value**: `") {
 54                  let value_start = start + "**Value**: `".len();
 55                  if let Some(end) = content[value_start..].find('`') {
 56                      return Some(content[value_start..value_start + end].to_string());
 57                  }
 58              }
 59              None
 60          }
 61          _ => None,
 62      }
 63  }
 64  
 65  #[tokio::test]
 66  async fn test_get_interpolation_returns_enabled_by_default() {
 67      let fixture = TestFixture::new().await;
 68  
 69      let result = get_interpolation(&fixture).await;
 70      assert!(result.is_some(), "Get interpolation should return a result");
 71  
 72      let result = result.unwrap();
 73      assert_eq!(
 74          result.get("enabled").and_then(|v| v.as_bool()),
 75          Some(true),
 76          "Interpolation should be enabled by default"
 77      );
 78  }
 79  
 80  #[tokio::test]
 81  async fn test_set_interpolation_changes_state() {
 82      let fixture = TestFixture::new().await;
 83  
 84      let result = get_interpolation(&fixture).await.unwrap();
 85      assert_eq!(result.get("enabled").and_then(|v| v.as_bool()), Some(true));
 86  
 87      let result = set_interpolation(&fixture, false).await;
 88      assert!(result.is_some());
 89      let result = result.unwrap();
 90      assert_eq!(result.get("success").and_then(|v| v.as_bool()), Some(true));
 91      assert_eq!(result.get("enabled").and_then(|v| v.as_bool()), Some(false));
 92  
 93      let result = get_interpolation(&fixture).await.unwrap();
 94      assert_eq!(
 95          result.get("enabled").and_then(|v| v.as_bool()),
 96          Some(false),
 97          "Interpolation should now be disabled"
 98      );
 99  
100      let result = set_interpolation(&fixture, true).await.unwrap();
101      assert_eq!(result.get("success").and_then(|v| v.as_bool()), Some(true));
102      assert_eq!(result.get("enabled").and_then(|v| v.as_bool()), Some(true));
103  
104      let result = get_interpolation(&fixture).await.unwrap();
105      assert_eq!(
106          result.get("enabled").and_then(|v| v.as_bool()),
107          Some(true),
108          "Interpolation should be enabled again"
109      );
110  }
111  
112  #[tokio::test]
113  async fn test_interpolation_affects_hover_values() {
114      let fixture = TestFixture::new().await;
115  
116      let env_path = fixture.temp_dir.join(".env.interpolation");
117      {
118          let mut env_file = File::create(&env_path).unwrap();
119          writeln!(env_file, "BASE_DIR=/home/user").unwrap();
120          writeln!(env_file, "DATA_PATH=${{BASE_DIR}}/data").unwrap();
121      }
122  
123      fixture
124          .state
125          .core
126          .refresh(abundantis::RefreshOptions::reset_all())
127          .await
128          .unwrap();
129  
130      let uri = fixture.create_file("test.js", "process.env.DATA_PATH");
131      fixture
132          .state
133          .document_manager
134          .open(
135              uri.clone(),
136              "javascript".to_string(),
137              "process.env.DATA_PATH".to_string(),
138              0,
139          )
140          .await;
141  
142      let hover = get_hover(&fixture, &uri, 0, 20).await;
143      if let Some(hover) = hover {
144          let value = extract_hover_value(&hover);
145          if let Some(value) = value {
146              assert!(
147                  value.contains("/home/user/data") || value == "/home/user/data",
148                  "With interpolation enabled, should show resolved value. Got: {}",
149                  value
150              );
151          }
152      }
153  
154      set_interpolation(&fixture, false).await;
155  
156      fixture
157          .state
158          .core
159          .refresh(abundantis::RefreshOptions::reset_all())
160          .await
161          .unwrap();
162  
163      let hover = get_hover(&fixture, &uri, 0, 20).await;
164      if let Some(hover) = hover {
165          let value = extract_hover_value(&hover);
166          if let Some(value) = value {
167              assert!(
168                  value.contains("${BASE_DIR}") || value == "${BASE_DIR}/data",
169                  "With interpolation disabled, should show raw value. Got: {}",
170                  value
171              );
172          }
173      }
174  }
175  
176  #[tokio::test]
177  async fn test_boolean_argument_parsing() {
178      let fixture = TestFixture::new().await;
179  
180      let params = ExecuteCommandParams {
181          command: "ecolog.interpolation.set".to_string(),
182          arguments: vec![json!(true)],
183          work_done_progress_params: Default::default(),
184      };
185      let result = handle_execute_command(params, &fixture.state)
186          .await
187          .unwrap();
188      assert_eq!(result.get("enabled").and_then(|v| v.as_bool()), Some(true));
189  
190      let params = ExecuteCommandParams {
191          command: "ecolog.interpolation.set".to_string(),
192          arguments: vec![json!(false)],
193          work_done_progress_params: Default::default(),
194      };
195      let result = handle_execute_command(params, &fixture.state)
196          .await
197          .unwrap();
198      assert_eq!(result.get("enabled").and_then(|v| v.as_bool()), Some(false));
199  
200      let result = get_interpolation(&fixture).await.unwrap();
201      assert_eq!(result.get("enabled").and_then(|v| v.as_bool()), Some(false));
202  }