services_test.rs
1 //! Tests for server/services - EnvService, DocumentService, WorkspaceService 2 3 mod common; 4 5 use common::TestFixture; 6 7 // ============================================================ 8 // EnvService Tests 9 // ============================================================ 10 11 #[tokio::test] 12 async fn test_env_service_get_workspace_root() { 13 let fixture = TestFixture::new().await; 14 15 // Create EnvService from the core 16 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 17 18 let root = env_service.get_workspace_root().await; 19 assert!(root.exists(), "Workspace root should exist"); 20 } 21 22 #[tokio::test] 23 async fn test_env_service_get_for_file() { 24 let fixture = TestFixture::new().await; 25 26 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 27 28 // DB_URL is defined in the fixture's .env 29 let result = env_service.get_for_file("DB_URL", &fixture.temp_dir).await; 30 31 assert!(result.is_some(), "Should resolve DB_URL"); 32 let var = result.unwrap(); 33 assert_eq!(var.key.as_str(), "DB_URL"); 34 } 35 36 #[tokio::test] 37 async fn test_env_service_get_for_file_not_found() { 38 let fixture = TestFixture::new().await; 39 40 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 41 42 let result = env_service 43 .get_for_file("NONEXISTENT_VAR", &fixture.temp_dir) 44 .await; 45 46 assert!(result.is_none(), "Should return None for nonexistent var"); 47 } 48 49 #[tokio::test] 50 async fn test_env_service_all_for_file() { 51 let fixture = TestFixture::new().await; 52 53 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 54 55 let vars = env_service.all_for_file(&fixture.temp_dir).await; 56 57 assert!(!vars.is_empty(), "Should return env vars from .env"); 58 // Check that DB_URL is present 59 let has_db_url = vars.iter().any(|v| v.key.as_str() == "DB_URL"); 60 assert!(has_db_url, "Should contain DB_URL"); 61 } 62 63 #[tokio::test] 64 async fn test_env_service_set_active_files() { 65 let fixture = TestFixture::new().await; 66 67 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 68 69 // Set active file filter 70 env_service.set_active_files(&[".env.local".to_string()]); 71 72 // Clear should work 73 env_service.clear_active_files(); 74 } 75 76 #[tokio::test] 77 async fn test_env_service_active_env_files() { 78 let fixture = TestFixture::new().await; 79 80 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 81 82 let files = env_service.active_env_files(&fixture.temp_dir); 83 // Should find at least the .env file 84 assert!(!files.is_empty(), "Should find .env file"); 85 } 86 87 #[tokio::test] 88 async fn test_env_service_refresh() { 89 let fixture = TestFixture::new().await; 90 91 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 92 93 // Refresh should not panic 94 env_service 95 .refresh(abundantis::RefreshOptions::default()) 96 .await; 97 } 98 99 #[tokio::test] 100 async fn test_env_service_clone() { 101 let fixture = TestFixture::new().await; 102 103 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 104 let cloned = env_service.clone(); 105 106 // Both should return the same workspace root 107 let root1 = env_service.get_workspace_root().await; 108 let root2 = cloned.get_workspace_root().await; 109 assert_eq!(root1, root2); 110 } 111 112 #[tokio::test] 113 async fn test_env_service_registered_file_paths() { 114 let fixture = TestFixture::new().await; 115 116 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 117 118 let paths = env_service.registered_file_paths(); 119 // Should have at least the .env file registered 120 assert!(!paths.is_empty(), "Should have registered .env file"); 121 } 122 123 #[tokio::test] 124 async fn test_env_service_context_for_file() { 125 let fixture = TestFixture::new().await; 126 127 let env_service = ecolog_lsp::server::services::EnvService::new(fixture.state.core.clone()); 128 129 // Create a test file path 130 let test_file = fixture.temp_dir.join("test.js"); 131 std::fs::write(&test_file, "const x = 1;").unwrap(); 132 133 let context = env_service.get_context_for_file(&test_file); 134 assert!( 135 context.is_some(), 136 "Should get context for file in workspace" 137 ); 138 } 139 140 // ============================================================ 141 // DocumentService Tests 142 // ============================================================ 143 144 #[tokio::test] 145 async fn test_document_service_open_and_get() { 146 let fixture = TestFixture::new().await; 147 let uri = fixture.create_file("test.js", "const x = process.env.DB_URL;"); 148 149 fixture 150 .state 151 .document_manager 152 .open( 153 uri.clone(), 154 "javascript".into(), 155 "const x = process.env.DB_URL;".into(), 156 1, 157 ) 158 .await; 159 160 let doc = fixture.state.document_manager.get(&uri); 161 assert!(doc.is_some(), "Should retrieve opened document"); 162 } 163 164 #[tokio::test] 165 async fn test_document_service_document_exists_after_open() { 166 let fixture = TestFixture::new().await; 167 let uri = fixture.create_file("test.js", "const x = 1;"); 168 169 // Before opening 170 assert!(fixture.state.document_manager.get(&uri).is_none()); 171 172 fixture 173 .state 174 .document_manager 175 .open(uri.clone(), "javascript".into(), "const x = 1;".into(), 1) 176 .await; 177 178 // After opening 179 assert!(fixture.state.document_manager.get(&uri).is_some()); 180 } 181 182 #[tokio::test] 183 async fn test_document_service_close() { 184 let fixture = TestFixture::new().await; 185 let uri = fixture.create_file("test.js", "const x = 1;"); 186 187 fixture 188 .state 189 .document_manager 190 .open(uri.clone(), "javascript".into(), "const x = 1;".into(), 1) 191 .await; 192 193 assert!(fixture.state.document_manager.get(&uri).is_some()); 194 195 fixture.state.document_manager.close(&uri); 196 197 assert!(fixture.state.document_manager.get(&uri).is_none()); 198 } 199 200 #[tokio::test] 201 async fn test_document_service_change() { 202 use tower_lsp::lsp_types::{Position, Range, TextDocumentContentChangeEvent}; 203 204 let fixture = TestFixture::new().await; 205 let uri = fixture.create_file("test.js", "const x = 1;"); 206 207 fixture 208 .state 209 .document_manager 210 .open(uri.clone(), "javascript".into(), "const x = 1;".into(), 1) 211 .await; 212 213 // Full document replacement via ranged incremental change 214 let changes = vec![TextDocumentContentChangeEvent { 215 range: Some(Range::new(Position::new(0, 0), Position::new(0, 12))), 216 range_length: None, 217 text: "const y = 2;".to_string(), 218 }]; 219 fixture 220 .state 221 .document_manager 222 .change(&uri, changes, 2) 223 .await; 224 225 let doc = fixture.state.document_manager.get(&uri).unwrap(); 226 assert_eq!(doc.content.as_str(), "const y = 2;"); 227 assert_eq!(doc.version, 2); 228 } 229 230 #[tokio::test] 231 async fn test_document_service_all_uris() { 232 let fixture = TestFixture::new().await; 233 let uri1 = fixture.create_file("a.js", "const a = 1;"); 234 let uri2 = fixture.create_file("b.js", "const b = 2;"); 235 236 fixture 237 .state 238 .document_manager 239 .open(uri1.clone(), "javascript".into(), "const a = 1;".into(), 1) 240 .await; 241 fixture 242 .state 243 .document_manager 244 .open(uri2.clone(), "javascript".into(), "const b = 2;".into(), 1) 245 .await; 246 247 let uris = fixture.state.document_manager.all_uris(); 248 assert_eq!(uris.len(), 2); 249 assert!(uris.contains(&uri1)); 250 assert!(uris.contains(&uri2)); 251 } 252 253 #[tokio::test] 254 async fn test_document_service_document_count() { 255 let fixture = TestFixture::new().await; 256 let uri = fixture.create_file("test.js", "const x = 1;"); 257 258 assert_eq!(fixture.state.document_manager.document_count(), 0); 259 260 fixture 261 .state 262 .document_manager 263 .open(uri.clone(), "javascript".into(), "const x = 1;".into(), 1) 264 .await; 265 266 assert_eq!(fixture.state.document_manager.document_count(), 1); 267 } 268 269 // ============================================================ 270 // WorkspaceService Tests 271 // ============================================================ 272 273 #[tokio::test] 274 async fn test_workspace_index_stats() { 275 let fixture = TestFixture::new().await; 276 fixture.index_workspace().await; 277 278 let stats = fixture.state.workspace_index.stats(); 279 // Verify stats can be retrieved without panic 280 let _ = stats.total_files; 281 } 282 283 #[tokio::test] 284 async fn test_workspace_index_files_for_env_var() { 285 let fixture = TestFixture::new().await; 286 287 // Create a file that references DB_URL 288 fixture.create_file("test.js", "const db = process.env.DB_URL;"); 289 fixture.index_workspace().await; 290 291 let files = fixture.state.workspace_index.files_for_env_var("DB_URL"); 292 // May or may not find files depending on indexing 293 // Verify operation doesn't panic 294 let _ = files.len(); 295 } 296 297 #[tokio::test] 298 async fn test_workspace_index_all_env_vars() { 299 let fixture = TestFixture::new().await; 300 fixture.create_file("test.js", "const db = process.env.DB_URL;"); 301 fixture.index_workspace().await; 302 303 let env_vars = fixture.state.workspace_index.all_env_vars(); 304 // Should find at least DB_URL from the indexed file 305 // Verify operation doesn't panic 306 let _ = env_vars.len(); 307 }