Skip to content

fix(tests): wait for the attach instead of assuming it, and unify the inbound Indigo fakes (#304, #306) - #311

Merged
simons-plugins merged 1 commit into
mainfrom
fix/304-306-test-hygiene
Aug 25, 2026
Merged

fix(tests): wait for the attach instead of assuming it, and unify the inbound Indigo fakes (#304, #306)#311
simons-plugins merged 1 commit into
mainfrom
fix/304-306-test-hygiene

Conversation

@simons-plugins

Copy link
Copy Markdown
Owner

Two test-hygiene fixes. Both were found by the CI gate added in #305 — the first one within minutes of turning it on.

Closes #304. Closes #306.

#304 — four tests asserted on state they never waited for

task = asyncio.create_task(client.run())
await client.wait_connected(timeout=2)
assert client.status.endpoint_count == 0     # AttributeError: NoneType on 3.11

wait_connected is transport-level (ws_json_client, _connected_event). The attach that populates status and sets _attached lands later, on the run loop. On 3.13 the loop happened to have run it by the time the assertion executed; on 3.11 it had not.

The tests passed on 3.13 by luck. Pre-existing — confirmed failing at 913ad44 — and invisible until now precisely because no CI ran the suite and everyone develops on 3.13.

Fix

BridgeClient gains wait_attached(timeout=...), mirroring wait_connected: an asyncio.Event set at all three sites where _attached becomes True, cleared in _mark_disconnected beside the existing reset.

It follows _attached, not the attached property. That property is _attached and connected because an endpoint_map_invalid refusal leaves a live socket serving nothing — the distinction is deliberate and stays.

No assertion changed. Only what the four tests await:

-            await client.wait_connected(timeout=2)
+            await client.wait_attached(timeout=2)

×4, and nothing else in that file. A fix that needed an assertion edited would have been the wrong fix. The other 28 wait_connected sites are untouched — they test transport state and already passed on 3.11.

CI matrix restored

python-version: ["3.11", "3.13"]. 3.11 is the floor pyproject.toml declares and pylint targets; 3.13 is what jarvis runs. Both green — 3.11 for the first time.

#306 — a drifted copy of the inbound Indigo fakes

test_generic_switch.py carried its own FakeDev/FakeDevices/FakeDeviceFactory/FakeFolderFactory under a header reading "Helpers shared with DeviceSync tests". They were not shared, and had already lost the richer version's Supports* state seeding, replacePluginPropsOnServer simulation, fail_replace rollback, stateListOrDisplayStateIdChanged counting, the ADR-0009 device-group model, and real folder creation.

Fix

Authoritative versions move to a new tests/indigo_fakes.py — not fakes.py, which holds the export-side static doubles. The two families must not be conflated (one is stateful by necessity, the other deliberately isn't), and a separate module makes that structurally unmissable instead of relying on a comment banner.

Grep found two further importers reaching into test_device_sync directly — test_power_source.py and test_integration.py — both repointed.

One thing merged rather than moved: initial_states, which only the generic_switch copy had. It seeds matterButton's lastButtonEvent/pressCount, derived from neither Supports* nor the static state table; dropping it would have broken that suite.

Nothing failed once generic_switch used the richer fake. Every mechanism the copy lacked is inert for that suite — so the drift was latent, not already producing wrong answers. It would not have stayed that way.

Numbers

3873 passing on both 3.11 and 3.13. pylint 9.48, unchanged. tests/fakes.py untouched.

Net −35 lines: test_device_sync.py −304, test_generic_switch.py −80, new module +322.

Version 2026.28.8. No [release] marker.

🤖 Generated with Claude Code

… inbound Indigo fakes (#304, #306)

Two test-hygiene fixes, both found by the CI gate added in #305.

**#304 — four tests asserted on state they never waited for.**

`TestReattach::test_attach_on_a_live_connection_updates_the_status` and three
`set_state` siblings did `await client.wait_connected(...)` and then read
`client.status` / expected an attach-dependent log line. `wait_connected` is
transport-level (`ws_json_client`, `_connected_event`); the ATTACH that
populates `status` and sets `_attached` lands later on the run loop. On 3.13
the loop happened to have run it by the assertion, on 3.11 it had not — so the
tests passed on 3.13 by luck. Pre-existing, confirmed failing at 913ad44, and
invisible until now precisely because no CI ran the suite and everyone
develops on 3.13.

`BridgeClient` gains `wait_attached(timeout=...)`, mirroring `wait_connected`:
an `asyncio.Event` set at all three sites where `_attached` becomes True and
cleared in `_mark_disconnected` beside the existing reset. It follows
`_attached`, NOT the `attached` property — that property is
`_attached and connected` because an `endpoint_map_invalid` refusal leaves a
live socket serving nothing, and the distinction is deliberate.

The four tests now await that. **No assertion changed** — only what they wait
for; a fix that needed an assertion edited would have been the wrong fix. The
other 28 `wait_connected` sites are untouched: they test transport state and
already passed on 3.11.

`.github/workflows/tests.yml` restores `python-version: ["3.11", "3.13"]`.
3.11 is the floor `pyproject.toml` declares and pylint targets; 3.13 is what
jarvis runs. Both green now — 3.11 for the first time.

**#306 — a drifted copy of the inbound Indigo fakes.**

`test_generic_switch.py` carried its own `FakeDev`/`FakeDevices`/
`FakeDeviceFactory`/`FakeFolderFactory` under a header reading "Helpers shared
with DeviceSync tests". They were not shared, and had already lost the richer
version's `Supports*` state seeding, `replacePluginPropsOnServer` simulation,
`fail_replace` rollback, `stateListOrDisplayStateIdChanged` counting, the
ADR-0009 device-group model, and real folder creation.

The authoritative versions move to `tests/indigo_fakes.py` — a new module
rather than `fakes.py`, because `fakes.py` holds the EXPORT-side static
doubles and the two families must not be conflated; a separate module makes
that structurally unmissable instead of relying on a banner. `test_device_sync`,
`test_generic_switch`, and two further importers found by grep
(`test_power_source`, `test_integration`, which were reaching into
`test_device_sync` directly) now all import from it.

One thing merged rather than moved: `initial_states`, which only the
generic_switch copy had, seeds `matterButton`'s `lastButtonEvent`/`pressCount`
— values derived from neither `Supports*` nor the static state table. Dropping
it would have broken that suite.

Nothing failed once generic_switch used the richer fake: every mechanism the
copy lacked is inert for that suite, so the drift was latent rather than
already causing wrong answers. It would not have stayed that way.

3873 passing on BOTH 3.11 and 3.13; pylint 9.48, unchanged.

Closes #304
Closes #306

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dc4cee2d-8598-42fb-8f57-08d9db25d9ea


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@simons-plugins
simons-plugins merged commit d862df3 into main Aug 25, 2026
7 checks passed
@simons-plugins
simons-plugins deleted the fix/304-306-test-hygiene branch August 25, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant