"""Pytest configuration and shared fixtures.""" from datetime import datetime, timezone import pytest from wled_controller.config import Config, StorageConfig, ServerConfig, AuthConfig from wled_controller.storage.device_store import Device, DeviceStore from wled_controller.storage.sync_clock import SyncClock from wled_controller.storage.sync_clock_store import SyncClockStore from wled_controller.storage.output_target_store import OutputTargetStore from wled_controller.storage.automation import ( Automation, ) from wled_controller.storage.automation_store import AutomationStore from wled_controller.storage.value_source_store import ValueSourceStore # --------------------------------------------------------------------------- # Directory / path fixtures # --------------------------------------------------------------------------- @pytest.fixture def test_data_dir(tmp_path): """Provide a temporary directory for test data.""" d = tmp_path / "data" d.mkdir(parents=True, exist_ok=True) return d @pytest.fixture def test_config_dir(tmp_path): """Provide a temporary directory for test configuration.""" return tmp_path / "config" @pytest.fixture def temp_store_dir(tmp_path): """Provide a temp directory for JSON store files, cleaned up after tests.""" d = tmp_path / "stores" d.mkdir(parents=True, exist_ok=True) return d # --------------------------------------------------------------------------- # Config fixtures # --------------------------------------------------------------------------- @pytest.fixture def test_config(tmp_path): """A Config instance with temp directories for all store files.""" data_dir = tmp_path / "data" data_dir.mkdir(parents=True, exist_ok=True) storage = StorageConfig( devices_file=str(data_dir / "devices.json"), templates_file=str(data_dir / "capture_templates.json"), postprocessing_templates_file=str(data_dir / "postprocessing_templates.json"), picture_sources_file=str(data_dir / "picture_sources.json"), output_targets_file=str(data_dir / "output_targets.json"), pattern_templates_file=str(data_dir / "pattern_templates.json"), color_strip_sources_file=str(data_dir / "color_strip_sources.json"), audio_sources_file=str(data_dir / "audio_sources.json"), audio_templates_file=str(data_dir / "audio_templates.json"), value_sources_file=str(data_dir / "value_sources.json"), automations_file=str(data_dir / "automations.json"), scene_presets_file=str(data_dir / "scene_presets.json"), color_strip_processing_templates_file=str(data_dir / "color_strip_processing_templates.json"), sync_clocks_file=str(data_dir / "sync_clocks.json"), ) return Config( server=ServerConfig(host="127.0.0.1", port=9999), auth=AuthConfig(api_keys={"test": "test-api-key-12345"}), storage=storage, ) # --------------------------------------------------------------------------- # Store fixtures # --------------------------------------------------------------------------- @pytest.fixture def device_store(temp_store_dir): """Provide a DeviceStore backed by a temp file.""" return DeviceStore(temp_store_dir / "devices.json") @pytest.fixture def sync_clock_store(temp_store_dir): """Provide a SyncClockStore backed by a temp file.""" return SyncClockStore(str(temp_store_dir / "sync_clocks.json")) @pytest.fixture def output_target_store(temp_store_dir): """Provide an OutputTargetStore backed by a temp file.""" return OutputTargetStore(str(temp_store_dir / "output_targets.json")) @pytest.fixture def automation_store(temp_store_dir): """Provide an AutomationStore backed by a temp file.""" return AutomationStore(str(temp_store_dir / "automations.json")) @pytest.fixture def value_source_store(temp_store_dir): """Provide a ValueSourceStore backed by a temp file.""" return ValueSourceStore(str(temp_store_dir / "value_sources.json")) # --------------------------------------------------------------------------- # Sample entity factories # --------------------------------------------------------------------------- @pytest.fixture def sample_device(): """Provide a sample device configuration dict.""" return { "id": "test_device_001", "name": "Test WLED Device", "url": "http://192.168.1.100", "led_count": 150, "enabled": True, "device_type": "wled", } @pytest.fixture def make_device(): """Factory fixture: call make_device(name=..., **overrides) to build a Device.""" _counter = 0 def _factory(name=None, **kwargs): nonlocal _counter _counter += 1 defaults = dict( device_id=f"device_test_{_counter:04d}", name=name or f"Device {_counter}", url=f"http://192.168.1.{_counter}", led_count=150, ) defaults.update(kwargs) return Device(**defaults) return _factory @pytest.fixture def make_sync_clock(): """Factory fixture: call make_sync_clock(name=..., **overrides).""" _counter = 0 def _factory(name=None, **kwargs): nonlocal _counter _counter += 1 now = datetime.now(timezone.utc) defaults = dict( id=f"sc_test_{_counter:04d}", name=name or f"Clock {_counter}", speed=1.0, created_at=now, updated_at=now, ) defaults.update(kwargs) return SyncClock(**defaults) return _factory @pytest.fixture def make_automation(): """Factory fixture: call make_automation(name=..., **overrides).""" _counter = 0 def _factory(name=None, **kwargs): nonlocal _counter _counter += 1 now = datetime.now(timezone.utc) defaults = dict( id=f"auto_test_{_counter:04d}", name=name or f"Automation {_counter}", enabled=True, condition_logic="or", conditions=[], scene_preset_id=None, deactivation_mode="none", deactivation_scene_preset_id=None, created_at=now, updated_at=now, ) defaults.update(kwargs) return Automation(**defaults) return _factory # --------------------------------------------------------------------------- # Authenticated test client # --------------------------------------------------------------------------- @pytest.fixture def authenticated_client(test_config, monkeypatch): """Provide a FastAPI TestClient with auth header pre-set. Patches global config so the app uses temp storage paths. """ import wled_controller.config as config_mod monkeypatch.setattr(config_mod, "config", test_config) from fastapi.testclient import TestClient from wled_controller.main import app client = TestClient(app, raise_server_exceptions=False) client.headers["Authorization"] = "Bearer test-api-key-12345" return client # --------------------------------------------------------------------------- # Calibration sample (kept from original conftest) # --------------------------------------------------------------------------- @pytest.fixture def sample_calibration(): """Provide a sample calibration configuration.""" return { "layout": "clockwise", "start_position": "bottom_left", "segments": [ {"edge": "bottom", "led_start": 0, "led_count": 40, "reverse": False}, {"edge": "right", "led_start": 40, "led_count": 30, "reverse": False}, {"edge": "top", "led_start": 70, "led_count": 40, "reverse": True}, {"edge": "left", "led_start": 110, "led_count": 40, "reverse": True}, ], }