6ede33ccdb
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
145 lines
4.5 KiB
Python
145 lines
4.5 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from app.agents.chat.multi_agent_chat.shared.middleware.filesystem.backends.local_folder import (
|
|
LocalFolderBackend,
|
|
)
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
def test_local_backend_write_read_edit_roundtrip(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "notes").mkdir()
|
|
|
|
write = backend.write("/notes/test.md", "line1\nline2")
|
|
assert write.error is None
|
|
assert write.path == "/notes/test.md"
|
|
|
|
read = backend.read("/notes/test.md", offset=0, limit=20)
|
|
assert "line1" in read
|
|
assert "line2" in read
|
|
|
|
edit = backend.edit("/notes/test.md", "line2", "updated")
|
|
assert edit.error is None
|
|
assert edit.occurrences == 1
|
|
|
|
read_after = backend.read("/notes/test.md", offset=0, limit=20)
|
|
assert "updated" in read_after
|
|
|
|
|
|
def test_local_backend_blocks_path_escape(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
|
|
result = backend.write("/../../etc/passwd", "bad")
|
|
assert result.error is not None
|
|
assert "Invalid path" in result.error
|
|
|
|
|
|
def test_local_backend_glob_and_grep(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "docs").mkdir()
|
|
(tmp_path / "docs" / "a.txt").write_text("hello world\n")
|
|
(tmp_path / "docs" / "b.md").write_text("hello markdown\n")
|
|
|
|
infos = backend.glob_info("**/*.txt", "/docs")
|
|
paths = {info["path"] for info in infos}
|
|
assert "/docs/a.txt" in paths
|
|
|
|
grep = backend.grep_raw("hello", "/docs", "*.md")
|
|
assert isinstance(grep, list)
|
|
assert any(match["path"] == "/docs/b.md" for match in grep)
|
|
|
|
|
|
def test_local_backend_read_raw_returns_exact_content(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "notes").mkdir()
|
|
expected = "# Title\n\nline 1\nline 2\n"
|
|
write = backend.write("/notes/raw.md", expected)
|
|
assert write.error is None
|
|
|
|
raw = backend.read_raw("/notes/raw.md")
|
|
assert raw == expected
|
|
|
|
|
|
def test_local_backend_write_rejects_missing_parent_directory(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
|
|
write = backend.write("/tempoo/new-note.md", "# New note")
|
|
|
|
assert write.error is not None
|
|
assert "parent directory" in write.error
|
|
assert not (tmp_path / "tempoo").exists()
|
|
|
|
|
|
def test_local_backend_delete_file_success(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "delete-me.md").write_text("bye")
|
|
|
|
res = backend.delete_file("/delete-me.md")
|
|
assert res.error is None
|
|
assert res.path == "/delete-me.md"
|
|
assert not (tmp_path / "delete-me.md").exists()
|
|
|
|
|
|
def test_local_backend_delete_file_rejects_directory(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "subdir").mkdir()
|
|
|
|
res = backend.delete_file("/subdir")
|
|
assert res.error is not None
|
|
assert "directory" in res.error
|
|
assert (tmp_path / "subdir").exists()
|
|
|
|
|
|
def test_local_backend_delete_file_missing_returns_error(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
|
|
res = backend.delete_file("/nope.md")
|
|
assert res.error is not None
|
|
assert "not found" in res.error
|
|
|
|
|
|
def test_local_backend_rmdir_success(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "empty").mkdir()
|
|
|
|
res = backend.rmdir("/empty")
|
|
assert res.error is None
|
|
assert res.path == "/empty"
|
|
assert not (tmp_path / "empty").exists()
|
|
|
|
|
|
def test_local_backend_rmdir_rejects_non_empty(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "withkid").mkdir()
|
|
(tmp_path / "withkid" / "child.md").write_text("x")
|
|
|
|
res = backend.rmdir("/withkid")
|
|
assert res.error is not None
|
|
assert "not empty" in res.error
|
|
assert (tmp_path / "withkid" / "child.md").exists()
|
|
|
|
|
|
def test_local_backend_rmdir_rejects_file(tmp_path: Path):
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
(tmp_path / "f.md").write_text("x")
|
|
|
|
res = backend.rmdir("/f.md")
|
|
assert res.error is not None
|
|
assert "not a directory" in res.error
|
|
|
|
|
|
def test_local_backend_rmdir_rejects_root(tmp_path: Path):
|
|
"""``rmdir /`` MUST fail. The exact error wording comes from
|
|
``_resolve_virtual`` (root resolves to outside the sandbox); what
|
|
matters is that the call returns an error and does NOT delete the
|
|
sandbox root on disk."""
|
|
backend = LocalFolderBackend(str(tmp_path))
|
|
|
|
res = backend.rmdir("/")
|
|
assert res.error is not None
|
|
assert "Invalid path" in res.error or "root" in res.error
|
|
assert tmp_path.exists()
|