Initiales Halloween-Projekt

This commit is contained in:
marcinator_2 committed 2026-10-01 03:19:17 +02:00
commit e071fbad2a
36 files changed
+2220

No files matched your search

+58
View File
@@ -0,0 +1,58 @@
import json
import tempfile
import unittest
from pathlib import Path
from halloween.controller import Controller
class ControllerTest(unittest.TestCase):
def setUp(self):
self.folder = tempfile.TemporaryDirectory()
self.addCleanup(self.folder.cleanup)
self.now = 100.0
self.path = Path(self.folder.name)
self.controller = Controller(self.path, clock=lambda: self.now)
def test_start_needs_clear_zone_and_fresh_trigger(self):
self.assertEqual(self.controller.status()["mode"], "voll")
self.assertFalse(self.controller.trigger()[0])
self.controller.zone_clear()
self.assertTrue(self.controller.trigger()[0])
self.assertFalse(self.controller.trigger()[0])
self.now += 11
self.assertFalse(self.controller.trigger()[0])
self.controller.zone_clear()
self.assertTrue(self.controller.trigger()[0])
def test_stop_latches_until_reset_and_new_clear_zone(self):
self.controller.zone_clear()
self.controller.stop()
self.assertFalse(self.controller.trigger()[0])
self.controller.reset()
self.assertFalse(self.controller.trigger()[0])
self.controller.zone_clear()
self.assertTrue(self.controller.trigger()[0])
def test_mode_persists_and_corrupt_file_fails_closed(self):
self.controller.set_mode("sanft")
self.assertEqual(Controller(self.path).mode, "sanft")
(self.path / "settings.json").write_text("{kaputt", encoding="utf-8")
broken = Controller(self.path)
self.assertEqual(broken.mode, "aus")
self.assertIsNotNone(broken.config_error)
broken.zone_clear()
self.assertFalse(broken.trigger()[0])
broken.set_mode("voll")
self.assertEqual(json.loads((self.path / "settings.json").read_text())["mode"], "voll")
def test_mode_change_cancels_running_sequence(self):
self.controller.zone_clear()
self.assertTrue(self.controller.trigger()[0])
self.controller.set_mode("aus")
self.assertFalse(self.controller.trigger()[0])
self.assertFalse(self.controller.status()["zone_clear_seen"])
if __name__ == "__main__":
unittest.main()
+54
View File
@@ -0,0 +1,54 @@
import asyncio
import tempfile
import unittest
from pathlib import Path
import httpx
from halloween.web import create_app
class WebTest(unittest.TestCase):
def test_demo_live_image_and_controls(self):
async def scenario():
with tempfile.TemporaryDirectory() as folder:
app = create_app(demo=True, state_dir=Path(folder))
app.state.camera.start()
try:
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://local") as client:
page = await client.get("/")
self.assertEqual(page.status_code, 200)
self.assertIn("/stream.mjpg", page.text)
self.assertIn("Aktoren nur simuliert", page.text)
for _ in range(30):
if (await client.get("/api/status")).json()["camera"]["ready"]:
break
await asyncio.sleep(0.05)
image = await client.get("/snapshot.jpg")
self.assertEqual(image.status_code, 200)
self.assertTrue(image.content.startswith(b"\xff\xd8"))
self.assertEqual((await client.post("/api/stop")).status_code, 403)
headers = {"X-Halloween-UI": "1"}
self.assertEqual((await client.post("/api/stop", headers=headers)).status_code, 200)
self.assertTrue((await client.get("/api/status")).json()["controller"]["stopped"])
self.assertEqual((await client.post("/api/reset", headers=headers)).status_code, 200)
result = await client.post(
"/api/mode", headers=headers, json={"mode": "sanft"}
)
self.assertEqual(result.status_code, 200)
self.assertEqual((await client.get("/api/status")).json()["controller"]["mode"], "sanft")
route = next(route for route in app.routes if route.path == "/stream.mjpg")
response = await route.endpoint()
first = await response.body_iterator.__anext__()
self.assertTrue(first.startswith(b"--frame\r\nContent-Type: image/jpeg"))
await response.body_iterator.aclose()
finally:
app.state.camera.stop()
asyncio.run(scenario())
if __name__ == "__main__":
unittest.main()