summaryrefslogtreecommitdiff
path: root/tests/test_vfs.py
diff options
context:
space:
mode:
authorCaptainJack2491 <jayrupnakawala@gmail.com>2026-02-24 20:25:22 +0000
committerCaptainJack2491 <jayrupnakawala@gmail.com>2026-02-24 20:25:22 +0000
commit8d7b13d9d0fee29a3be2d56bfe1ba16354df4f90 (patch)
treee37ab8ffd2c369e2c33b5aa2e4d86ae3e627f1c8 /tests/test_vfs.py
parent0e8ba44735f589765beb53133fd44eb587b58b3e (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.py195
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 == {"/": {}}