"""Installer ohne Netzwerk; optionaler Smoke-Test mit echten Modellgewichten.""" from contextlib import redirect_stdout import hashlib import importlib.util from io import BytesIO, StringIO import os from pathlib import Path import tempfile import unittest from unittest.mock import patch from PIL import Image from halloween.detection import NanoDet class InstallerTest(unittest.TestCase): def setUp(self): spec = importlib.util.spec_from_file_location('model_installer', Path(__file__).parents[1] / 'tools/install_detection_model.py') self.installer = importlib.util.module_from_spec(spec) spec.loader.exec_module(self.installer) self.folder = tempfile.TemporaryDirectory() self.addCleanup(self.folder.cleanup) self.data = b'test-model' self.manifest = {'file': 'model.onnx', 'sha256': hashlib.sha256(self.data).hexdigest(), 'size': len(self.data), 'url': 'https://example.invalid/model'} self.target = Path(self.folder.name) / 'models/model.onnx' def install(self, data): with patch.object(self.installer.json, 'loads', return_value=self.manifest), \ patch.object(self.installer.urllib.request, 'urlopen', return_value=BytesIO(data)) as download, \ patch('sys.argv', ['installer', '--state-dir', self.folder.name]), redirect_stdout(StringIO()): self.installer.main() return download def test_verified_download_is_atomic_and_idempotent(self): self.assertEqual(self.install(self.data).call_count, 1) self.assertEqual(self.target.read_bytes(), self.data) self.assertEqual(self.install(self.data).call_count, 0) self.assertEqual(list(self.target.parent.iterdir()), [self.target]) def test_bad_download_keeps_existing_file(self): self.target.parent.mkdir() self.target.write_bytes(b'old-corrupt-model') with self.assertRaises(ValueError): self.install(b'bad-model!') self.assertEqual(self.target.read_bytes(), b'old-corrupt-model') self.assertEqual(list(self.target.parent.iterdir()), [self.target]) def test_oversized_download_is_rejected_and_cleaned(self): with self.assertRaises(ValueError): self.install(self.data+b'extra') self.assertEqual(list(self.target.parent.iterdir()), []) @unittest.skipUnless(os.environ.get('HALLOWEEN_TEST_MODEL'), 'Optionales Modell nicht angegeben') class ModelSmokeTest(unittest.TestCase): def test_real_model_outputs_and_empty_frame(self): model = NanoDet(Path(os.environ['HALLOWEEN_TEST_MODEL'])) frame = BytesIO() Image.new('RGB', (640,480)).save(frame, format='JPEG') self.assertEqual(model.detect(frame.getvalue()), []) if __name__ == '__main__': unittest.main()