Files
MajoorWaldi-ComfyUI-Majoor-…/tests/core/test_integration.py
T
Your Name 7183c87400 Refactor filtersController to manage event listeners with lifecycle support
- Introduced `addManagedListener` function to handle event listeners with automatic cleanup.
- Updated event listeners for various filter controls to use the new management function.
- Added lifecycle signal support for event listeners to ensure proper disposal.
- Implemented cleanup logic to remove event listeners when no longer needed.

---

Update database schema handling to support async operations

- Changed `db.has_table` to `db.ahas_table` for async compatibility.
- Updated related database checks to use the new async method.

---

Refactor SQLite adapter to prioritize async operations

- Transitioned public API to expose async methods as primary.
- Removed synchronous methods and replaced them with async counterparts.
- Updated internal transaction handling to support async context management.
- Enhanced error handling and logging for async operations.

---

Add async support for FFProbe tool

- Implemented `aread` and `aread_batch` methods for asynchronous metadata reading.
- Utilized asyncio subprocess for non-blocking execution of ffprobe commands.

---

Enhance IndexScanner with I/O throttling

- Introduced `SCAN_IOPS_LIMIT` to control I/O operations during directory scans.
- Implemented pacing logic to manage scan I/O operations effectively.

---

Update MetadataService to utilize async reading methods

- Refactored methods to call async variants of exif and ffprobe reading.
- Improved error handling for async operations.

---

Implement path resolution checks in asset routes

- Added `_is_resolved_path_allowed` function to enforce allowed roots for file paths.
- Updated asset route handlers to validate resolved paths against allowed roots.

---

Refactor database maintenance routes for async handling

- Changed database close operations to use async `aclose` method.
- Updated error handling during database maintenance operations.

---

Update scan routes to utilize async database vacuuming

- Replaced synchronous database vacuum calls with async `avacuum` method.

---

Modify tests to support async database operations

- Updated test examples to use async context management for database interactions.
- Refactored integration tests to ensure proper async cleanup of database connections.
- Adjusted regression tests to count async transaction invocations correctly.
2026-02-16 01:05:48 +01:00

156 lines
5.0 KiB
Python

"""
Integration test - validates full service stack.
Tests: DI, services, metadata extraction on real files.
"""
import sys
from pathlib import Path
import pytest
# Fix Windows console encoding
if sys.platform == "win32":
import os
os.system("")
sys.stdout.reconfigure(encoding='utf-8')
from mjr_am_shared import get_logger, log_success
logger = get_logger(__name__)
@pytest.mark.asyncio
async def test_services_build(services):
"""Test service container builds successfully."""
logger.info("Testing service container...")
# Verify all services present
assert "db" in services
assert "exiftool" in services
assert "ffprobe" in services
assert "metadata" in services
assert "health" in services
log_success(logger, "Service container built successfully!")
@pytest.mark.asyncio
async def test_health_service(services):
"""Test health service."""
logger.info("Testing health service...")
health = services["health"]
# Get status
status_result = await health.status()
assert status_result.ok
logger.info(f"Health status: {status_result.data['overall']}")
logger.info(f"ExifTool available: {status_result.data['tools']['exiftool']['available']}")
logger.info(f"ffprobe available: {status_result.data['tools']['ffprobe']['available']}")
# Get counters
counters_result = await health.get_counters()
assert counters_result.ok
logger.info(f"Total assets: {counters_result.data.get('total')}")
log_success(logger, "Health service works!")
@pytest.mark.asyncio
async def test_metadata_service(services):
"""Test metadata extraction on real files."""
logger.info("Testing metadata service...")
metadata_service = services["metadata"]
parser_root = Path(__file__).resolve().parents[1] / "parser"
png_files = sorted([p for p in parser_root.rglob("*.png") if p.is_file()])
webp_files = sorted([p for p in parser_root.rglob("*.webp") if p.is_file()])
mp4_files = sorted([p for p in parser_root.rglob("*.mp4") if p.is_file()])
# Test PNG file
if png_files:
png_file = png_files[0]
logger.info(f"Testing PNG: {png_file.name}")
result = metadata_service.get_metadata(str(png_file))
if result.ok:
logger.info(f" Quality: {result.meta.get('quality')}")
logger.info(f" Has workflow: {result.data.get('workflow') is not None}")
logger.info(f" Has parameters: {result.data.get('parameters') is not None}")
log_success(logger, "PNG metadata extracted!")
else:
logger.warning(f" Failed: {result.error}")
# Test WEBP file
if webp_files:
webp_file = webp_files[0]
logger.info(f"Testing WEBP: {webp_file.name}")
result = metadata_service.get_metadata(str(webp_file))
if result.ok:
logger.info(f" Quality: {result.meta.get('quality')}")
logger.info(f" Has workflow: {result.data.get('workflow') is not None}")
log_success(logger, "WEBP metadata extracted!")
else:
logger.warning(f" Failed: {result.error}")
# Test MP4 file
if mp4_files:
mp4_file = mp4_files[0]
logger.info(f"Testing MP4: {mp4_file.name}")
result = metadata_service.get_metadata(str(mp4_file))
if result.ok:
logger.info(f" Quality: {result.meta.get('quality')}")
logger.info(f" Has workflow: {result.data.get('workflow') is not None}")
logger.info(f" Duration: {result.data.get('duration')}")
logger.info(f" Resolution: {result.data.get('resolution')}")
log_success(logger, "MP4 metadata extracted!")
else:
logger.warning(f" Failed: {result.error}")
def main():
"""Run integration tests."""
logger.info("=" * 60)
logger.info("🚀 Majoor Assets Manager - Integration Test")
logger.info("=" * 60)
print()
try:
# Build services (manual run uses direct build)
from mjr_am_backend.deps import build_services
import asyncio
services_res = asyncio.run(build_services("test_integration.db"))
if not services_res.ok:
raise RuntimeError(services_res.error or "Failed to build services")
services = services_res.data
print()
# Test health
test_health_service(services)
print()
# Test metadata extraction
test_metadata_service(services)
print()
log_success(logger, "All integration tests passed!")
logger.info("=" * 60)
# Cleanup
asyncio.run(services["db"].aclose())
test_db = Path("test_integration.db")
if test_db.exists():
test_db.unlink()
Path("test_integration.db-shm").unlink(missing_ok=True)
Path("test_integration.db-wal").unlink(missing_ok=True)
return 0
except Exception as e:
logger.error(f"Integration test failed: {e}", exc_info=True)
return 1
if __name__ == "__main__":
sys.exit(main())