"""The aircraft reports its battery twice, with different values. Observed on the live drone 2026-10-02: `BatteryStateChanged` (0-5-1) arrives as `00 05 01 00 40` on buffer 126 and `00 05 01 00 00` on buffer 127, repeatedly. The 64 is corroborated by the drone's own log and tracks a smooth discharge; the zeros appear in no log line. A store that keeps the newest frame therefore reported a healthy aircraft as flat, and preflight refused on it. """ import pytest from mcbebop.arsdk.session import Reading from mcbebop.arsdk.types import Event from mcbebop.tools.state import _battery KEY = "BatteryStateChanged_percent" class FakeSession: def __init__(self, entry): self.entry = entry def state(self, keys): return {KEY: self.entry} if self.entry is not None else {} def test_reading_detects_disagreement(): assert Reading(0, 1.0, 4, {126: 64, 127: 0}).conflicting assert not Reading(64, 1.0, 1, {126: 64}).conflicting assert not Reading(64, 1.0, 2, {126: 64, 127: 64}).conflicting def test_event_carries_its_buffer(): """Without the source, the store cannot tell two reporters apart.""" assert Event(ids=(0, 5, 1), name="x", values={}, at=0.0).buffer_id == 0 assert Event(ids=(0, 5, 1), name="x", values={}, at=0.0, buffer_id=127).buffer_id == 127 def test_the_real_conflict_resolves_to_the_credible_value(): pct, detail = _battery(FakeSession({"value": 0, "age": 0.1, "sources": {126: 64, 127: 0}})) assert pct == 64, "the newest frame said 0; the credible one is 64" assert "contested" in detail and "0" in detail, "the caller must learn the number was disputed" def test_a_lone_reading_is_not_described_as_contested(): pct, detail = _battery(FakeSession({"value": 64, "age": 0.1, "sources": {126: 64}})) assert pct == 64 and detail == "64%" @pytest.mark.parametrize( ("entry", "expected"), [ ({"value": 58, "age": 0.1}, 58), ({"value": 0, "age": 0.1, "sources": {126: 0, 127: 0}}, 0), (None, None), ], ) def test_other_shapes(entry, expected): """A genuine zero from every source still reports zero, and still blocks.""" assert _battery(FakeSession(entry))[0] == expected