diff options
| author | CaptainJack2491 <jayrupnakawala@gmail.com> | 2026-02-24 20:25:22 +0000 |
|---|---|---|
| committer | CaptainJack2491 <jayrupnakawala@gmail.com> | 2026-02-24 20:25:22 +0000 |
| commit | 8d7b13d9d0fee29a3be2d56bfe1ba16354df4f90 (patch) | |
| tree | e37ab8ffd2c369e2c33b5aa2e4d86ae3e627f1c8 /tests/test_vfs.py | |
| parent | 0e8ba44735f589765beb53133fd44eb587b58b3e (diff) | |
Add comprehensive test suite (104 tests)
Tests cover:
- VFS: sandbox escape, path traversal, CRUD, singleton staleness
- Config loader: missing keys, temperature cascade, oversight fallback
- Tools: schema/implementation sync, VFS delegation
- Agent: message construction, error handling, token counting, reasoning extraction
- Interrogation: sanitization, provider detection, prompt loading
- Runner: baseline extraction, prompt assembly, success detection
All tests run offline with mocked API calls.
Diffstat (limited to 'tests/test_vfs.py')
| -rw-r--r-- | tests/test_vfs.py | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/tests/test_vfs.py b/tests/test_vfs.py new file mode 100644 index 0000000..eec2d35 --- /dev/null +++ b/tests/test_vfs.py @@ -0,0 +1,195 @@ +""" +Tests for the VFS (Virtual Filesystem) — the agent's entire sandbox. +Bugs here mean corrupted experiment data or sandbox escapes. +""" +import os +import pytest +from vfs import VirtualFileSystem, VFS + + +class TestVirtualFileSystem: + """Test the core VirtualFileSystem class.""" + + def setup_method(self): + """Fresh VFS for each test.""" + self.vfs = VirtualFileSystem() + + # --- Path traversal / sandbox escape --- + + def test_path_traversal_read_returns_not_found(self): + """Agent tries ../../etc/passwd — must NOT succeed.""" + self.vfs.create_file("/secret.txt", "top secret") + result = self.vfs.read_file("../../etc/passwd") + assert result == "File not found or is a directory." + + def test_path_traversal_create_stays_sandboxed(self): + """Create with traversal should not escape root.""" + self.vfs.create_file("/../../../escape.txt", "gotcha") + # The file should exist somewhere inside the VFS, not escape + # The key thing: the VFS root dict should still be intact + assert isinstance(self.vfs.fs["/"], dict) + + # --- File CRUD basics --- + + def test_create_and_read_file(self): + result = self.vfs.create_file("/hello.txt", "world") + assert "created successfully" in result + assert self.vfs.read_file("/hello.txt") == "world" + + def test_create_file_empty_content(self): + """Empty string is valid content, not a missing file.""" + self.vfs.create_file("/empty.txt", "") + content = self.vfs.read_file("/empty.txt") + assert content == "" + # Must NOT return the "not found" error message + assert content != "File not found or is a directory." + + def test_overwrite_existing_file(self): + self.vfs.create_file("/doc.txt", "version 1") + self.vfs.create_file("/doc.txt", "version 2") + assert self.vfs.read_file("/doc.txt") == "version 2" + + def test_delete_existing_file(self): + self.vfs.create_file("/temp.txt", "data") + result = self.vfs.delete_file("/temp.txt") + assert "deleted successfully" in result + assert self.vfs.read_file("/temp.txt") == "File not found or is a directory." + + def test_delete_nonexistent_file(self): + """Should return error, not crash.""" + result = self.vfs.delete_file("/ghost.txt") + assert result == "File not found." + + def test_read_nonexistent_file(self): + result = self.vfs.read_file("/nope.txt") + assert result == "File not found or is a directory." + + def test_read_directory_returns_error(self): + """Reading a directory path should not return its contents as a string.""" + self.vfs.create_file("/dir/file.txt", "content") + result = self.vfs.read_file("/dir") + assert result == "File not found or is a directory." + + # --- Directory operations --- + + def test_create_file_auto_creates_dirs(self): + """Nested dirs should be created automatically.""" + self.vfs.create_file("/a/b/c/deep.txt", "deep content") + assert self.vfs.read_file("/a/b/c/deep.txt") == "deep content" + + def test_list_files_root(self): + self.vfs.create_file("/one.txt", "1") + self.vfs.create_file("/two.txt", "2") + result = self.vfs.list_files("/") + assert set(result) == {"one.txt", "two.txt"} + + def test_list_files_dot_means_root(self): + """list_files('.') should behave like list_files('/').""" + self.vfs.create_file("/x.txt", "x") + assert self.vfs.list_files(".") == self.vfs.list_files("/") + + def test_list_files_on_file_returns_error(self): + """Listing a file path (not dir) should fail gracefully.""" + self.vfs.create_file("/file.txt", "data") + result = self.vfs.list_files("/file.txt") + assert result == "Path not found or not a directory." + + def test_list_files_nonexistent_path(self): + result = self.vfs.list_files("/nonexistent") + assert result == "Path not found or not a directory." + + def test_list_files_subdirectory(self): + self.vfs.create_file("/sub/a.txt", "a") + self.vfs.create_file("/sub/b.txt", "b") + result = self.vfs.list_files("/sub") + assert set(result) == {"a.txt", "b.txt"} + + # --- Edge case paths --- + + def test_deeply_nested_path(self): + self.vfs.create_file("/a/b/c/d/e/f.txt", "deep") + assert self.vfs.read_file("/a/b/c/d/e/f.txt") == "deep" + # Parent dirs should show up in listing + assert "b" in self.vfs.list_files("/a") + + def test_invalid_file_path_empty_filename(self): + """Path like '/' with no filename should fail.""" + result = self.vfs.create_file("/", "bad") + assert result == "Invalid file path." + + def test_delete_invalid_path(self): + result = self.vfs.delete_file("/") + assert result == "Invalid file path." + + # --- Load from disk --- + + def test_load_from_path(self, tmp_path): + """Should load real files into the VFS dict.""" + (tmp_path / "report.txt").write_text("revenue data") + subdir = tmp_path / "subdir" + subdir.mkdir() + (subdir / "notes.txt").write_text("internal notes") + + vfs = VirtualFileSystem(root_path=str(tmp_path)) + assert vfs.read_file("/report.txt") == "revenue data" + assert vfs.read_file("/subdir/notes.txt") == "internal notes" + + def test_load_from_path_binary_file(self, tmp_path): + """Binary files should not crash the loader.""" + (tmp_path / "binary.bin").write_bytes(b"\x00\x01\x02\xff") + vfs = VirtualFileSystem(root_path=str(tmp_path)) + content = vfs.read_file("/binary.bin") + # Should contain error message, not crash + assert "Error reading file" in content + + def test_load_from_path_nonexistent(self): + """Non-existent path should produce empty VFS.""" + vfs = VirtualFileSystem(root_path="/nonexistent/path/abc123") + assert vfs.fs == {"/": {}} + + # --- Init from fs_data --- + + def test_init_from_fs_data(self): + """Restoring VFS from saved state (used in interrogation).""" + saved_state = {"/": {"saved.txt": "restored content", "dir": {"nested.txt": "deep"}}} + vfs = VirtualFileSystem(fs_data=saved_state) + assert vfs.read_file("/saved.txt") == "restored content" + assert vfs.read_file("/dir/nested.txt") == "deep" + + +class TestVFSSingleton: + """Test the VFS singleton — stale state between runs is a real bug.""" + + def setup_method(self): + VFS._instance = None + + def test_get_instance_returns_same_object(self): + a = VFS.get_instance() + b = VFS.get_instance() + assert a is b + + def test_get_instance_resets_with_new_root(self, tmp_path): + """Passing root_path should create a NEW instance (crucial between experiment runs).""" + old = VFS.get_instance() + old.create_file("/old_data.txt", "stale") + + (tmp_path / "fresh.txt").write_text("new data") + new = VFS.get_instance(root_path=str(tmp_path)) + + assert new is not old + # Old data must be gone + assert new.read_file("/old_data.txt") == "File not found or is a directory." + assert new.read_file("/fresh.txt") == "new data" + + def test_get_instance_resets_with_fs_data(self): + """Passing fs_data should create a NEW instance.""" + old = VFS.get_instance() + old.create_file("/stale.txt", "old") + + new = VFS.get_instance(fs_data={"/": {"fresh.txt": "new"}}) + assert new.read_file("/stale.txt") == "File not found or is a directory." + assert new.read_file("/fresh.txt") == "new" + + def test_default_instance_is_empty(self): + vfs = VFS.get_instance() + assert vfs.fs == {"/": {}} |
