Skip to content

Fix/issue 392 retry transient#3

Merged
rhammen merged 5 commits into
masterfrom
fix/issue-392-retry-transient
Jul 11, 2026
Merged

Fix/issue 392 retry transient#3
rhammen merged 5 commits into
masterfrom
fix/issue-392-retry-transient

Conversation

@rhammen

@rhammen rhammen commented Jul 11, 2026

Copy link
Copy Markdown
Owner

No description provided.

rhammen and others added 5 commits July 8, 2026 20:19
api.py previously had only a single integration test that requires live
login, leaving the core logic effectively uncovered offline. This adds a
fake aiohttp ClientSession and exercises the pure logic and request
machinery without credentials or network:

- request() status handling: 200 JSON, 204 bytes, invalid JSON, error
  status codes, 500 GET retry-to-exhaustion vs 500 POST immediate raise,
  401 -> token refresh -> retry
- _request_worker retry/backoff: connection errors and timeouts retried
  then surfaced as RequestConnectionError / RequestTimeoutError
- state_to_attrs: keydict mapping, Value/ValueAsString precedence, missing
  key/value skipping, excludes, duplicate-last-wins
- set_attributes: ATTR_TYPES conversion, snake_case keys, conversion-failure
  fallback, update-in-place
- is_command_valid: all resume/stop branches (incl. a characterization test
  for the int(None) issue flagged for the Phase 3 correctness cleanup)
- stream_update routing: matching charger, unknown/missing/zero-guid ids
- Zaptec mapping + poll dispatch: register/contains/qual_id, poll dispatch
  and unknown-object error

Tests are self-contained (no live-constants fixture) so they run under
SKIP_ZAPTEC_API_TEST and in CI. Lint-clean under the repo's ruff config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follows up the initial offline suite with the cheap, high-value cases,
raising api.py statement coverage from ~50% to ~66%:

- command(): named id, numeric id, authorize_charge alias, unknown-command
  error
- charger settings wrappers: set_settings (valid + unknown-key), authorize_
  charge, set_permanent_cable_lock, set_hmi_brightness (URL + payload)
- installation current setters: set_limit_current (availableCurrent,
  missing-arg, partial-phase, out-of-range) and
  set_three_to_one_phase_switch_current (valid + out-of-range)
- Charger.poll_info (happy, 403 -> charger-list fallback, non-403 re-raise)
  and poll_state (happy, 403 ignored)
- Installation.poll_info SupportGroup logo stripping
- login()/_refresh_token: token stored and sent on later requests; 400 ->
  AuthenticationError
- small accessors/lifecycle: is_charging, model/model_prefix, Zaptec
  objects/installations/chargers/iter/len, async context manager

Payload validation is bypassed in the poll tests (it has its own test
module). build()/streaming/poll_firmware remain uncovered and are better
tested alongside the Phase 2 typed-model work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The token/OAuth request and every other API call previously raised on the
first transient HTTP error: the retry loop in _request_worker only re-tried
TimeoutError/ClientConnectionError, and _refresh_token raised immediately on
any non-200/400 status. A single 503 from api.zaptec.com on the token POST
was therefore fatal (issue custom-components#392).

Treat 429/502/503/504 as retryable in _request_worker for all methods, using
the existing exponential backoff and honoring a Retry-After header when
present. On the final attempt the response is still yielded so the caller
raises its normal RequestError(status). This covers both _refresh_token and
request() from one place. The existing 500-on-GET-only behavior is unchanged,
so non-idempotent POST/PUT are never double-executed on a 500.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
)

A transient RequestError during setup login was mapped to ConfigEntryError,
which Home Assistant treats as permanent (no auto-retry) -- so a 503 on the
token endpoint required a full HA restart to recover.

Extract _config_entry_error(), which maps authentication failures to
ConfigEntryAuthFailed and connection/timeout errors plus transient server
statuses (429/502/503/504) to ConfigEntryNotReady, letting HA retry setup
automatically. All other API errors remain permanent ConfigEntryError.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Satisfies the new enforcing ruff check CI step (excludes api.py):
D209 in _config_entry_error's docstring and PLR2004 magic values
(2, 2.0) in the transient-retry offline tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rhammen
rhammen merged commit 2c2cb5c into master Jul 11, 2026
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.

1 participant