318 lines
9.6 KiB
Python
318 lines
9.6 KiB
Python
"""
|
|
Coverage tests for index_db_ops.py.
|
|
Targets lines: 29-68, 80-96, 112-164.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
from mjr_am_backend.features.index import index_db_ops as dbo
|
|
from mjr_am_backend.shared import Result
|
|
|
|
# ─── helpers ────────────────────────────────────────────────────────────────
|
|
|
|
class _DB:
|
|
def __init__(self, insert_ok=True, update_ok=True, asset_id=1):
|
|
self._insert_ok = insert_ok
|
|
self._update_ok = update_ok
|
|
self._asset_id = asset_id
|
|
self._lock_called = False
|
|
self.calls = []
|
|
|
|
async def aexecute(self, sql, params):
|
|
self.calls.append((sql, params))
|
|
if "INSERT" in sql:
|
|
if not self._insert_ok:
|
|
return Result.Err("INSERT_FAILED", "db error")
|
|
return Result.Ok(self._asset_id)
|
|
if "UPDATE" in sql:
|
|
if not self._update_ok:
|
|
return Result.Err("UPDATE_FAILED", "db error")
|
|
return Result.Ok(1)
|
|
return Result.Ok(None)
|
|
|
|
class _Lock:
|
|
def __init__(self, asset_id):
|
|
self.asset_id = asset_id
|
|
|
|
async def __aenter__(self):
|
|
return self
|
|
|
|
async def __aexit__(self, exc_type, exc, tb):
|
|
return False
|
|
|
|
def lock_for_asset(self, asset_id):
|
|
return self._Lock(asset_id)
|
|
|
|
|
|
def _make_metadata_result(ok=True):
|
|
if ok:
|
|
return Result.Ok({"width": 512, "height": 768, "prompt": "test"})
|
|
return Result.Err("META_ERR", "fail")
|
|
|
|
|
|
def _make_execution_metadata_result():
|
|
return Result.Ok({
|
|
"width": 512,
|
|
"height": 768,
|
|
"job_id": "job-1",
|
|
"workflow_id": "workflow-1",
|
|
"source_node_id": "node-1",
|
|
})
|
|
|
|
|
|
# ─── add_asset ───────────────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_asset_success(monkeypatch):
|
|
async def _write_row(db, asset_id, metadata_result, *, filepath):
|
|
return Result.Ok(None)
|
|
|
|
monkeypatch.setattr(dbo.MetadataHelpers, "write_asset_metadata_row", _write_row)
|
|
|
|
db = _DB(insert_ok=True, asset_id=42)
|
|
scanner = SimpleNamespace(db=db, _log_scan_event=lambda *a, **kw: None)
|
|
|
|
result = await dbo.add_asset(
|
|
scanner,
|
|
filename="img.png",
|
|
subfolder="",
|
|
filepath="C:/x/img.png",
|
|
kind="image",
|
|
mtime=1000,
|
|
size=4096,
|
|
file_path=Path("C:/x/img.png"),
|
|
metadata_result=_make_metadata_result(),
|
|
write_metadata=True,
|
|
)
|
|
assert result.ok
|
|
assert result.data["action"] == "added"
|
|
assert result.data["asset_id"] == 42
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_asset_persists_execution_metadata(monkeypatch):
|
|
async def _write_row(db, asset_id, metadata_result, *, filepath):
|
|
return Result.Ok(None)
|
|
|
|
monkeypatch.setattr(dbo.MetadataHelpers, "write_asset_metadata_row", _write_row)
|
|
|
|
db = _DB(insert_ok=True, asset_id=42)
|
|
scanner = SimpleNamespace(db=db, _log_scan_event=lambda *a, **kw: None)
|
|
|
|
result = await dbo.add_asset(
|
|
scanner,
|
|
filename="img.png",
|
|
subfolder="",
|
|
filepath="C:/x/img.png",
|
|
kind="image",
|
|
mtime=1000,
|
|
size=4096,
|
|
file_path=Path("C:/x/img.png"),
|
|
metadata_result=_make_execution_metadata_result(),
|
|
write_metadata=True,
|
|
)
|
|
|
|
assert result.ok
|
|
insert_sql, insert_params = db.calls[0]
|
|
assert "job_id, workflow_id, source_node_id" in insert_sql
|
|
assert insert_params[-3:] == ("job-1", "workflow-1", "node-1")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_asset_insert_failure():
|
|
db = _DB(insert_ok=False)
|
|
scanner = SimpleNamespace(db=db)
|
|
|
|
result = await dbo.add_asset(
|
|
scanner,
|
|
filename="img.png",
|
|
subfolder="",
|
|
filepath="C:/img.png",
|
|
kind="image",
|
|
mtime=1000,
|
|
size=512,
|
|
file_path=Path("C:/img.png"),
|
|
metadata_result=_make_metadata_result(),
|
|
)
|
|
assert not result.ok and "INSERT_FAILED" in result.code
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_asset_no_asset_id():
|
|
db = _DB(insert_ok=True, asset_id=None)
|
|
scanner = SimpleNamespace(db=db)
|
|
|
|
result = await dbo.add_asset(
|
|
scanner,
|
|
filename="img.png",
|
|
subfolder="",
|
|
filepath="C:/img.png",
|
|
kind="image",
|
|
mtime=1000,
|
|
size=512,
|
|
file_path=Path("C:/img.png"),
|
|
metadata_result=_make_metadata_result(),
|
|
)
|
|
assert not result.ok
|
|
|
|
|
|
# ─── write_asset_metadata_if_needed ─────────────────────────────────────────
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_write_asset_metadata_if_needed_skip_lock():
|
|
scanner = SimpleNamespace()
|
|
# skip_lock=True → early return
|
|
await dbo.write_asset_metadata_if_needed(
|
|
scanner, asset_id=1, metadata_result=_make_metadata_result(),
|
|
filepath="x.png", write_metadata=True, skip_lock=True
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_write_asset_metadata_if_needed_no_write():
|
|
scanner = SimpleNamespace()
|
|
# write_metadata=False → early return
|
|
await dbo.write_asset_metadata_if_needed(
|
|
scanner, asset_id=1, metadata_result=_make_metadata_result(),
|
|
filepath="x.png", write_metadata=False, skip_lock=False
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_write_asset_metadata_if_needed_write_fails(monkeypatch):
|
|
async def _write_row(db, asset_id, metadata_result, *, filepath):
|
|
return Result.Err("FAIL", "write error")
|
|
|
|
monkeypatch.setattr(dbo.MetadataHelpers, "write_asset_metadata_row", _write_row)
|
|
log_calls = []
|
|
scanner = SimpleNamespace(
|
|
db=object(),
|
|
_log_scan_event=lambda level, msg, **kw: log_calls.append(msg),
|
|
)
|
|
await dbo.write_asset_metadata_if_needed(
|
|
scanner, asset_id=5, metadata_result=_make_metadata_result(),
|
|
filepath="x.png", write_metadata=True, skip_lock=False
|
|
)
|
|
assert any("Failed" in c for c in log_calls)
|
|
|
|
|
|
# ─── update_asset ────────────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_asset_success_with_lock(monkeypatch):
|
|
async def _write_row(db, asset_id, metadata_result, *, filepath):
|
|
return Result.Ok(None)
|
|
|
|
monkeypatch.setattr(dbo.MetadataHelpers, "write_asset_metadata_row", _write_row)
|
|
|
|
db = _DB(update_ok=True)
|
|
scanner = SimpleNamespace(db=db, _log_scan_event=lambda *a, **kw: None)
|
|
|
|
result = await dbo.update_asset(
|
|
scanner,
|
|
asset_id=1,
|
|
file_path=Path("C:/x.png"),
|
|
mtime=2000,
|
|
size=1024,
|
|
metadata_result=_make_metadata_result(),
|
|
write_metadata=True,
|
|
skip_lock=False,
|
|
)
|
|
assert result.ok and result.data["action"] == "updated"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_asset_persists_execution_metadata(monkeypatch):
|
|
async def _write_row(db, asset_id, metadata_result, *, filepath):
|
|
return Result.Ok(None)
|
|
|
|
monkeypatch.setattr(dbo.MetadataHelpers, "write_asset_metadata_row", _write_row)
|
|
|
|
db = _DB(update_ok=True)
|
|
scanner = SimpleNamespace(db=db, _log_scan_event=lambda *a, **kw: None)
|
|
|
|
result = await dbo.update_asset(
|
|
scanner,
|
|
asset_id=1,
|
|
file_path=Path("C:/x.png"),
|
|
mtime=2000,
|
|
size=1024,
|
|
metadata_result=_make_execution_metadata_result(),
|
|
write_metadata=True,
|
|
skip_lock=False,
|
|
)
|
|
|
|
assert result.ok
|
|
update_sql, update_params = db.calls[0]
|
|
assert "job_id = COALESCE" in update_sql
|
|
assert update_params[-4:-1] == ("job-1", "workflow-1", "node-1")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_asset_skip_lock(monkeypatch):
|
|
async def _write_row(db, asset_id, metadata_result, *, filepath):
|
|
return Result.Ok(None)
|
|
|
|
monkeypatch.setattr(dbo.MetadataHelpers, "write_asset_metadata_row", _write_row)
|
|
|
|
db = _DB(update_ok=True)
|
|
scanner = SimpleNamespace(db=db)
|
|
|
|
result = await dbo.update_asset(
|
|
scanner,
|
|
asset_id=2,
|
|
file_path=Path("C:/y.png"),
|
|
mtime=3000,
|
|
size=2048,
|
|
metadata_result=_make_metadata_result(),
|
|
write_metadata=True,
|
|
skip_lock=True,
|
|
)
|
|
assert result.ok
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_asset_db_error():
|
|
db = _DB(update_ok=False)
|
|
scanner = SimpleNamespace(db=db)
|
|
|
|
result = await dbo.update_asset(
|
|
scanner,
|
|
asset_id=3,
|
|
file_path=Path("C:/z.png"),
|
|
mtime=4000,
|
|
size=512,
|
|
metadata_result=_make_metadata_result(),
|
|
write_metadata=False,
|
|
skip_lock=True,
|
|
)
|
|
assert not result.ok
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_asset_metadata_write_fails(monkeypatch):
|
|
async def _write_row(db, asset_id, metadata_result, *, filepath):
|
|
return Result.Err("FAIL", "write error")
|
|
|
|
monkeypatch.setattr(dbo.MetadataHelpers, "write_asset_metadata_row", _write_row)
|
|
|
|
log_calls = []
|
|
db = _DB(update_ok=True)
|
|
scanner = SimpleNamespace(db=db, _log_scan_event=lambda level, msg, **kw: log_calls.append(msg))
|
|
|
|
result = await dbo.update_asset(
|
|
scanner,
|
|
asset_id=4,
|
|
file_path=Path("C:/w.png"),
|
|
mtime=5000,
|
|
size=256,
|
|
metadata_result=_make_metadata_result(),
|
|
write_metadata=True,
|
|
skip_lock=False,
|
|
)
|
|
assert result.ok
|
|
assert any("Failed" in c for c in log_calls)
|