Skip to content

test: add unit tests for ZaptecUpdateCoordinator and ZaptecBaseEntity - #394

Closed
rhammen wants to merge 12 commits into
custom-components:masterfrom
rhammen:test/coordinator-entity-coverage
Closed

test: add unit tests for ZaptecUpdateCoordinator and ZaptecBaseEntity#394
rhammen wants to merge 12 commits into
custom-components:masterfrom
rhammen:test/coordinator-entity-coverage

Conversation

@rhammen

@rhammen rhammen commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds unit tests for custom_components/zaptec/coordinator.py (ZaptecUpdateCoordinator) and custom_components/zaptec/entity.py (ZaptecBaseEntity), raising coverage on these files from ~27-29% to 100% and 98% respectively (the one remaining line in entity.py is a genuinely unreachable defensive fallback).
  • Since neither class can be meaningfully tested with bare mocks (both touch real event-loop scheduling and ConfigEntry internals), and the standard pytest-homeassistant-custom-component harness is not viable on native Windows dev setups, tests use small hand-rolled test doubles instead: a MagicMock-based fake hass with a real running event loop attached as .loop, and a minimal FakeConfigEntry class (both in tests/conftest.py). This keeps the tests fast, dependency-free, and runnable everywhere pytest runs.
  • Along the way, documents (via a test, not a production-code change) a pre-existing gap in entity.py: _handle_coordinator_update sets self._attr_available = False on a KeyUnavailableError, but ZaptecBaseEntity never overrides HA's CoordinatorEntity.available property, so that flag currently has no effect on the entity's actual reported availability. Left as a documented finding for a separate follow-up, out of scope here.

