mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-06 19:42:34 +08:00
Split the ~1000 line app/assets/database/queries.py into focused modules: - queries/asset.py - Asset entity queries (asset_exists_by_hash, get_asset_by_hash) - queries/asset_info.py - AssetInfo queries (~15 functions) - queries/cache_state.py - AssetCacheState queries (list_cache_states_by_asset_id, pick_best_live_path, prune_orphaned_assets, fast_db_consistency_pass) - queries/tags.py - Tag queries (8 functions including ensure_tags_exist, add/remove tag functions, list_tags_with_usage) - queries/__init__.py - Re-exports all public functions for backward compatibility Also adds comprehensive unit tests using in-memory SQLite: - tests-unit/assets_test/queries/conftest.py - Session fixture - tests-unit/assets_test/queries/test_asset.py - 5 tests - tests-unit/assets_test/queries/test_asset_info.py - 23 tests - tests-unit/assets_test/queries/test_cache_state.py - 8 tests - tests-unit/assets_test/queries/test_metadata.py - 12 tests for _apply_metadata_filter - tests-unit/assets_test/queries/test_tags.py - 23 tests All 71 unit tests pass. Existing integration tests unaffected. Amp-Thread-ID: https://ampcode.com/threads/T-019c24bb-475b-7442-9ff9-8288edea3345 Co-authored-by: Amp <amp@ampcode.com>
129 lines
4.1 KiB
Python
129 lines
4.1 KiB
Python
"""Tests for cache_state query functions."""
|
|
import os
|
|
import tempfile
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.assets.database.models import Asset, AssetCacheState, AssetInfo
|
|
from app.assets.database.queries import (
|
|
list_cache_states_by_asset_id,
|
|
pick_best_live_path,
|
|
)
|
|
from app.assets.helpers import utcnow
|
|
|
|
|
|
def _make_asset(session: Session, hash_val: str | None = None, size: int = 1024) -> Asset:
|
|
asset = Asset(hash=hash_val, size_bytes=size)
|
|
session.add(asset)
|
|
session.flush()
|
|
return asset
|
|
|
|
|
|
def _make_cache_state(
|
|
session: Session,
|
|
asset: Asset,
|
|
file_path: str,
|
|
mtime_ns: int | None = None,
|
|
needs_verify: bool = False,
|
|
) -> AssetCacheState:
|
|
state = AssetCacheState(
|
|
asset_id=asset.id,
|
|
file_path=file_path,
|
|
mtime_ns=mtime_ns,
|
|
needs_verify=needs_verify,
|
|
)
|
|
session.add(state)
|
|
session.flush()
|
|
return state
|
|
|
|
|
|
class TestListCacheStatesByAssetId:
|
|
def test_returns_empty_for_no_states(self, session: Session):
|
|
asset = _make_asset(session, "hash1")
|
|
states = list_cache_states_by_asset_id(session, asset_id=asset.id)
|
|
assert list(states) == []
|
|
|
|
def test_returns_states_for_asset(self, session: Session):
|
|
asset = _make_asset(session, "hash1")
|
|
_make_cache_state(session, asset, "/path/a.bin")
|
|
_make_cache_state(session, asset, "/path/b.bin")
|
|
session.commit()
|
|
|
|
states = list_cache_states_by_asset_id(session, asset_id=asset.id)
|
|
paths = [s.file_path for s in states]
|
|
assert set(paths) == {"/path/a.bin", "/path/b.bin"}
|
|
|
|
def test_does_not_return_other_assets_states(self, session: Session):
|
|
asset1 = _make_asset(session, "hash1")
|
|
asset2 = _make_asset(session, "hash2")
|
|
_make_cache_state(session, asset1, "/path/asset1.bin")
|
|
_make_cache_state(session, asset2, "/path/asset2.bin")
|
|
session.commit()
|
|
|
|
states = list_cache_states_by_asset_id(session, asset_id=asset1.id)
|
|
paths = [s.file_path for s in states]
|
|
assert paths == ["/path/asset1.bin"]
|
|
|
|
|
|
class TestPickBestLivePath:
|
|
def test_returns_empty_for_empty_list(self):
|
|
result = pick_best_live_path([])
|
|
assert result == ""
|
|
|
|
def test_returns_empty_when_no_files_exist(self, session: Session):
|
|
asset = _make_asset(session, "hash1")
|
|
state = _make_cache_state(session, asset, "/nonexistent/path.bin")
|
|
session.commit()
|
|
|
|
result = pick_best_live_path([state])
|
|
assert result == ""
|
|
|
|
def test_prefers_verified_path(self, session: Session, tmp_path):
|
|
"""needs_verify=False should be preferred."""
|
|
asset = _make_asset(session, "hash1")
|
|
|
|
verified_file = tmp_path / "verified.bin"
|
|
verified_file.write_bytes(b"data")
|
|
|
|
unverified_file = tmp_path / "unverified.bin"
|
|
unverified_file.write_bytes(b"data")
|
|
|
|
state_verified = _make_cache_state(
|
|
session, asset, str(verified_file), needs_verify=False
|
|
)
|
|
state_unverified = _make_cache_state(
|
|
session, asset, str(unverified_file), needs_verify=True
|
|
)
|
|
session.commit()
|
|
|
|
states = [state_unverified, state_verified]
|
|
result = pick_best_live_path(states)
|
|
assert result == str(verified_file)
|
|
|
|
def test_falls_back_to_existing_unverified(self, session: Session, tmp_path):
|
|
"""If all states need verification, return first existing path."""
|
|
asset = _make_asset(session, "hash1")
|
|
|
|
existing_file = tmp_path / "exists.bin"
|
|
existing_file.write_bytes(b"data")
|
|
|
|
state = _make_cache_state(session, asset, str(existing_file), needs_verify=True)
|
|
session.commit()
|
|
|
|
result = pick_best_live_path([state])
|
|
assert result == str(existing_file)
|
|
|
|
|
|
class TestPickBestLivePathWithMocking:
|
|
def test_handles_missing_file_path_attr(self):
|
|
"""Gracefully handle states with None file_path."""
|
|
|
|
class MockState:
|
|
file_path = None
|
|
needs_verify = False
|
|
|
|
result = pick_best_live_path([MockState()])
|
|
assert result == ""
|