Skip to content

fix: SensorUpdate leaked mutable state across updates; add API test coverage - #137

Draft
bluetoothbot wants to merge 2 commits into
Bluetooth-Devices:mainfrom
bluetoothbot:koan/test-sensordata-coverage
Draft

fix: SensorUpdate leaked mutable state across updates; add API test coverage#137
bluetoothbot wants to merge 2 commits into
Bluetooth-Devices:mainfrom
bluetoothbot:koan/test-sensordata-coverage

Conversation

@bluetoothbot

@bluetoothbot bluetoothbot commented May 24, 2026

Copy link
Copy Markdown

What

Make each SensorUpdate an immutable snapshot, and add behavioral test coverage for the previously-untested SensorData public API.

Why

SensorUpdate is declared frozen=True, but _finish_update() returned the live per-update dicts by reference. Those dicts are reused across calls — update() calls _events_updates.clear(), and update_sensor()/update_binary_sensor() reassign entries — so a previously-returned SensorUpdate would silently mutate when the next update() ran:

  • its events got cleared out from under the caller (events are transient and .clear()-ed each cycle);
  • its entity_values / descriptions were rewritten to the newer reading.

Any consumer that holds the prior update to diff against the next one (a common pattern) would read corrupted data. This came to light while adding coverage — the demonstrating tests fail against the pre-fix commit.

How

  • _finish_update() now returns shallow dict(...) copies of each per-update mapping. The contained value/description objects are themselves frozen dataclasses, so a shallow copy is a complete point-in-time snapshot. Cost is a few small dict copies per update — negligible vs. correctness.
  • New tests/test_data.py covers title, per-device info aggregation, primary_device_id, get_device_name (name→type fallback), supported(), binary sensors, device_id propagation, and two regression guards that pin the snapshot semantics.

Structured as two commits (failing tests → fix) so the before/after is reviewable.

Testing

PYTHONPATH=src python3.12 -m pytest tests/ → 25 passed (was 8). ruff check clean. The 3 snapshot tests fail on commit 1 and pass on commit 2.


Quality Report

Changes: 2 files changed, 275 insertions(+), 6 deletions(-)

Code scan: clean

Tests: passed (25 passed)

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

bluetoothbot and others added 2 commits May 24, 2026 07:16
Adds behavioral coverage for the previously-untested SensorData surface:
title, per-device info aggregation, primary_device_id, get_device_name,
supported(), binary sensors, and device_id propagation.

Three tests intentionally FAIL against the current code, demonstrating that
SensorUpdate (frozen=True) leaks mutable instance state: _finish_update()
returns the live per-update dicts by reference, so a later update() clears
events and rewrites values on an already-returned snapshot. The next commit
fixes this.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_finish_update() returned the live per-update dicts by reference. Because
those dicts are reused across calls — update() clears _events_updates and
update_sensor() reassigns value/description entries — a previously returned
SensorUpdate would silently lose its events and have its values rewritten by
the next update(). SensorUpdate is frozen=True, so callers reasonably treat it
as an immutable snapshot.

Copy the dicts on return so each SensorUpdate is a stable point-in-time view.
The copies are shallow; the contained value/description objects are themselves
frozen dataclasses.

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

codecov Bot commented May 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.37%. Comparing base (31bd826) to head (7f437a1).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #137      +/-   ##
==========================================
+ Coverage   94.37%   99.37%   +5.00%     
==========================================
  Files          12       12              
  Lines         480      480              
  Branches        5        5              
==========================================
+ Hits          453      477      +24     
+ Misses         26        2      -24     
  Partials        1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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