Files
ComfyUI-Lora-Manager/tests/services/test_model_scanner_cache_validation.py
Will Miao 778ad8abd2 feat(cache): add cache health monitoring and validation system, see #730
- Add cache entry validator service for data integrity checks
- Add cache health monitor service for periodic health checks
- Enhance model cache and scanner with validation support
- Update websocket manager for health status broadcasting
- Add initialization banner service for cache health alerts
- Add comprehensive test coverage for new services
- Update translations across all locales
- Refactor sync translation keys script
2026-02-02 08:30:59 +08:00

168 lines
5.4 KiB
Python

"""
Integration tests for cache validation in ModelScanner
"""
import pytest
import asyncio
from py.services.model_scanner import ModelScanner
from py.services.cache_entry_validator import CacheEntryValidator
from py.services.cache_health_monitor import CacheHealthMonitor, CacheHealthStatus
@pytest.mark.asyncio
async def test_model_scanner_validates_cache_entries(tmp_path_factory):
"""Test that ModelScanner validates cache entries during initialization"""
# Create temporary test data
tmp_dir = tmp_path_factory.mktemp("test_loras")
# Create test files
test_file = tmp_dir / "test_model.safetensors"
test_file.write_bytes(b"fake model data" * 100)
# Mock model scanner (we can't easily instantiate a full scanner in tests)
# Instead, test the validation logic directly
entries = [
{
'file_path': str(test_file),
'sha256': 'abc123def456',
'file_name': 'test_model.safetensors',
},
{
'file_path': str(tmp_dir / 'invalid.safetensors'),
# Missing sha256 - invalid
},
]
valid, invalid = CacheEntryValidator.validate_batch(entries, auto_repair=True)
assert len(valid) == 1
assert len(invalid) == 1
assert valid[0]['sha256'] == 'abc123def456'
@pytest.mark.asyncio
async def test_model_scanner_detects_degraded_cache():
"""Test that ModelScanner detects degraded cache health"""
# Create 100 entries with 2% corruption
entries = [
{
'file_path': f'/models/test{i}.safetensors',
'sha256': f'hash{i}',
}
for i in range(98)
]
# Add 2 invalid entries
entries.append({'file_path': '/models/invalid1.safetensors'})
entries.append({'file_path': '/models/invalid2.safetensors'})
monitor = CacheHealthMonitor()
report = monitor.check_health(entries, auto_repair=True)
assert report.status == CacheHealthStatus.DEGRADED
assert report.invalid_entries == 2
assert report.valid_entries == 98
@pytest.mark.asyncio
async def test_model_scanner_detects_corrupted_cache():
"""Test that ModelScanner detects corrupted cache health"""
# Create 100 entries with 10% corruption
entries = [
{
'file_path': f'/models/test{i}.safetensors',
'sha256': f'hash{i}',
}
for i in range(90)
]
# Add 10 invalid entries
for i in range(10):
entries.append({'file_path': f'/models/invalid{i}.safetensors'})
monitor = CacheHealthMonitor()
report = monitor.check_health(entries, auto_repair=True)
assert report.status == CacheHealthStatus.CORRUPTED
assert report.invalid_entries == 10
assert report.valid_entries == 90
@pytest.mark.asyncio
async def test_model_scanner_removes_invalid_from_hash_index():
"""Test that ModelScanner removes invalid entries from hash index"""
from py.services.model_hash_index import ModelHashIndex
# Create a hash index with some entries
hash_index = ModelHashIndex()
valid_entry = {
'file_path': '/models/valid.safetensors',
'sha256': 'abc123',
}
invalid_entry = {
'file_path': '/models/invalid.safetensors',
'sha256': '', # Empty sha256
}
# Add entries to hash index
hash_index.add_entry(valid_entry['sha256'], valid_entry['file_path'])
hash_index.add_entry(invalid_entry['sha256'], invalid_entry['file_path'])
# Verify both entries are in the index (using get_hash method)
assert hash_index.get_hash(valid_entry['file_path']) == valid_entry['sha256']
# Invalid entry won't be added due to empty sha256
assert hash_index.get_hash(invalid_entry['file_path']) is None
# Simulate removing invalid entry (it's not actually there, but let's test the method)
hash_index.remove_by_path(
CacheEntryValidator.get_file_path_safe(invalid_entry),
CacheEntryValidator.get_sha256_safe(invalid_entry)
)
# Verify valid entry remains
assert hash_index.get_hash(valid_entry['file_path']) == valid_entry['sha256']
def test_cache_entry_validator_handles_various_field_types():
"""Test that validator handles various field types correctly"""
# Test with different field types
entry = {
'file_path': '/models/test.safetensors',
'sha256': 'abc123',
'size': 1024, # int
'modified': 1234567890.0, # float
'favorite': True, # bool
'tags': ['tag1', 'tag2'], # list
'exclude': False, # bool
}
result = CacheEntryValidator.validate(entry, auto_repair=False)
assert result.is_valid is True
assert result.repaired is False
def test_cache_health_report_serialization():
"""Test that HealthReport can be serialized to dict"""
from py.services.cache_health_monitor import HealthReport
report = HealthReport(
status=CacheHealthStatus.DEGRADED,
total_entries=100,
valid_entries=98,
invalid_entries=2,
repaired_entries=1,
invalid_paths=['/path1', '/path2'],
message="Cache issues detected"
)
result = report.to_dict()
assert result['status'] == 'degraded'
assert result['total_entries'] == 100
assert result['valid_entries'] == 98
assert result['invalid_entries'] == 2
assert result['repaired_entries'] == 1
assert result['corruption_rate'] == '2.0%'
assert len(result['invalid_paths']) == 2
assert result['message'] == "Cache issues detected"