1+ """Shared test fixtures for Climate Scheduler tests.
2+
3+ We use lightweight mocking instead of the full HA test framework
4+ so these tests can run without a Home Assistant installation.
5+ """
6+ import asyncio
7+ import json
8+ from datetime import time
9+ from unittest .mock import AsyncMock , MagicMock , patch
10+ import pytest
11+
12+ # ---------------------------------------------------------------------------
13+ # Make the integration importable without HA on sys.path
14+ # ---------------------------------------------------------------------------
15+ import sys
16+ import os
17+
18+ COMP_ROOT = os .path .join (os .path .dirname (__file__ ), ".." , "custom_components" )
19+ sys .path .insert (0 , os .path .abspath (COMP_ROOT ))
20+
21+ # Stub out heavy HA imports that the integration relies on
22+ # so we can run pure-logic and storage tests without installing HA.
23+ _ha_stubs = {
24+ "homeassistant" : MagicMock (),
25+ "homeassistant.core" : MagicMock (),
26+ "homeassistant.helpers" : MagicMock (),
27+ "homeassistant.helpers.storage" : MagicMock (),
28+ "homeassistant.helpers.update_coordinator" : MagicMock (),
29+ "homeassistant.helpers.typing" : MagicMock (),
30+ "homeassistant.helpers.event" : MagicMock (),
31+ "homeassistant.config_entries" : MagicMock (),
32+ "homeassistant.const" : MagicMock (),
33+ "homeassistant.util" : MagicMock (),
34+ "homeassistant.util.dt" : MagicMock (),
35+ "homeassistant.components" : MagicMock (),
36+ "homeassistant.components.http" : MagicMock (),
37+ "homeassistant.components.climate" : MagicMock (),
38+ }
39+
40+ for mod_name , mod_obj in _ha_stubs .items ():
41+ if mod_name not in sys .modules :
42+ sys .modules [mod_name ] = mod_obj
43+
44+ # Minimal stubs for constants that the code imports
45+ from custom_components .climate_scheduler .const import (
46+ DOMAIN ,
47+ STORAGE_KEY ,
48+ STORAGE_VERSION ,
49+ MIN_TEMP ,
50+ MAX_TEMP ,
51+ NO_CHANGE_TEMP ,
52+ UPDATE_INTERVAL_SECONDS ,
53+ )
54+
55+
56+ # ---------------------------------------------------------------------------
57+ # Fake Store – in-memory replacement for HA's Store
58+ # ---------------------------------------------------------------------------
59+ class FakeStore :
60+ """In-memory storage stub that replaces homeassistant.helpers.storage.Store."""
61+
62+ def __init__ (self , hass , version , key ):
63+ self ._data = None
64+ self ._version = version
65+ self ._key = key
66+
67+ async def async_load (self ):
68+ return self ._data
69+
70+ async def async_save (self , data ):
71+ self ._data = data
72+
73+
74+ # ---------------------------------------------------------------------------
75+ # Fixtures
76+ # ---------------------------------------------------------------------------
77+ @pytest .fixture
78+ def fake_hass ():
79+ """Minimal fake HomeAssistant object."""
80+ hass = MagicMock ()
81+ hass .data = {DOMAIN : {}}
82+ hass .services = MagicMock ()
83+ hass .bus = MagicMock ()
84+ hass .states = MagicMock ()
85+ return hass
86+
87+
88+ @pytest .fixture
89+ def fake_store (fake_hass ):
90+ """Return a FakeStore instance."""
91+ return FakeStore (fake_hass , STORAGE_VERSION , STORAGE_KEY )
92+
93+
94+ @pytest .fixture
95+ def storage (fake_hass , fake_store , monkeypatch ):
96+ """Return a ScheduleStorage wired to FakeStore."""
97+ from custom_components .climate_scheduler .storage import ScheduleStorage
98+
99+ # Patch Store class so ScheduleStorage uses our fake
100+ monkeypatch .setattr (
101+ "custom_components.climate_scheduler.storage.Store" ,
102+ lambda hass , version , key : fake_store ,
103+ )
104+ s = ScheduleStorage (fake_hass )
105+ # Pre-seed empty data so async_load doesn't fail
106+ fake_store ._data = {"groups" : {}, "settings" : {}, "advance_history" : {}}
107+ return s
108+
109+
110+ def _make_storage_with_data (fake_hass , fake_store , monkeypatch , data ):
111+ """Helper: return a ScheduleStorage pre-loaded with *data*."""
112+ from custom_components .climate_scheduler .storage import ScheduleStorage
113+
114+ monkeypatch .setattr (
115+ "custom_components.climate_scheduler.storage.Store" ,
116+ lambda hass , version , key : fake_store ,
117+ )
118+ fake_store ._data = data
119+ s = ScheduleStorage (fake_hass )
120+ s ._data = data # skip async_load
121+ return s
0 commit comments