mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 22:52:12 -03:00
feat: add cleanup example image folders functionality and UI integration
This commit is contained in:
@@ -27,6 +27,7 @@ class ExampleImagesHarness:
|
||||
download_manager: "StubDownloadManager"
|
||||
processor: "StubExampleImagesProcessor"
|
||||
file_manager: "StubExampleImagesFileManager"
|
||||
cleanup_service: "StubExampleImagesCleanupService"
|
||||
controller: ExampleImagesRoutes
|
||||
|
||||
|
||||
@@ -88,6 +89,21 @@ class StubExampleImagesFileManager:
|
||||
return web.json_response({"operation": "has_images", "query": dict(request.query)})
|
||||
|
||||
|
||||
class StubExampleImagesCleanupService:
|
||||
def __init__(self) -> None:
|
||||
self.calls: List[Dict[str, Any]] = []
|
||||
self.result: Dict[str, Any] = {
|
||||
"success": True,
|
||||
"moved_total": 0,
|
||||
"moved_empty_folders": 0,
|
||||
"moved_orphaned_folders": 0,
|
||||
}
|
||||
|
||||
async def cleanup_example_image_folders(self) -> Dict[str, Any]:
|
||||
self.calls.append({})
|
||||
return self.result
|
||||
|
||||
|
||||
class StubWebSocketManager:
|
||||
def __init__(self) -> None:
|
||||
self.broadcast_calls: List[Dict[str, Any]] = []
|
||||
@@ -103,6 +119,7 @@ async def example_images_app() -> ExampleImagesHarness:
|
||||
download_manager = StubDownloadManager()
|
||||
processor = StubExampleImagesProcessor()
|
||||
file_manager = StubExampleImagesFileManager()
|
||||
cleanup_service = StubExampleImagesCleanupService()
|
||||
ws_manager = StubWebSocketManager()
|
||||
|
||||
controller = ExampleImagesRoutes(
|
||||
@@ -110,6 +127,7 @@ async def example_images_app() -> ExampleImagesHarness:
|
||||
download_manager=download_manager,
|
||||
processor=processor,
|
||||
file_manager=file_manager,
|
||||
cleanup_service=cleanup_service,
|
||||
)
|
||||
|
||||
app = web.Application()
|
||||
@@ -125,6 +143,7 @@ async def example_images_app() -> ExampleImagesHarness:
|
||||
download_manager=download_manager,
|
||||
processor=processor,
|
||||
file_manager=file_manager,
|
||||
cleanup_service=cleanup_service,
|
||||
controller=controller,
|
||||
)
|
||||
finally:
|
||||
@@ -255,6 +274,23 @@ async def test_file_routes_delegate_to_file_manager():
|
||||
]
|
||||
|
||||
|
||||
async def test_cleanup_route_delegates_to_service():
|
||||
async with example_images_app() as harness:
|
||||
harness.cleanup_service.result = {
|
||||
"success": True,
|
||||
"moved_total": 2,
|
||||
"moved_empty_folders": 1,
|
||||
"moved_orphaned_folders": 1,
|
||||
}
|
||||
|
||||
response = await harness.client.post("/api/lm/cleanup-example-image-folders")
|
||||
body = await response.json()
|
||||
|
||||
assert response.status == 200
|
||||
assert body == harness.cleanup_service.result
|
||||
assert len(harness.cleanup_service.calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_download_handler_methods_delegate() -> None:
|
||||
class Recorder:
|
||||
@@ -337,15 +373,20 @@ async def test_management_handler_methods_delegate() -> None:
|
||||
return "delete"
|
||||
|
||||
recorder = Recorder()
|
||||
cleanup_service = StubExampleImagesCleanupService()
|
||||
use_case = StubImportUseCase()
|
||||
handler = ExampleImagesManagementHandler(use_case, recorder)
|
||||
handler = ExampleImagesManagementHandler(use_case, recorder, cleanup_service)
|
||||
request = object()
|
||||
|
||||
import_response = await handler.import_example_images(request)
|
||||
assert json.loads(import_response.text) == {"status": "imported"}
|
||||
assert await handler.delete_example_image(request) == "delete"
|
||||
cleanup_service.result = {"success": True}
|
||||
cleanup_response = await handler.cleanup_example_image_folders(request)
|
||||
assert json.loads(cleanup_response.text) == {"success": True}
|
||||
assert use_case.requests == [request]
|
||||
assert recorder.calls == [("delete_custom_image", request)]
|
||||
assert len(cleanup_service.calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -403,7 +444,8 @@ def test_handler_set_route_mapping_includes_all_handlers() -> None:
|
||||
return {}
|
||||
|
||||
download = ExampleImagesDownloadHandler(DummyUseCase(), DummyManager())
|
||||
management = ExampleImagesManagementHandler(DummyUseCase(), DummyProcessor())
|
||||
cleanup_service = StubExampleImagesCleanupService()
|
||||
management = ExampleImagesManagementHandler(DummyUseCase(), DummyProcessor(), cleanup_service)
|
||||
files = ExampleImagesFileHandler(object())
|
||||
handler_set = ExampleImagesHandlerSet(
|
||||
download=download,
|
||||
@@ -421,6 +463,7 @@ def test_handler_set_route_mapping_includes_all_handlers() -> None:
|
||||
"force_download_example_images",
|
||||
"import_example_images",
|
||||
"delete_example_image",
|
||||
"cleanup_example_image_folders",
|
||||
"open_example_images_folder",
|
||||
"get_example_image_files",
|
||||
"has_example_images",
|
||||
|
||||
86
tests/services/test_example_images_cleanup_service.py
Normal file
86
tests/services/test_example_images_cleanup_service.py
Normal file
@@ -0,0 +1,86 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from py.services.example_images_cleanup_service import ExampleImagesCleanupService
|
||||
from py.services.service_registry import ServiceRegistry
|
||||
from py.services.settings_manager import settings
|
||||
|
||||
|
||||
class StubScanner:
|
||||
def __init__(self, valid_hashes: set[str] | None = None) -> None:
|
||||
self._valid_hashes = valid_hashes or set()
|
||||
|
||||
def has_hash(self, value: str) -> bool:
|
||||
return value in self._valid_hashes
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cleanup_moves_empty_and_orphaned(tmp_path, monkeypatch):
|
||||
service = ExampleImagesCleanupService()
|
||||
|
||||
previous_path = settings.get('example_images_path')
|
||||
settings.settings['example_images_path'] = str(tmp_path)
|
||||
|
||||
try:
|
||||
empty_folder = tmp_path / 'empty_folder'
|
||||
empty_folder.mkdir()
|
||||
|
||||
orphan_hash = 'a' * 64
|
||||
orphan_folder = tmp_path / orphan_hash
|
||||
orphan_folder.mkdir()
|
||||
(orphan_folder / 'image.png').write_text('data', encoding='utf-8')
|
||||
|
||||
valid_hash = 'b' * 64
|
||||
valid_folder = tmp_path / valid_hash
|
||||
valid_folder.mkdir()
|
||||
(valid_folder / 'image.png').write_text('data', encoding='utf-8')
|
||||
|
||||
matching_scanner = StubScanner({valid_hash})
|
||||
empty_scanner = StubScanner()
|
||||
|
||||
async def get_matching_scanner(*_args, **_kwargs):
|
||||
return matching_scanner
|
||||
|
||||
async def get_empty_scanner(*_args, **_kwargs):
|
||||
return empty_scanner
|
||||
|
||||
monkeypatch.setattr(ServiceRegistry, 'get_lora_scanner', get_matching_scanner)
|
||||
monkeypatch.setattr(ServiceRegistry, 'get_checkpoint_scanner', get_empty_scanner)
|
||||
monkeypatch.setattr(ServiceRegistry, 'get_embedding_scanner', get_empty_scanner)
|
||||
|
||||
result = await service.cleanup_example_image_folders()
|
||||
|
||||
deleted_bucket = Path(result['deleted_root'])
|
||||
assert result['success'] is True
|
||||
assert result['moved_total'] == 2
|
||||
assert not empty_folder.exists()
|
||||
assert not (deleted_bucket / 'empty_folder').exists()
|
||||
assert (deleted_bucket / orphan_hash).exists()
|
||||
assert not orphan_folder.exists()
|
||||
assert valid_folder.exists()
|
||||
|
||||
finally:
|
||||
if previous_path is None:
|
||||
settings.settings.pop('example_images_path', None)
|
||||
else:
|
||||
settings.settings['example_images_path'] = previous_path
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cleanup_handles_missing_path(monkeypatch):
|
||||
service = ExampleImagesCleanupService()
|
||||
|
||||
previous_path = settings.get('example_images_path')
|
||||
settings.settings.pop('example_images_path', None)
|
||||
|
||||
try:
|
||||
result = await service.cleanup_example_image_folders()
|
||||
finally:
|
||||
if previous_path is not None:
|
||||
settings.settings['example_images_path'] = previous_path
|
||||
|
||||
assert result['success'] is False
|
||||
assert result['error_code'] == 'path_not_configured'
|
||||
Reference in New Issue
Block a user