Skip to content

Commit d5c5c6e

Browse files
focused tests
1 parent 0ccb304 commit d5c5c6e

File tree

1 file changed

+195
-109
lines changed
  • crates/djls-workspace/src

1 file changed

+195
-109
lines changed

crates/djls-workspace/src/fs.rs

Lines changed: 195 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,11 @@ use std::sync::Arc;
1414
use crate::buffers::Buffers;
1515
use crate::paths;
1616

17-
/// Trait for file system operations
1817
pub trait FileSystem: Send + Sync {
19-
/// Read the entire contents of a file
2018
fn read_to_string(&self, path: &Path) -> io::Result<String>;
21-
22-
/// Check if a path exists
2319
fn exists(&self, path: &Path) -> bool;
2420
}
2521

26-
/// In-memory file system for testing
2722
#[cfg(test)]
2823
pub struct InMemoryFileSystem {
2924
files: HashMap<PathBuf, String>,
@@ -120,125 +115,216 @@ impl FileSystem for WorkspaceFileSystem {
120115

121116
#[cfg(test)]
122117
mod tests {
123-
use url::Url;
124-
125118
use super::*;
126-
use crate::buffers::Buffers;
127-
use crate::document::TextDocument;
128-
use crate::language::LanguageId;
129-
130-
#[test]
131-
fn test_lsp_filesystem_overlay_precedence() {
132-
let mut memory_fs = InMemoryFileSystem::new();
133-
memory_fs.add_file(
134-
std::path::PathBuf::from("/test/file.py"),
135-
"original content".to_string(),
136-
);
137-
138-
let buffers = Buffers::new();
139-
let lsp_fs = WorkspaceFileSystem::new(buffers.clone(), Arc::new(memory_fs));
140-
141-
// Before adding buffer, should read from fallback
142-
let path = std::path::Path::new("/test/file.py");
143-
assert_eq!(lsp_fs.read_to_string(path).unwrap(), "original content");
144-
145-
// Add buffer - this simulates having an open document with changes
146-
let url = Url::from_file_path("/test/file.py").unwrap();
147-
let document = TextDocument::new("overlay content".to_string(), 1, LanguageId::Python);
148-
buffers.open(url, document);
149-
150-
// Now should read from buffer
151-
assert_eq!(lsp_fs.read_to_string(path).unwrap(), "overlay content");
152-
}
153119

154-
#[test]
155-
fn test_lsp_filesystem_fallback_when_no_overlay() {
156-
let mut memory_fs = InMemoryFileSystem::new();
157-
memory_fs.add_file(
158-
std::path::PathBuf::from("/test/file.py"),
159-
"disk content".to_string(),
160-
);
120+
mod in_memory {
121+
use super::*;
161122

162-
let buffers = Buffers::new();
163-
let lsp_fs = WorkspaceFileSystem::new(buffers, Arc::new(memory_fs));
123+
#[test]
124+
fn test_read_existing_file() {
125+
let mut fs = InMemoryFileSystem::new();
126+
fs.add_file("/test.py".into(), "file content".to_string());
164127

165-
// Should fall back to disk when no buffer exists
166-
let path = std::path::Path::new("/test/file.py");
167-
assert_eq!(lsp_fs.read_to_string(path).unwrap(), "disk content");
168-
}
128+
assert_eq!(
129+
fs.read_to_string(Path::new("/test.py")).unwrap(),
130+
"file content"
131+
);
132+
}
169133

170-
#[test]
171-
fn test_lsp_filesystem_other_operations_delegate() {
172-
let mut memory_fs = InMemoryFileSystem::new();
173-
memory_fs.add_file(
174-
std::path::PathBuf::from("/test/file.py"),
175-
"content".to_string(),
176-
);
134+
#[test]
135+
fn test_read_nonexistent_file() {
136+
let fs = InMemoryFileSystem::new();
177137

178-
let buffers = Buffers::new();
179-
let lsp_fs = WorkspaceFileSystem::new(buffers, Arc::new(memory_fs));
138+
let result = fs.read_to_string(Path::new("/missing.py"));
139+
assert!(result.is_err());
140+
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::NotFound);
141+
}
180142

181-
let path = std::path::Path::new("/test/file.py");
143+
#[test]
144+
fn test_exists_returns_true_for_existing() {
145+
let mut fs = InMemoryFileSystem::new();
146+
fs.add_file("/exists.py".into(), "content".to_string());
182147

183-
// This should delegate to the fallback filesystem
184-
assert!(lsp_fs.exists(path));
148+
assert!(fs.exists(Path::new("/exists.py")));
149+
}
150+
151+
#[test]
152+
fn test_exists_returns_false_for_nonexistent() {
153+
let fs = InMemoryFileSystem::new();
154+
155+
assert!(!fs.exists(Path::new("/missing.py")));
156+
}
185157
}
186158

187-
#[test]
188-
fn test_overlay_consistency() {
189-
// Create an empty filesystem (no files on disk)
190-
let memory_fs = InMemoryFileSystem::new();
191-
let buffers = Buffers::new();
192-
let lsp_fs = WorkspaceFileSystem::new(buffers.clone(), Arc::new(memory_fs));
159+
mod workspace {
160+
use url::Url;
193161

194-
let path = std::path::Path::new("/test/overlay_only.py");
162+
use crate::buffers::Buffers;
163+
use crate::document::TextDocument;
164+
use crate::language::LanguageId;
195165

196-
// Before adding to overlay, file doesn't exist
197-
assert!(!lsp_fs.exists(path));
166+
use super::*;
198167

199-
// Add file to overlay only (not on disk)
200-
let url = Url::from_file_path("/test/overlay_only.py").unwrap();
201-
let document = TextDocument::new("overlay content".to_string(), 1, LanguageId::Python);
202-
buffers.open(url, document);
168+
#[test]
169+
fn test_reads_from_buffer_when_present() {
170+
let disk = Arc::new(InMemoryFileSystem::new());
171+
let buffers = Buffers::new();
172+
let fs = WorkspaceFileSystem::new(buffers.clone(), disk);
203173

204-
// Now file should exist
205-
assert!(lsp_fs.exists(path), "Overlay file should exist");
174+
// Add file to buffer
175+
let url = Url::from_file_path("/test.py").unwrap();
176+
let doc = TextDocument::new("buffer content".to_string(), 1, LanguageId::Python);
177+
buffers.open(url, doc);
206178

207-
// And we should be able to read its content
208-
assert_eq!(
209-
lsp_fs.read_to_string(path).unwrap(),
210-
"overlay content",
211-
"Should read overlay content"
212-
);
213-
}
179+
assert_eq!(
180+
fs.read_to_string(Path::new("/test.py")).unwrap(),
181+
"buffer content"
182+
);
183+
}
184+
185+
#[test]
186+
fn test_reads_from_disk_when_no_buffer() {
187+
let mut disk_fs = InMemoryFileSystem::new();
188+
disk_fs.add_file("/test.py".into(), "disk content".to_string());
189+
190+
let buffers = Buffers::new();
191+
let fs = WorkspaceFileSystem::new(buffers, Arc::new(disk_fs));
192+
193+
assert_eq!(
194+
fs.read_to_string(Path::new("/test.py")).unwrap(),
195+
"disk content"
196+
);
197+
}
198+
199+
#[test]
200+
fn test_buffer_overrides_disk() {
201+
let mut disk_fs = InMemoryFileSystem::new();
202+
disk_fs.add_file("/test.py".into(), "disk content".to_string());
203+
204+
let buffers = Buffers::new();
205+
let fs = WorkspaceFileSystem::new(buffers.clone(), Arc::new(disk_fs));
206+
207+
// Add buffer with different content
208+
let url = Url::from_file_path("/test.py").unwrap();
209+
let doc = TextDocument::new("buffer content".to_string(), 1, LanguageId::Python);
210+
buffers.open(url, doc);
211+
212+
assert_eq!(
213+
fs.read_to_string(Path::new("/test.py")).unwrap(),
214+
"buffer content"
215+
);
216+
}
217+
218+
#[test]
219+
fn test_exists_for_buffer_only_file() {
220+
let disk = Arc::new(InMemoryFileSystem::new());
221+
let buffers = Buffers::new();
222+
let fs = WorkspaceFileSystem::new(buffers.clone(), disk);
214223

215-
#[test]
216-
fn test_overlay_with_relative_path() {
217-
// Create an empty filesystem (no files on disk)
218-
let memory_fs = InMemoryFileSystem::new();
219-
let buffers = Buffers::new();
220-
let lsp_fs = WorkspaceFileSystem::new(buffers.clone(), Arc::new(memory_fs));
221-
222-
// Use a relative path that doesn't exist on disk
223-
let relative_path = std::path::Path::new("nonexistent/overlay.py");
224-
225-
// Convert to absolute URL for the buffer (simulating how LSP would provide it)
226-
let absolute_path = std::env::current_dir().unwrap().join(relative_path);
227-
let url = Url::from_file_path(&absolute_path).unwrap();
228-
229-
// Add to overlay
230-
let document = TextDocument::new("relative overlay".to_string(), 1, LanguageId::Python);
231-
buffers.open(url, document);
232-
233-
// The relative path should now work through the overlay
234-
assert!(
235-
lsp_fs.exists(relative_path),
236-
"Relative overlay file should exist"
237-
);
238-
assert_eq!(
239-
lsp_fs.read_to_string(relative_path).unwrap(),
240-
"relative overlay",
241-
"Should read relative overlay content"
242-
);
224+
// Add file only to buffer
225+
let url = Url::from_file_path("/buffer_only.py").unwrap();
226+
let doc = TextDocument::new("content".to_string(), 1, LanguageId::Python);
227+
buffers.open(url, doc);
228+
229+
assert!(fs.exists(Path::new("/buffer_only.py")));
230+
}
231+
232+
#[test]
233+
fn test_exists_for_disk_only_file() {
234+
let mut disk_fs = InMemoryFileSystem::new();
235+
disk_fs.add_file("/disk_only.py".into(), "content".to_string());
236+
237+
let buffers = Buffers::new();
238+
let fs = WorkspaceFileSystem::new(buffers, Arc::new(disk_fs));
239+
240+
assert!(fs.exists(Path::new("/disk_only.py")));
241+
}
242+
243+
#[test]
244+
fn test_exists_for_both_buffer_and_disk() {
245+
let mut disk_fs = InMemoryFileSystem::new();
246+
disk_fs.add_file("/both.py".into(), "disk".to_string());
247+
248+
let buffers = Buffers::new();
249+
let fs = WorkspaceFileSystem::new(buffers.clone(), Arc::new(disk_fs));
250+
251+
// Also add to buffer
252+
let url = Url::from_file_path("/both.py").unwrap();
253+
let doc = TextDocument::new("buffer".to_string(), 1, LanguageId::Python);
254+
buffers.open(url, doc);
255+
256+
assert!(fs.exists(Path::new("/both.py")));
257+
}
258+
259+
#[test]
260+
fn test_exists_returns_false_when_nowhere() {
261+
let disk = Arc::new(InMemoryFileSystem::new());
262+
let buffers = Buffers::new();
263+
let fs = WorkspaceFileSystem::new(buffers, disk);
264+
265+
assert!(!fs.exists(Path::new("/nowhere.py")));
266+
}
267+
268+
#[test]
269+
fn test_read_error_when_file_nowhere() {
270+
let disk = Arc::new(InMemoryFileSystem::new());
271+
let buffers = Buffers::new();
272+
let fs = WorkspaceFileSystem::new(buffers, disk);
273+
274+
let result = fs.read_to_string(Path::new("/missing.py"));
275+
assert!(result.is_err());
276+
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::NotFound);
277+
}
278+
279+
#[test]
280+
fn test_reflects_buffer_updates() {
281+
let disk = Arc::new(InMemoryFileSystem::new());
282+
let buffers = Buffers::new();
283+
let fs = WorkspaceFileSystem::new(buffers.clone(), disk);
284+
285+
let url = Url::from_file_path("/test.py").unwrap();
286+
287+
// Initial buffer content
288+
let doc1 = TextDocument::new("version 1".to_string(), 1, LanguageId::Python);
289+
buffers.open(url.clone(), doc1);
290+
assert_eq!(
291+
fs.read_to_string(Path::new("/test.py")).unwrap(),
292+
"version 1"
293+
);
294+
295+
// Update buffer content
296+
let doc2 = TextDocument::new("version 2".to_string(), 2, LanguageId::Python);
297+
buffers.update(url, doc2);
298+
assert_eq!(
299+
fs.read_to_string(Path::new("/test.py")).unwrap(),
300+
"version 2"
301+
);
302+
}
303+
304+
#[test]
305+
fn test_handles_buffer_removal() {
306+
let mut disk_fs = InMemoryFileSystem::new();
307+
disk_fs.add_file("/test.py".into(), "disk content".to_string());
308+
309+
let buffers = Buffers::new();
310+
let fs = WorkspaceFileSystem::new(buffers.clone(), Arc::new(disk_fs));
311+
312+
let url = Url::from_file_path("/test.py").unwrap();
313+
314+
// Add buffer
315+
let doc = TextDocument::new("buffer content".to_string(), 1, LanguageId::Python);
316+
buffers.open(url.clone(), doc);
317+
assert_eq!(
318+
fs.read_to_string(Path::new("/test.py")).unwrap(),
319+
"buffer content"
320+
);
321+
322+
// Remove buffer
323+
let _ = buffers.close(&url);
324+
assert_eq!(
325+
fs.read_to_string(Path::new("/test.py")).unwrap(),
326+
"disk content"
327+
);
328+
}
243329
}
244330
}

0 commit comments

Comments
 (0)