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 }