Files
MajoorWaldi-ComfyUI-Majoor-…/tests/features/test_assets_operation_services.py
T

147 lines
4.7 KiB
Python

from __future__ import annotations
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Any
import pytest
from mjr_am_backend.features.assets.delete_service import delete_asset_and_cleanup
from mjr_am_backend.features.assets.models import AssetDeleteTarget, AssetRenameTarget
from mjr_am_backend.features.assets.rename_service import rename_asset_and_sync
from mjr_am_backend.shared import Result
class _DB:
def __init__(self) -> None:
self.executed: list[tuple[str, tuple]] = []
self.query_result: Result[Any] = Result.Ok([])
self.execute_result: Result[Any] = Result.Ok(1)
@asynccontextmanager
async def atransaction(self, mode="immediate"):
self.executed.append(("TRANSACTION", (mode,)))
yield
async def aexecute(self, sql, params=()):
self.executed.append((sql, tuple(params)))
return self.execute_result
async def aquery(self, sql, params=()):
self.executed.append((sql, tuple(params)))
return self.query_result
class _Index:
def __init__(self) -> None:
self.calls: list[tuple] = []
async def index_paths(self, *args):
self.calls.append(args)
return Result.Ok({})
@pytest.mark.asyncio
async def test_delete_asset_and_cleanup_removes_file_and_db_rows(tmp_path: Path):
file_path = tmp_path / "asset.png"
file_path.write_bytes(b"x")
db = _DB()
target = AssetDeleteTarget(
services={"db": db},
matched_asset_id=7,
resolved_path=file_path,
filepath_where="filepath IN (?) OR filepath = ? COLLATE NOCASE",
filepath_params=(str(file_path), str(file_path)),
)
def _delete_file(path: Path) -> Result[bool]:
path.unlink()
return Result.Ok(True)
result = await delete_asset_and_cleanup(
services={"db": db},
target=target,
delete_file_safe=_delete_file,
safe_error_message=lambda exc, default: f"{default}: {exc}",
)
assert result.ok is True
assert file_path.exists() is False
assert any("DELETE FROM assets WHERE id = ?" in sql for sql, _ in db.executed)
payload = result.unwrap()
assert payload["db_cleanup_ok"] is True
@pytest.mark.asyncio
async def test_delete_asset_and_cleanup_reports_db_cleanup_failure(tmp_path: Path):
file_path = tmp_path / "asset.png"
file_path.write_bytes(b"x")
db = _DB()
db.execute_result = Result.Err("DB_ERROR", "locked")
target = AssetDeleteTarget(
services={"db": db},
matched_asset_id=7,
resolved_path=file_path,
filepath_where="filepath IN (?) OR filepath = ? COLLATE NOCASE",
filepath_params=(str(file_path), str(file_path)),
)
def _delete_file(path: Path) -> Result[bool]:
path.unlink()
return Result.Ok(True)
result = await delete_asset_and_cleanup(
services={"db": db},
target=target,
delete_file_safe=_delete_file,
safe_error_message=lambda exc, default: f"{default}: {exc}",
)
assert result.ok is True
assert file_path.exists() is False
payload = result.unwrap()
assert payload["deleted"] == 1
assert payload["db_cleanup_ok"] is False
assert payload["db_errors"]
@pytest.mark.asyncio
async def test_rename_asset_and_sync_updates_file_db_and_reindex(tmp_path: Path):
old_path = tmp_path / "old.png"
old_path.write_bytes(b"x")
db = _DB()
db.query_result = Result.Ok([{"id": 1, "filename": "new.png"}])
index = _Index()
target = AssetRenameTarget(
services={"db": db, "index": index},
matched_asset_id=1,
current_resolved=old_path,
current_filename="old.png",
current_source="output",
current_root_id="",
filepath_where="filepath IN (?) OR filepath = ? COLLATE NOCASE",
filepath_params=(str(old_path), str(old_path)),
new_name="new.png",
)
result = await rename_asset_and_sync(
services={"db": db, "index": index},
target=target,
infer_source_and_root_id_from_path=lambda *args, **kwargs: _raise_not_used(),
is_within_root=lambda _p, _r: True,
get_runtime_output_root=lambda: str(tmp_path),
get_input_directory=lambda: str(tmp_path / "in"),
list_custom_roots=lambda: [],
to_thread_timeout_s=5,
safe_error_message=lambda exc, default: f"{default}: {exc}",
)
assert result.ok is True
assert (tmp_path / "new.png").exists() is True
assert old_path.exists() is False
assert any("UPDATE assets SET filename = ?, filepath = ?, mtime = ? WHERE id = ?" in sql for sql, _ in db.executed)
assert len(index.calls) == 1
def _raise_not_used():
raise AssertionError("should not be called")