From 63b94c1f2ae651d552521f634b400f48ec05b922 Mon Sep 17 00:00:00 2001 From: mik-laj <12058428+mik-laj@users.noreply.github.com> Date: Wed, 10 Jun 2026 02:20:26 +0200 Subject: [PATCH 1/8] Fetch effects from /json/effects to handle ESP8266 buffer truncation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On ESP8266 devices the /json response truncates the effects array when the output buffer is too small — the device may report fxcount=220 but return only 54 entries. This causes KeyError when a segment references an effect ID that is missing from the truncated list (WLED issue #5674). Fix this by always fetching the complete list from /json/effects on initial load, which is a dedicated endpoint not affected by the buffer limit. On subsequent updates, re-fetch only when fxcount changes or a device restart is detected (boot_time shift > 2 s, derived as now - uptime to avoid false positives from the ever-incrementing uptime counter). Add tests covering: skip when unchanged, re-fetch on fxcount change, re-fetch after device restart, and full list from /json/effects despite truncated /json response. Co-Authored-By: Claude Sonnet 4.6 --- src/wled/wled.py | 71 ++++++++++++++++++++++++++++ tests/conftest.py | 6 +++ tests/test_wled.py | 114 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) diff --git a/src/wled/wled.py b/src/wled/wled.py index 65b88ef6..1a0ac7e9 100644 --- a/src/wled/wled.py +++ b/src/wled/wled.py @@ -42,6 +42,14 @@ class _PresetsVersion: boot_time: int +@dataclass +class _EffectsVersion: + """Tracks effects state to avoid unnecessary fetches.""" + + effect_count: int + boot_time: int + + @dataclass class WLED: """Main class for handling connections with WLED.""" @@ -54,6 +62,7 @@ class WLED: _close_session: bool = False _device: Device | None = None _presets_version: _PresetsVersion | None = None + _effects_version: _EffectsVersion | None = None @property def connected(self) -> bool: @@ -316,12 +325,23 @@ async def update(self) -> Device: raise WLEDEmptyResponseError(msg) data["presets"] = presets + changed_effects, new_effects_version = self._check_effects_changed(data) + if changed_effects: + if not (effects := await self.request("/json/effects")): + msg = ( + f"WLED device at {self.host} returned an empty API" + " response on effects update", + ) + raise WLEDEmptyResponseError(msg) + data["effects"] = effects + if not self._device: self._device = Device.from_dict(data) else: self._device.update_from_dict(data) self._presets_version = new_version + self._effects_version = new_effects_version return self._device async def master( @@ -823,6 +843,57 @@ def _check_presets_changed( ) return (changed, new_version) + def _check_effects_changed( + self, data: dict[str, Any] + ) -> tuple[bool, _EffectsVersion | None]: + """Check if effects have changed since the last check. + + Compares the effect count and approximate boot time to detect changes. + A significant shift in boot_time (> 2 s) signals a device restart. + + On ESP8266 devices the /json response may return a truncated effects + list due to a limited output buffer (WLED issue #5674). The initial + load therefore always fetches the complete list from /json/effects, + which is unaffected by that limitation. + + Returns + ------- + A tuple of (changed, new_version). If the version cannot be + determined from the data, returns (True, None) to trigger a + safe refetch. + + """ + if not isinstance(data, dict) or "info" not in data: + # No info in message (e.g. state-only WebSocket update), + # effects can't have changed. + return (False, self._effects_version) + + info = data["info"] + if not (uptime := info.get("uptime")) or not (fxcount := info.get("fxcount")): + return (True, None) + + try: + new_version = _EffectsVersion( + effect_count=int(fxcount), + boot_time=int(time.time()) - int(uptime), + ) + except (ValueError, TypeError): + return (True, None) + + # For initial load, always fetch effects as /json may not include + # all effect information. + if self._device is None or self._device.effects is None: + return (True, new_version) + + if self._effects_version is None: + return (True, new_version) + + changed = ( + self._effects_version.effect_count != new_version.effect_count + or abs(self._effects_version.boot_time - new_version.boot_time) > 2 + ) + return (changed, new_version) + @dataclass class WLEDReleases: diff --git a/tests/conftest.py b/tests/conftest.py index d62f32d8..19d13d46 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -52,6 +52,12 @@ def mock_json_and_presets( body=json.dumps(wled_data), content_type="application/json", ) + mocked.get( + "http://example.com/json/effects", + status=200, + body=json.dumps(wled_data["effects"]), + content_type="application/json", + ) if presets_data is None: presets_data = load_fixture_json("presets") mocked.get( diff --git a/tests/test_wled.py b/tests/test_wled.py index 1a4649fc..ed6b92ec 100644 --- a/tests/test_wled.py +++ b/tests/test_wled.py @@ -286,6 +286,12 @@ async def test_update_skips_presets_when_unchanged( body=json.dumps(wled_data), content_type="application/json", ) + responses.get( + "http://example.com/json/effects", + status=200, + body=json.dumps(wled_data["effects"]), + content_type="application/json", + ) responses.get( "http://example.com/presets.json", status=200, @@ -345,6 +351,114 @@ async def test_update_refetches_presets_when_info_incomplete( await wled.update() +async def test_update_skips_effects_when_unchanged( + responses: aioresponses, wled: WLED +) -> None: + """Test update() skips /json/effects when effect count and boot time unchanged.""" + wled_data = load_fixture_json("wled") + changed_data = json.loads(json.dumps(wled_data)) + changed_data["info"]["fxcount"] += 1 + changed_data["effects"] = wled_data["effects"] + ["New Effect"] + + # First update: fetches /json, /json/effects, /presets.json + mock_json_and_presets(responses, wled_data) + # Second update: same fxcount and boot_time — only /json fetched + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(wled_data), + content_type="application/json", + ) + # Third update: fxcount increased — /json/effects refetched with extra effect + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(changed_data), + content_type="application/json", + ) + responses.get( + "http://example.com/json/effects", + status=200, + body=json.dumps(changed_data["effects"]), + content_type="application/json", + ) + + device1 = await wled.update() + assert device1.info.effect_count == wled_data["info"]["fxcount"] + initial_effect_count = len(device1.effects) + + device2 = await wled.update() + assert device2.info.effect_count == wled_data["info"]["fxcount"] + assert len(device2.effects) == initial_effect_count # no re-fetch, unchanged + + device3 = await wled.update() + assert device3.info.effect_count == changed_data["info"]["fxcount"] + # "New Effect" added after fxcount bump — re-fetch brought it in + assert len(device3.effects) == initial_effect_count + 1 + + +async def test_update_refetches_effects_after_device_restart( + responses: aioresponses, wled: WLED +) -> None: + """Test update() refetches effects when a device restart is detected.""" + wled_data = load_fixture_json("wled") + restarted_data = json.loads(json.dumps(wled_data)) + restarted_data["info"]["uptime"] = 5 # uptime reset — device just booted + restarted_data["info"]["fxcount"] += 1 + restarted_data["effects"] = wled_data["effects"] + ["Post Restart Effect"] + + mock_json_and_presets(responses, wled_data) + # After restart uptime drops from 32489 → 5, so boot_time shifts by ~32484s + mock_json_and_presets(responses, restarted_data) + + device1 = await wled.update() + assert device1.info.effect_count == wled_data["info"]["fxcount"] + initial_effect_count = len(device1.effects) + + device2 = await wled.update() + assert device2.info.effect_count == restarted_data["info"]["fxcount"] + # "Post Restart Effect" added — re-fetch after restart brought it in + assert len(device2.effects) == initial_effect_count + 1 + + +async def test_update_uses_effects_endpoint_for_full_list( + responses: aioresponses, wled: WLED +) -> None: + """Test update() uses /json/effects to get the complete effects list. + + Simulates the ESP8266 /json buffer overflow (WLED issue #5674): /json + returns a truncated effects list while /json/effects returns the full one. + """ + wled_data = load_fixture_json("wled") + full_effects = wled_data["effects"] + # Truncate in-place — simulates ESP8266 /json buffer overflow + wled_data["effects"] = full_effects[:1] + + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(wled_data), + content_type="application/json", + ) + responses.get( + "http://example.com/json/effects", + status=200, + body=json.dumps(full_effects), + content_type="application/json", + ) + responses.get( + "http://example.com/presets.json", + status=200, + body=json.dumps(load_fixture_json("presets")), + content_type="application/json", + ) + + device = await wled.update() + + # Despite /json returning only 1 effect, device has the full list from /json/effects + assert len(device.effects) == 3 + + async def test_listen_preset_change_via_websocket( responses: aioresponses, wled: WLED ) -> None: From f7ce075e3dc01470d6e1a9113df40fd456a6b062 Mon Sep 17 00:00:00 2001 From: mik-laj <12058428+mik-laj@users.noreply.github.com> Date: Wed, 10 Jun 2026 02:47:50 +0200 Subject: [PATCH 2/8] Address review comments on effects fetch PR - Fix WLEDEmptyResponseError messages constructed as 1-tuples due to a trailing comma; the exception now receives a plain string in all four call sites (listen() and update()) - Treat uptime=0 as a valid state in _check_effects_changed by using an explicit `is None` guard instead of `not uptime`; previously a device polled in its first second of uptime would never write _effects_version and would re-fetch /json/effects on every subsequent call - Freeze time.time() in test_update_skips_effects_when_unchanged to prevent flakiness when CI is slow enough that the boot_time delta exceeds the 2 s tolerance between update() calls - Update mock_json_and_presets docstring to reflect three endpoints Co-Authored-By: Claude Sonnet 4.6 --- src/wled/wled.py | 12 +++++---- tests/conftest.py | 2 +- tests/test_wled.py | 65 +++++++++++++++++++++++----------------------- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/wled/wled.py b/src/wled/wled.py index 1a0ac7e9..60d631a4 100644 --- a/src/wled/wled.py +++ b/src/wled/wled.py @@ -147,7 +147,7 @@ async def listen(self, callback: Callable[[Device], None]) -> None: if not (presets := await self.request("/presets.json")): msg = ( f"WLED device at {self.host} returned an empty API" - " response on presets update", + " response on presets update" ) raise WLEDEmptyResponseError(msg) message_data["presets"] = presets @@ -311,7 +311,7 @@ async def update(self) -> Device: if not (data := await self.request("/json")): msg = ( f"WLED device at {self.host} returned an empty API" - " response on full update", + " response on full update" ) raise WLEDEmptyResponseError(msg) @@ -320,7 +320,7 @@ async def update(self) -> Device: if not (presets := await self.request("/presets.json")): msg = ( f"WLED device at {self.host} returned an empty API" - " response on presets update", + " response on presets update" ) raise WLEDEmptyResponseError(msg) data["presets"] = presets @@ -330,7 +330,7 @@ async def update(self) -> Device: if not (effects := await self.request("/json/effects")): msg = ( f"WLED device at {self.host} returned an empty API" - " response on effects update", + " response on effects update" ) raise WLEDEmptyResponseError(msg) data["effects"] = effects @@ -869,7 +869,9 @@ def _check_effects_changed( return (False, self._effects_version) info = data["info"] - if not (uptime := info.get("uptime")) or not (fxcount := info.get("fxcount")): + if (uptime := info.get("uptime")) is None or not ( + fxcount := info.get("fxcount") + ): return (True, None) try: diff --git a/tests/conftest.py b/tests/conftest.py index 19d13d46..4d602e7a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,7 +43,7 @@ def mock_json_and_presets( wled_data: dict[str, Any] | None = None, presets_data: dict[str, Any] | None = None, ) -> None: - """Register the two GET endpoints that WLED.update() calls.""" + """Register the three GET endpoints that WLED.update() calls.""" if wled_data is None: wled_data = load_fixture_json("wled") mocked.get( diff --git a/tests/test_wled.py b/tests/test_wled.py index ed6b92ec..fd8d9efc 100644 --- a/tests/test_wled.py +++ b/tests/test_wled.py @@ -360,41 +360,42 @@ async def test_update_skips_effects_when_unchanged( changed_data["info"]["fxcount"] += 1 changed_data["effects"] = wled_data["effects"] + ["New Effect"] - # First update: fetches /json, /json/effects, /presets.json - mock_json_and_presets(responses, wled_data) - # Second update: same fxcount and boot_time — only /json fetched - responses.get( - "http://example.com/json", - status=200, - body=json.dumps(wled_data), - content_type="application/json", - ) - # Third update: fxcount increased — /json/effects refetched with extra effect - responses.get( - "http://example.com/json", - status=200, - body=json.dumps(changed_data), - content_type="application/json", - ) - responses.get( - "http://example.com/json/effects", - status=200, - body=json.dumps(changed_data["effects"]), - content_type="application/json", - ) + with patch("wled.wled.time.time", return_value=1_000_000.0): + # First update: fetches /json, /json/effects, /presets.json + mock_json_and_presets(responses, wled_data) + # Second update: same fxcount and boot_time — only /json fetched + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(wled_data), + content_type="application/json", + ) + # Third update: fxcount increased — /json/effects refetched with extra effect + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(changed_data), + content_type="application/json", + ) + responses.get( + "http://example.com/json/effects", + status=200, + body=json.dumps(changed_data["effects"]), + content_type="application/json", + ) - device1 = await wled.update() - assert device1.info.effect_count == wled_data["info"]["fxcount"] - initial_effect_count = len(device1.effects) + device1 = await wled.update() + assert device1.info.effect_count == wled_data["info"]["fxcount"] + initial_effect_count = len(device1.effects) - device2 = await wled.update() - assert device2.info.effect_count == wled_data["info"]["fxcount"] - assert len(device2.effects) == initial_effect_count # no re-fetch, unchanged + device2 = await wled.update() + assert device2.info.effect_count == wled_data["info"]["fxcount"] + assert len(device2.effects) == initial_effect_count # no re-fetch, unchanged - device3 = await wled.update() - assert device3.info.effect_count == changed_data["info"]["fxcount"] - # "New Effect" added after fxcount bump — re-fetch brought it in - assert len(device3.effects) == initial_effect_count + 1 + device3 = await wled.update() + assert device3.info.effect_count == changed_data["info"]["fxcount"] + # "New Effect" added after fxcount bump — re-fetch brought it in + assert len(device3.effects) == initial_effect_count + 1 async def test_update_refetches_effects_after_device_restart( From 349865132051aadff62502a862be2d8c0acd1dca Mon Sep 17 00:00:00 2001 From: mik-laj <12058428+mik-laj@users.noreply.github.com> Date: Wed, 10 Jun 2026 02:59:22 +0200 Subject: [PATCH 3/8] Prevent truncated /json effects from overwriting cached full list When _check_effects_changed returns False (no re-fetch needed), the effects array from /json was still passed into update_from_dict(), which would silently overwrite the full list previously fetched from /json/effects with the truncated ESP8266 payload on every subsequent poll. Fix: pop "effects" from the /json payload when not re-fetching, so update_from_dict() leaves the cached effects untouched. Extend test_update_uses_effects_endpoint_for_full_list with a second update() where /json is still truncated and no /json/effects stub is registered, asserting the full effects list survives. Co-Authored-By: Claude Sonnet 4.6 --- src/wled/wled.py | 4 ++++ tests/test_wled.py | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/wled/wled.py b/src/wled/wled.py index 60d631a4..b0522083 100644 --- a/src/wled/wled.py +++ b/src/wled/wled.py @@ -334,6 +334,10 @@ async def update(self) -> Device: ) raise WLEDEmptyResponseError(msg) data["effects"] = effects + else: + # Drop the possibly-truncated effects list from /json so that + # update_from_dict() keeps the cached full list from /json/effects. + data.pop("effects", None) if not self._device: self._device = Device.from_dict(data) diff --git a/tests/test_wled.py b/tests/test_wled.py index fd8d9efc..a8bdc58f 100644 --- a/tests/test_wled.py +++ b/tests/test_wled.py @@ -453,11 +453,20 @@ async def test_update_uses_effects_endpoint_for_full_list( body=json.dumps(load_fixture_json("presets")), content_type="application/json", ) + # Second update: /json still truncated, fxcount unchanged — no /json/effects stub. + # The cached full list must survive and not be overwritten by the truncated payload. + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(wled_data), + content_type="application/json", + ) device = await wled.update() + assert len(device.effects) == 3 # full list from /json/effects - # Despite /json returning only 1 effect, device has the full list from /json/effects - assert len(device.effects) == 3 + device = await wled.update() + assert len(device.effects) == 3 # still full — truncated /json did not overwrite async def test_listen_preset_change_via_websocket( From ecca50afa9a038d01f91456c4a6ef3dbacecec89 Mon Sep 17 00:00:00 2001 From: mik-laj <12058428+mik-laj@users.noreply.github.com> Date: Wed, 10 Jun 2026 03:07:33 +0200 Subject: [PATCH 4/8] Use is None guards for uptime and fxcount; fix outdated comment Treat fxcount=0 as a valid value rather than "missing": change `not fxcount` to `fxcount is None` in _check_effects_changed, consistent with the earlier uptime fix. A device reporting zero effects would otherwise be forced into a safe-refetch loop on every poll. Update the inline comment in test_update_skips_presets_when_unchanged to reflect that the first update now fetches three endpoints (/json, /json/effects, /presets.json) after mock_json_and_presets was extended to also stub /json/effects. Co-Authored-By: Claude Sonnet 4.6 --- src/wled/wled.py | 4 ++-- tests/test_wled.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wled/wled.py b/src/wled/wled.py index b0522083..d43d405c 100644 --- a/src/wled/wled.py +++ b/src/wled/wled.py @@ -873,9 +873,9 @@ def _check_effects_changed( return (False, self._effects_version) info = data["info"] - if (uptime := info.get("uptime")) is None or not ( + if (uptime := info.get("uptime")) is None or ( fxcount := info.get("fxcount") - ): + ) is None: return (True, None) try: diff --git a/tests/test_wled.py b/tests/test_wled.py index a8bdc58f..56b4a24c 100644 --- a/tests/test_wled.py +++ b/tests/test_wled.py @@ -279,7 +279,7 @@ async def test_update_skips_presets_when_unchanged( """Test update() skips fetching presets.json when presets haven't changed.""" wled_data = load_fixture_json("wled") - # First update: fetches both /json and /presets.json + # First update: fetches /json, /json/effects, /presets.json responses.get( "http://example.com/json", status=200, From 0af8fd18ab6b6c8e793772ba71b34fcfb7fe4525 Mon Sep 17 00:00:00 2001 From: mik-laj <12058428+mik-laj@users.noreply.github.com> Date: Wed, 10 Jun 2026 03:23:14 +0200 Subject: [PATCH 5/8] Fix empty-effects guard; isolate restart detection from fxcount change Replace `not effects` with `not isinstance(effects, list)` so that a valid empty effects list from /json/effects no longer raises WLEDEmptyResponseError (an empty list is falsy but not an error). In test_update_refetches_effects_after_device_restart, remove the fxcount increment from restarted_data so the re-fetch is driven solely by the boot_time shift (restart detection), not by a count change. Replace the length assertion with a name-presence check so the test validates the actual content rather than an artifact of fxcount. Co-Authored-By: Claude Sonnet 4.6 --- src/wled/wled.py | 3 ++- tests/test_wled.py | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/wled/wled.py b/src/wled/wled.py index d43d405c..783cc8c4 100644 --- a/src/wled/wled.py +++ b/src/wled/wled.py @@ -327,7 +327,8 @@ async def update(self) -> Device: changed_effects, new_effects_version = self._check_effects_changed(data) if changed_effects: - if not (effects := await self.request("/json/effects")): + effects = await self.request("/json/effects") + if not isinstance(effects, list): msg = ( f"WLED device at {self.host} returned an empty API" " response on effects update" diff --git a/tests/test_wled.py b/tests/test_wled.py index 56b4a24c..355634e1 100644 --- a/tests/test_wled.py +++ b/tests/test_wled.py @@ -405,7 +405,6 @@ async def test_update_refetches_effects_after_device_restart( wled_data = load_fixture_json("wled") restarted_data = json.loads(json.dumps(wled_data)) restarted_data["info"]["uptime"] = 5 # uptime reset — device just booted - restarted_data["info"]["fxcount"] += 1 restarted_data["effects"] = wled_data["effects"] + ["Post Restart Effect"] mock_json_and_presets(responses, wled_data) @@ -414,12 +413,10 @@ async def test_update_refetches_effects_after_device_restart( device1 = await wled.update() assert device1.info.effect_count == wled_data["info"]["fxcount"] - initial_effect_count = len(device1.effects) device2 = await wled.update() - assert device2.info.effect_count == restarted_data["info"]["fxcount"] - # "Post Restart Effect" added — re-fetch after restart brought it in - assert len(device2.effects) == initial_effect_count + 1 + # Refetch was triggered by boot_time shift, not fxcount — verify by content + assert any(e.name == "Post Restart Effect" for e in device2.effects.values()) async def test_update_uses_effects_endpoint_for_full_list( From 355aa3b9225ab96bb21e627abcc7b19151497903 Mon Sep 17 00:00:00 2001 From: mik-laj <12058428+mik-laj@users.noreply.github.com> Date: Wed, 10 Jun 2026 21:12:38 +0200 Subject: [PATCH 6/8] Fix misleading error message; remove dead effects-is-None check The WLEDEmptyResponseError for /json/effects said "empty API response" but the guard is isinstance(effects, list), which also fires for a non-empty but wrong-shaped payload. Change to "invalid response" so the message matches the actual check. Device.effects is typed as dict with default_factory=dict and is always initialised in from_dict/update_from_dict, so it can never be None. Remove the dead `or self._device.effects is None` branch from _check_effects_changed. Co-Authored-By: Claude Sonnet 4.6 --- src/wled/wled.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wled/wled.py b/src/wled/wled.py index 783cc8c4..8c9a47fe 100644 --- a/src/wled/wled.py +++ b/src/wled/wled.py @@ -330,10 +330,10 @@ async def update(self) -> Device: effects = await self.request("/json/effects") if not isinstance(effects, list): msg = ( - f"WLED device at {self.host} returned an empty API" + f"WLED device at {self.host} returned an invalid" " response on effects update" ) - raise WLEDEmptyResponseError(msg) + raise WLEDInvalidResponseError(msg) data["effects"] = effects else: # Drop the possibly-truncated effects list from /json so that @@ -889,7 +889,7 @@ def _check_effects_changed( # For initial load, always fetch effects as /json may not include # all effect information. - if self._device is None or self._device.effects is None: + if self._device is None: return (True, new_version) if self._effects_version is None: From c880cd2f87b58902a7b79766812f4cb18caf8de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Bregu=C5=82a?= Date: Wed, 10 Jun 2026 22:53:19 +0200 Subject: [PATCH 7/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/test_wled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_wled.py b/tests/test_wled.py index 355634e1..bead905a 100644 --- a/tests/test_wled.py +++ b/tests/test_wled.py @@ -429,7 +429,7 @@ async def test_update_uses_effects_endpoint_for_full_list( """ wled_data = load_fixture_json("wled") full_effects = wled_data["effects"] - # Truncate in-place — simulates ESP8266 /json buffer overflow + # Truncate list — simulates ESP8266 /json buffer overflow wled_data["effects"] = full_effects[:1] responses.get( From 95c40fc284d49f62a8f77f5f07c0599eced22b37 Mon Sep 17 00:00:00 2001 From: mik-laj <12058428+mik-laj@users.noreply.github.com> Date: Wed, 10 Jun 2026 23:03:49 +0200 Subject: [PATCH 8/8] Remove redundant mock --- tests/test_wled.py | 65 +++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/tests/test_wled.py b/tests/test_wled.py index bead905a..7dd3b7cc 100644 --- a/tests/test_wled.py +++ b/tests/test_wled.py @@ -360,42 +360,41 @@ async def test_update_skips_effects_when_unchanged( changed_data["info"]["fxcount"] += 1 changed_data["effects"] = wled_data["effects"] + ["New Effect"] - with patch("wled.wled.time.time", return_value=1_000_000.0): - # First update: fetches /json, /json/effects, /presets.json - mock_json_and_presets(responses, wled_data) - # Second update: same fxcount and boot_time — only /json fetched - responses.get( - "http://example.com/json", - status=200, - body=json.dumps(wled_data), - content_type="application/json", - ) - # Third update: fxcount increased — /json/effects refetched with extra effect - responses.get( - "http://example.com/json", - status=200, - body=json.dumps(changed_data), - content_type="application/json", - ) - responses.get( - "http://example.com/json/effects", - status=200, - body=json.dumps(changed_data["effects"]), - content_type="application/json", - ) + # First update: fetches /json, /json/effects, /presets.json + mock_json_and_presets(responses, wled_data) + # Second update: same fxcount and boot_time — only /json fetched + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(wled_data), + content_type="application/json", + ) + # Third update: fxcount increased — /json/effects refetched with extra effect + responses.get( + "http://example.com/json", + status=200, + body=json.dumps(changed_data), + content_type="application/json", + ) + responses.get( + "http://example.com/json/effects", + status=200, + body=json.dumps(changed_data["effects"]), + content_type="application/json", + ) - device1 = await wled.update() - assert device1.info.effect_count == wled_data["info"]["fxcount"] - initial_effect_count = len(device1.effects) + device1 = await wled.update() + assert device1.info.effect_count == wled_data["info"]["fxcount"] + initial_effect_count = len(device1.effects) - device2 = await wled.update() - assert device2.info.effect_count == wled_data["info"]["fxcount"] - assert len(device2.effects) == initial_effect_count # no re-fetch, unchanged + device2 = await wled.update() + assert device2.info.effect_count == wled_data["info"]["fxcount"] + assert len(device2.effects) == initial_effect_count # no re-fetch, unchanged - device3 = await wled.update() - assert device3.info.effect_count == changed_data["info"]["fxcount"] - # "New Effect" added after fxcount bump — re-fetch brought it in - assert len(device3.effects) == initial_effect_count + 1 + device3 = await wled.update() + assert device3.info.effect_count == changed_data["info"]["fxcount"] + # "New Effect" added after fxcount bump — re-fetch brought it in + assert len(device3.effects) == initial_effect_count + 1 async def test_update_refetches_effects_after_device_restart(