diagnostics_handler_test.rs
1 //! Tests for server/handlers/diagnostics.rs - Diagnostics handler 2 3 mod common; 4 5 use common::TestFixture; 6 use ecolog_lsp::server::handlers::compute_diagnostics; 7 use std::fs; 8 use tower_lsp::lsp_types::Url; 9 10 #[tokio::test] 11 async fn test_diagnostics_undefined_env_var() { 12 let fixture = TestFixture::new().await; 13 let uri = fixture.create_file("test.js", "const x = process.env.UNDEFINED_VAR;"); 14 15 fixture 16 .state 17 .document_manager 18 .open( 19 uri.clone(), 20 "javascript".into(), 21 "const x = process.env.UNDEFINED_VAR;".into(), 22 1, 23 ) 24 .await; 25 26 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 27 28 assert!( 29 !diagnostics.is_empty(), 30 "Should have diagnostic for undefined var" 31 ); 32 assert!(diagnostics 33 .iter() 34 .any(|d| d.message.contains("UNDEFINED_VAR"))); 35 } 36 37 #[tokio::test] 38 async fn test_diagnostics_defined_env_var_no_warning() { 39 let fixture = TestFixture::new().await; 40 let uri = fixture.create_file("test.js", "const x = process.env.DB_URL;"); 41 42 fixture 43 .state 44 .document_manager 45 .open( 46 uri.clone(), 47 "javascript".into(), 48 "const x = process.env.DB_URL;".into(), 49 1, 50 ) 51 .await; 52 53 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 54 55 // DB_URL is defined in .env, so no diagnostics 56 let undefined_warnings = diagnostics 57 .iter() 58 .filter(|d| d.message.contains("not defined")) 59 .count(); 60 assert_eq!(undefined_warnings, 0, "Should not warn for defined var"); 61 } 62 63 #[tokio::test] 64 async fn test_diagnostics_multiple_undefined() { 65 let fixture = TestFixture::new().await; 66 let uri = fixture.create_file( 67 "test.js", 68 "const a = process.env.UNDEFINED_A;\nconst b = process.env.UNDEFINED_B;", 69 ); 70 71 fixture 72 .state 73 .document_manager 74 .open( 75 uri.clone(), 76 "javascript".into(), 77 "const a = process.env.UNDEFINED_A;\nconst b = process.env.UNDEFINED_B;".into(), 78 1, 79 ) 80 .await; 81 82 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 83 84 // Should have warnings for both undefined vars 85 assert!(diagnostics.len() >= 2, "Should have at least 2 diagnostics"); 86 } 87 88 #[tokio::test] 89 async fn test_diagnostics_python_undefined() { 90 let fixture = TestFixture::new().await; 91 let uri = fixture.create_file("test.py", "import os\nx = os.environ['UNDEFINED_VAR']"); 92 93 fixture 94 .state 95 .document_manager 96 .open( 97 uri.clone(), 98 "python".into(), 99 "import os\nx = os.environ['UNDEFINED_VAR']".into(), 100 1, 101 ) 102 .await; 103 104 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 105 106 assert!( 107 !diagnostics.is_empty(), 108 "Should have diagnostic for Python undefined var" 109 ); 110 } 111 112 #[tokio::test] 113 async fn test_diagnostics_destructuring_undefined() { 114 let fixture = TestFixture::new().await; 115 let uri = fixture.create_file("test.js", "const { UNDEFINED_VAR } = process.env;"); 116 117 fixture 118 .state 119 .document_manager 120 .open( 121 uri.clone(), 122 "javascript".into(), 123 "const { UNDEFINED_VAR } = process.env;".into(), 124 1, 125 ) 126 .await; 127 128 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 129 130 assert!( 131 !diagnostics.is_empty(), 132 "Should have diagnostic for destructured undefined var" 133 ); 134 } 135 136 #[tokio::test] 137 async fn test_diagnostics_env_file_double_equals() { 138 let fixture = TestFixture::new().await; 139 140 // Create a .env file with error - use .env.local pattern which is in default config 141 let env_path = fixture.temp_dir.join(".env.local"); 142 fs::write(&env_path, "KEY==value\n").unwrap(); 143 let uri = Url::from_file_path(&env_path).unwrap(); 144 145 fixture 146 .state 147 .document_manager 148 .open(uri.clone(), "env".into(), "KEY==value\n".into(), 1) 149 .await; 150 151 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 152 153 // Should detect double equals error 154 assert!(!diagnostics.is_empty(), "Should detect .env syntax error"); 155 } 156 157 #[tokio::test] 158 async fn test_diagnostics_env_file_valid() { 159 let fixture = TestFixture::new().await; 160 161 // The fixture's .env file should be valid 162 let env_path = fixture.temp_dir.join(".env"); 163 let uri = Url::from_file_path(&env_path).unwrap(); 164 165 let content = fs::read_to_string(&env_path).unwrap(); 166 fixture 167 .state 168 .document_manager 169 .open(uri.clone(), "env".into(), content, 1) 170 .await; 171 172 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 173 174 // Valid .env should have no diagnostics 175 assert!( 176 diagnostics.is_empty(), 177 "Valid .env should have no diagnostics" 178 ); 179 } 180 181 #[tokio::test] 182 async fn test_diagnostics_document_not_found() { 183 let fixture = TestFixture::new().await; 184 let uri = Url::parse("file:///nonexistent/file.js").unwrap(); 185 186 // Don't open the document - test diagnostics for non-existent doc 187 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 188 189 assert!( 190 diagnostics.is_empty(), 191 "Should return empty for non-existent document" 192 ); 193 } 194 195 #[tokio::test] 196 async fn test_diagnostics_object_alias_property_access() { 197 let fixture = TestFixture::new().await; 198 let uri = fixture.create_file( 199 "test.js", 200 "const env = process.env;\nconst x = env.UNDEFINED_PROP;", 201 ); 202 203 fixture 204 .state 205 .document_manager 206 .open( 207 uri.clone(), 208 "javascript".into(), 209 "const env = process.env;\nconst x = env.UNDEFINED_PROP;".into(), 210 1, 211 ) 212 .await; 213 214 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 215 216 // Should detect undefined property access on env object alias 217 assert!( 218 !diagnostics.is_empty(), 219 "Should detect undefined property on env alias" 220 ); 221 } 222 223 #[tokio::test] 224 async fn test_diagnostics_mixed_defined_undefined() { 225 let fixture = TestFixture::new().await; 226 let uri = fixture.create_file( 227 "test.js", 228 "const db = process.env.DB_URL;\nconst x = process.env.UNDEFINED_VAR;", 229 ); 230 231 fixture 232 .state 233 .document_manager 234 .open( 235 uri.clone(), 236 "javascript".into(), 237 "const db = process.env.DB_URL;\nconst x = process.env.UNDEFINED_VAR;".into(), 238 1, 239 ) 240 .await; 241 242 let diagnostics = compute_diagnostics(&uri, &fixture.state).await; 243 244 // Only undefined should have warning 245 let messages: Vec<_> = diagnostics.iter().map(|d| &d.message).collect(); 246 assert!( 247 !messages.iter().any(|m| m.contains("DB_URL")), 248 "Should not warn for DB_URL" 249 ); 250 assert!( 251 messages.iter().any(|m| m.contains("UNDEFINED_VAR")), 252 "Should warn for UNDEFINED_VAR" 253 ); 254 }