Test plan

  • pytest tests/test_coordinator.py tests/test_entity.py -v — 35 passed
  • pytest tests -q — 45 passed, 2 skipped, 22 errors (pre-existing, unrelated DNS-fixture gap in test_zconst.py/test_redact.py — see repo's dev notes)
  • ruff format --diff / ruff check clean on all touched files
  • No custom_components/** production files modified — test-only change

🤖 Generated with Claude Code

rhammen added 9 commits July 11, 2026 01:19
Covers _trigger_poll's per-object delay/refresh sequencing (charger vs
installation), the installation-triggers-tracked-children fan-out and its
untracked-child skip path, trigger_poll's no-op when there is no
zaptec_object, and the cancel-in-flight-task-before-starting-new-one race.

The final test needed two asyncio.sleep(0) yields rather than one: the
first lets the replacement task run to completion, the second lets its
add_done_callback (which clears _trigger_task) actually fire, since Task
done-callbacks are scheduled via call_soon rather than invoked
synchronously on completion. Verified deterministic across 45+ runs.
rhammen and others added 2 commits July 16, 2026 00:39
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Keep planning docs local-only rather than publishing them to the PR branch, matching the convention used on other in-flight PRs.

@sveinse sveinse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the comments for adjustments.

Making unittests for testing HA functionality like coordinator and entities is something they must have a lot of in HA. Have you checked what testing facilities exists in HA that can assist us? How does other components do this type of testing? (And I would look at gold or platinum components in HA core for inspiration.)

I'd also like @steinmn comments on the PR if that's possible. You have a different set of eyes than me.

Comment thread docs/superpowers/plans/2026-07-11-coordinator-entity-tests.md Outdated
Comment thread tests/conftest.py Outdated
Comment on lines +18 to +20
`async_create_background_task`). A real ConfigEntry pulls in HA's full
test-harness machinery, which cannot run on native Windows in this dev
environment - see CLAUDE.md's environment notes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this comment. First about mentioning Windows and second about CLAUDE.md which isn't part of this project.

Comment thread tests/test_entity.py Outdated
Comment on lines +154 to +159
# NOTE: this sets _attr_available, but ZaptecBaseEntity does not override
# the `available` property inherited from HA's CoordinatorEntity (which
# returns coordinator.last_update_success instead), so this flag currently
# has no effect on the entity's actual reported availability. This test
# documents today's real behavior, not the intended one - see the "Known
# finding" note at the top of this plan.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that should be reported as a bug?

The text refers to "known finding" note at the top of this plan. What plan?

test: address review comments on conftest/entity docstrings

- conftest.py: justify FakeConfigEntry on technical grounds
  (avoids pytest-homeassistant-custom-component) instead of referencing
  the native-Windows dev env and CLAUDE.md, which are not part of this repo.
- test_entity.py: point the availability-finding NOTE at issue custom-components#410 instead
  of a removed plan doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
@rhammen
rhammen marked this pull request as draft July 25, 2026 16:38
@rhammen

rhammen commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — fair point, and having dug into it I agree the current approach doesn't really match how gold/platinum HA integrations test.

Those integrations lean on pytest-homeassistant-custom-component: a real hass, MockConfigEntry, and tests that set the integration up through the normal async_setup path with the cloud API mocked, then assert on public state (hass.states.get(...), the entity/device registries) — often via snapshot tests. This PR instead instantiates the coordinator/entity classes directly and asserts against private methods (# noqa: SLF001 throughout), which couples the tests to internals and tests implementation rather than behavior.

The original reason for the hand-rolled mocks was that the real harness didn't run on my native-Windows dev setup (homeassistant imports fcntl unconditionally). But that's a local-dev constraint, not a project one — CI runs on Linux where the harness works fine, and the Windows issue is solvable with a small, OS-guarded compat shim.

As a concrete example of why the harness is worth it: the _attr_available gap I documented here (now filed as #410) would become a real failing assertion under a full-setup test — the entity visibly staying available when it shouldn't — instead of a passing test that only checks the internal flag.

So rather than merge this as-is, I'm going to hold this PR as a draft and open a replacement built on the pytest-homeassistant-custom-component approach. I'll apply the same strategy to #395 (platform-entity tests), which I'm also marking draft for now. Design work is in progress. Would very much welcome @steinmn's eyes on the direction.

@steinmn

steinmn commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

The original reason for the hand-rolled mocks was that the real harness didn't run on my native-Windows dev setup (homeassistant imports fcntl unconditionally). But that's a local-dev constraint, not a project one — CI runs on Linux where the harness works fine, and the Windows issue is solvable with a small, OS-guarded compat shim.

I think I mentioned it on a different PR, not sure which: Have you tried working with the dev-container defined by .devcontainer.json? This is exactly the kind of dev-setup-issues this was created to handle.

As long as you have docker running on WSL2 and your repo download location is on the wsl-filesystem (see https://endjin.com/blog/supercharge-dev-containers-on-windows for details on why), you can get just as good performance running in a dev-container as running locally. At my day-job, we recently even managed to reduce the build-time of a c-project by 80% by moving from a native Windows pipeline to a dev-container running on WSL2.

@rhammen

rhammen commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

The original reason for the hand-rolled mocks was that the real harness didn't run on my native-Windows dev setup (homeassistant imports fcntl unconditionally). But that's a local-dev constraint, not a project one — CI runs on Linux where the harness works fine, and the Windows issue is solvable with a small, OS-guarded compat shim.

I think I mentioned it on a different PR, not sure which: Have you tried working with the dev-container defined by .devcontainer.json? This is exactly the kind of dev-setup-issues this was created to handle.

As long as you have docker running on WSL2 and your repo download location is on the wsl-filesystem (see https://endjin.com/blog/supercharge-dev-containers-on-windows for details on why), you can get just as good performance running in a dev-container as running locally. At my day-job, we recently even managed to reduce the build-time of a c-project by 80% by moving from a native Windows pipeline to a dev-container running on WSL2.

Yes, I have setup the DEVcontainer this weekend and it is working.
Not sure yet I really like it against my old way of working 😃 , but you are right: it solves the native-Windows constraints, and I shouldn't pollute this repo with workarounds for my personal workflow.
I use the DEVcontainer now at least to running the testsuite, to confirm all tests pass OK.
Thanks for the heads up!

@rhammen

rhammen commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Closing this PR.
It is superseded by #414, which implements the pytest-homeassistant-custom-component harness which is preferred to gold/platinum code.

@rhammen rhammen closed this Jul 27, 2026
rhammen added a commit to rhammen/zaptec that referenced this pull request Jul 27, 2026
…omponents#300)

Standalone version of custom-components#396, rebased onto master without the custom-components#394/custom-components#395
test-coverage dependencies. Imports Zaptec's archived charge-session
history (/api/sessions/archived) into Home Assistant long-term
statistics as an external statistic (zaptec:energy_<id>), fixing the
Energy Dashboard hour-misattribution described in custom-components#300 (and custom-components#162).

Squashed from the 17 energy-statistics commits (a62dc20..b103473) of
custom-components#396. Keeps the self-contained tests (bucket_sessions_hourly / _floor_hour
and the archived-sessions API client); omits the ZaptecStatisticsCoordinator
async tests and test_manager.py, which depend on fixtures introduced by
the (separate, being-redesigned) custom-components#394/custom-components#395, plus the internal
implementation-plan doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants