Why This Matters
link_sub_integrations (util.py:287-305) writes filesystem symlinks into <config_dir>/custom_components/ based on what's bundled inside Spook, and the consequence in __init__.py:42-79 is one of three outcomes: restart HA immediately, listen for EVENT_HOMEASSISTANT_START/STARTED to restart later, or raise a repair issue. That is the single most operationally dangerous code path in the integration — it both mutates the user's HA config directory and forces a process restart. It is also the path most likely to break across HA versions and across host filesystems (containers, symlink-hostile FS, read-only configs). Testing it in PHACC's tmp_path config dir is straightforward and removes a class of "works on my machine" bugs.
Approach
Add tests/test_setup.py. Use PHACC's hass.config.config_dir (already a tmp dir under the hood). Pre-populate it with custom_components/spook/integrations/<name>/manifest.json fixtures (or symlink to the real repo paths). Run async_setup_entry with MockConfigEntry and assert: (a) symlinks are created on first run, (b) a second run is a no-op (link_sub_integrations returns False), (c) unlink_sub_integrations removes them on async_remove_entry, (d) parametrize over CoreState.not_running, CoreState.starting, CoreState.running and the "Boo!" sentinel, asserting the right branch fires. Patch hass.async_stop to a recording mock so the test can assert RESTART_EXIT_CODE was requested without actually shutting HA down.
Acceptance Criteria
Risks & Caveats
The dest.symlink_to(src) call (util.py:303) targets an absolute path that may not exist inside the test's tmp config dir — you'll need to either create the source tree under tmp or stub the source path. Symlinks on Windows runners are nontrivial; if CI ever expands beyond ubuntu-latest, this test will need pytest.mark.skipif(sys.platform == "win32"). The restart-listener path is the trickiest to assert; consider using async_fire_time_changed + bus event simulation.
Scores
- Impact: ████████░░ 8/10
- Difficulty: ████████░░ 8/10
- Short-Term ROI: ████░░░░░░ 4/10
- Long-Term Value: █████████░ 9/10
Priority
Research Further
Dependencies
#1257, #1260
Why This Matters
link_sub_integrations(util.py:287-305) writes filesystem symlinks into<config_dir>/custom_components/based on what's bundled inside Spook, and the consequence in__init__.py:42-79is one of three outcomes: restart HA immediately, listen forEVENT_HOMEASSISTANT_START/STARTEDto restart later, or raise a repair issue. That is the single most operationally dangerous code path in the integration — it both mutates the user's HA config directory and forces a process restart. It is also the path most likely to break across HA versions and across host filesystems (containers, symlink-hostile FS, read-only configs). Testing it in PHACC'stmp_pathconfig dir is straightforward and removes a class of "works on my machine" bugs.Approach
Add
tests/test_setup.py. Use PHACC'shass.config.config_dir(already a tmp dir under the hood). Pre-populate it withcustom_components/spook/integrations/<name>/manifest.jsonfixtures (or symlink to the real repo paths). Runasync_setup_entrywithMockConfigEntryand assert: (a) symlinks are created on first run, (b) a second run is a no-op (link_sub_integrationsreturns False), (c)unlink_sub_integrationsremoves them onasync_remove_entry, (d) parametrize overCoreState.not_running,CoreState.starting,CoreState.runningand the"Boo!"sentinel, asserting the right branch fires. Patchhass.async_stopto a recording mock so the test can assertRESTART_EXIT_CODEwas requested without actually shutting HA down.Acceptance Criteria
CoreState× sentinel branches in__init__.py:53-79are coveredrestart_requiredrepair issue is asserted when no auto-restart firesunsubscribe_ghost_bustersswallows aValueErrorif called twice (regression test for the explicittry/exceptat__init__.py:103-109)Risks & Caveats
The
dest.symlink_to(src)call (util.py:303) targets an absolute path that may not exist inside the test's tmp config dir — you'll need to either create the source tree under tmp or stub the source path. Symlinks on Windows runners are nontrivial; if CI ever expands beyond ubuntu-latest, this test will needpytest.mark.skipif(sys.platform == "win32"). The restart-listener path is the trickiest to assert; consider usingasync_fire_time_changed+ bus event simulation.Scores
Priority
Research Further
Dependencies
#1257, #1260