From 80f01d4813d2698b99e2381eaed1177b8f0a97fd Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Sat, 25 Apr 2026 15:11:39 +0300 Subject: [PATCH] chore: harden test isolation, gitignore stale src/data, mark shutdown action done - ``tests/test_preferences_api.py`` no longer captures the auth API key at module-import time. The new ``client`` fixture resolves it inside its body and bakes the Bearer header into ``TestClient.headers``, so the e2e conftest swapping the global config singleton during collection cannot leave the test holding a stale 401-bound header. Same proven pattern as ``test_audio_processing_templates_api.py``. - ``.gitignore`` now anchors ``/server/src/data/`` defensively. If the server is launched from ``server/src/`` (uncommon but possible during ad-hoc debugging), its relative ``data/`` resolves there. Templates now live in SQLite (``capture_templates`` / ``pattern_templates`` / ``postprocessing_templates`` tables); any stale ``*.json`` that lands in that directory is a runtime export and must not be committed. - Three such stale exports were untracked at the start of the pre-merge audit and have been deleted from the working tree. - ``TODO.md`` flips the shutdown-action checklist to done and notes that real-hardware verification (WLED + serial after Ctrl+C) is still pending. --- .gitignore | 5 +++ TODO.md | 14 ++++++++ server/tests/test_preferences_api.py | 51 ++++++++++++---------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 0b5fd7e..98d679f 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,11 @@ logs/ # shipped sound assets out of the CI tag checkout. /data/ /server/data/ +# Defensive: if the server is launched from server/src/ (uncommon path), +# its relative `data/` dir resolves to server/src/data/. Templates now +# live in SQLite, so any *.json that lands here is stale runtime export +# and must not be committed. +/server/src/data/ *.db *.sqlite *.json.bak diff --git a/TODO.md b/TODO.md index a087383..ddd01e9 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,19 @@ # LedGrab TODO +## Server shutdown action + +Let user choose what happens to LED targets on server shutdown. + +- [x] Backend storage: `shutdown_action` in `db.settings` (`"stop_targets"` default | `"nothing"`) +- [x] Backend route: `GET/PUT /api/v1/system/shutdown-action` in `system_settings.py` +- [x] Backend schema: `ShutdownActionResponse/Request` in `schemas/system.py` +- [x] Backend wiring: lifespan shutdown in `main.py` reads action, passes `restore_devices` flag to `processor_manager.stop_all()` +- [x] `processor_manager.stop_all(restore_devices: bool = True)` — when False, calls public `proc.cancel_task()` (defined on `TargetProcessor`) which awaits cancellation without restoring device state; skips `_restore_device_idle_state` loop. No reach into private `_task` attribute. +- [x] Frontend: hidden `