Files
DemonGatanjieu-Anomalous_Mo…/tests/test_file_identity.py
T
DemonGatanjieuandClaude Opus 5.5 7b09909e5b test: publish the plugin's tests, one command to run them
tests/ (Python backend tests, .mjs frontend rule tests) is no longer
ignored. tools/run_tests.mjs runs the structure check, every
tests/*.mjs and the Python tests with $PYTHON, the portable
python_embeded or python. docs/guides/testing.md says how to run and
write them; README links it. Developer notes no longer point at 18 test
files that were removed earlier (AGENTS.md table, backend, frontend and
update-guide notes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 17:47:35 +08:00

65 lines
3.5 KiB
Python

import hashlib
import json
from pathlib import Path
import struct
import subprocess
import sys
import tempfile
import unittest
from unittest import mock
PLUGIN = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])]
from api import metadata, models, recipes
from model_identity import computed_file_identity
class FileIdentityTests(unittest.TestCase):
def test_header_digests_are_not_file_identity(self):
for key in ('modelspec.hash.blake3', 'modelspec.hash.sha256', 'modelspec.hash_sha256'):
with self.subTest(key=key), tempfile.TemporaryDirectory() as directory:
path = Path(directory, 'fixture.safetensors')
header = json.dumps({'__metadata__': {key: 'a' * 64}}).encode()
path.write_bytes(struct.pack('<Q', len(header)) + header + b'payload')
self.assertFalse(metadata.get_metadata(str(path))['hash'])
with mock.patch.object(recipes, '_resolve_exact_model_reference', return_value={'path': str(path)}):
identity, _ = recipes._identity_for_reference(path.name)
self.assertEqual(identity['status'], 'unverified')
result = models._resolve_from_candidates([{'type': 'vae', 'filename': path.name, 'path': str(path), 'size': path.stat().st_size}], 'a' * 64, path.stat().st_size, require_hash=True)
self.assertFalse(result['found'])
def test_legacy_offline_hash_requires_reverification_and_keeps_notes(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory, 'fixture.safetensors')
path.write_bytes(b'payload')
info = path.with_suffix('.info')
info.write_text(json.dumps({'id': -1, 'anomalous_custom_notes': 'Keep me', 'files': [{'hashes': {'SHA256': 'a' * 64}}]}))
self.assertFalse(metadata.get_metadata(str(path))['hash'])
digest = hashlib.sha256(path.read_bytes()).hexdigest()
models._compute_and_save_fallback_info(str(path), digest)
self.assertEqual(metadata.get_metadata(str(path))['hash'], digest)
self.assertEqual(json.loads(info.read_text())['anomalous_custom_notes'], 'Keep me')
def test_computed_record_invalidated_after_file_changes(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory, 'fixture.safetensors')
path.write_bytes(b'payload')
digest = hashlib.sha256(path.read_bytes()).hexdigest()
path.with_suffix('.info').write_text(json.dumps({'anomalous_file_identity': computed_file_identity(path, digest)}))
self.assertEqual(metadata.get_metadata(str(path))['hash'], digest)
path.write_bytes(b'changed payload')
self.assertFalse(metadata.get_metadata(str(path))['hash'])
def test_offline_scan_persists_full_file_sha256(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory, 'fixture.safetensors')
header = json.dumps({'__metadata__': {'modelspec.hash.blake3': 'a' * 64}}).encode()
path.write_bytes(struct.pack('<Q', len(header)) + header + b'payload')
result = subprocess.run([sys.executable, str(PLUGIN / 'scraper.py'), directory, '--offline-only', '--skip-media', '--skip-rename'], capture_output=True)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(metadata.get_metadata(str(path))['hash'], hashlib.sha256(path.read_bytes()).hexdigest())
if __name__ == '__main__':
unittest.main()