feat: voortgang delete in headerbar

This commit is contained in:
kodi
2026-03-15 11:52:39 +01:00
parent 73b09d2802
commit 7d910479f9
23 changed files with 311 additions and 43 deletions
@@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
import sys
import tempfile
import time
import unittest
from pathlib import Path
@@ -10,11 +11,15 @@ import httpx
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
from backend.app.dependencies import get_file_ops_service
from backend.app.dependencies import get_delete_task_service, get_file_ops_service, get_task_service
from backend.app.db.task_repository import TaskRepository
from backend.app.fs.filesystem_adapter import FilesystemAdapter
from backend.app.main import app
from backend.app.security.path_guard import PathGuard
from backend.app.services.delete_task_service import DeleteTaskService
from backend.app.services.file_ops_service import FileOpsService
from backend.app.services.task_service import TaskService
from backend.app.tasks_runner import TaskRunner
class FileOpsApiGoldenTest(unittest.TestCase):
@@ -22,21 +27,37 @@ class FileOpsApiGoldenTest(unittest.TestCase):
self.temp_dir = tempfile.TemporaryDirectory()
self.root = Path(self.temp_dir.name) / "root"
self.root.mkdir(parents=True, exist_ok=True)
self.repo = TaskRepository(str(Path(self.temp_dir.name) / "tasks.db"))
self.scope = self.root / "scope"
self.scope.mkdir(parents=True, exist_ok=True)
(self.scope / "old.txt").write_text("x", encoding="utf-8")
(self.scope / "existing.txt").write_text("y", encoding="utf-8")
path_guard = PathGuard({"storage1": str(self.root)})
service = FileOpsService(
path_guard=PathGuard({"storage1": str(self.root)}),
path_guard=path_guard,
filesystem=FilesystemAdapter(),
)
delete_service = DeleteTaskService(
path_guard=path_guard,
repository=self.repo,
runner=TaskRunner(repository=self.repo, filesystem=FilesystemAdapter()),
)
task_service = TaskService(repository=self.repo)
async def _override_file_ops_service() -> FileOpsService:
return service
async def _override_delete_task_service() -> DeleteTaskService:
return delete_service
async def _override_task_service() -> TaskService:
return task_service
app.dependency_overrides[get_file_ops_service] = _override_file_ops_service
app.dependency_overrides[get_delete_task_service] = _override_delete_task_service
app.dependency_overrides[get_task_service] = _override_task_service
def tearDown(self) -> None:
app.dependency_overrides.clear()
@@ -50,6 +71,24 @@ class FileOpsApiGoldenTest(unittest.TestCase):
return asyncio.run(_run())
def _get(self, url: str) -> httpx.Response:
async def _run() -> httpx.Response:
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
return await client.get(url)
return asyncio.run(_run())
def _wait_task(self, task_id: str, timeout_s: float = 2.0) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
response = self._get(f"/api/tasks/{task_id}")
body = response.json()
if body["status"] in {"completed", "failed"}:
return body
time.sleep(0.02)
self.fail("task did not reach terminal state in time")
def test_mkdir_success(self) -> None:
response = self._post(
"/api/files/mkdir",
@@ -225,8 +264,12 @@ class FileOpsApiGoldenTest(unittest.TestCase):
{"path": "storage1/scope/delete_me.txt"},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"path": "storage1/scope/delete_me.txt"})
self.assertEqual(response.status_code, 202)
body = response.json()
self.assertEqual(body["status"], "queued")
detail = self._wait_task(body["task_id"])
self.assertEqual(detail["operation"], "delete")
self.assertEqual(detail["status"], "completed")
self.assertFalse(target.exists())
def test_delete_empty_directory_success(self) -> None:
@@ -238,8 +281,12 @@ class FileOpsApiGoldenTest(unittest.TestCase):
{"path": "storage1/scope/empty_dir"},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"path": "storage1/scope/empty_dir"})
self.assertEqual(response.status_code, 202)
body = response.json()
self.assertEqual(body["status"], "queued")
detail = self._wait_task(body["task_id"])
self.assertEqual(detail["operation"], "delete")
self.assertEqual(detail["status"], "completed")
self.assertFalse(target.exists())
def test_delete_not_found(self) -> None:
@@ -312,8 +359,12 @@ class FileOpsApiGoldenTest(unittest.TestCase):
{"path": "storage1/scope/non_empty_recursive", "recursive": True},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"path": "storage1/scope/non_empty_recursive"})
self.assertEqual(response.status_code, 202)
body = response.json()
self.assertEqual(body["status"], "queued")
detail = self._wait_task(body["task_id"])
self.assertEqual(detail["operation"], "delete")
self.assertEqual(detail["status"], "completed")
self.assertFalse(target.exists())
def test_delete_invalid_path(self) -> None: