From 27715f69329000dcea511318e67a4fb9eac037b2 Mon Sep 17 00:00:00 2001 From: James Mackay Date: Fri, 9 Oct 2026 06:12:01 +0100 Subject: [PATCH] =?UTF-8?q?test(media=5Fplayer):=20MAC-collision=20pre-che?= =?UTF-8?q?ck=20migration=20branches=20=E2=80=94=20occupied=20MAC=20falls?= =?UTF-8?q?=20back=20to=20the=20entry=20id,=20free=20MAC=20taken=20(t26=20?= =?UTF-8?q?F4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_standalone_setup.py | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/test_standalone_setup.py b/tests/test_standalone_setup.py index f84fbaa..8c126ac 100644 --- a/tests/test_standalone_setup.py +++ b/tests/test_standalone_setup.py @@ -532,6 +532,82 @@ class TestLegacyUniqueIdMigration: assert entry.unique_id == "aa:bb:cc:dd:ee:ff" # MAC preferred + async def test_mac_collision_falls_back_to_entry_id(self, tmp_path): + """t26 F4: an existing entry owning the target MAC blocks the move.""" + from types import SimpleNamespace as MigrationEntry + + import entity_stublib + + entity_stublib.load_entity_modules() + modules = sys.modules["nsh.media_player"] + entry = MigrationEntry( + entry_id="cfgid123", + data={CONF_ANDROIDTV_ENTITY: "media_player.shield"}, + options={}, + title="Shield", + unique_id="media_player.shield", + runtime_data=None, + ) + + class _Manager: + async def async_protocol_device_info(self): + return "Shield TV", "aa:bb:cc:dd:ee:ff" + + entry.runtime_data = _Manager() + + class _Entries: + entries = [ + SimpleNamespace(entry_id="cfgid999", unique_id="aa:bb:cc:dd:ee:ff") + ] + updating_entry_calls: list = [] + + def async_update_entry(self, entry, **kwargs): + self.updating_entry_calls.append((entry, kwargs)) + for key, value in kwargs.items(): + if key in ("options", "data", "unique_id"): + setattr(entry, key, value) + + entries = _Entries() + entries.hass = FakeHass(str(tmp_path)) + hass = entries.hass + hass.config_entries = entries + + await modules._migrate_legacy_unique_id(hass, entry) + + # collision → the config-entry id is used instead + assert entry.unique_id == "cfgid123" + + async def test_free_mac_is_taken_without_collision(self, tmp_path): + """t26 F4: no other entry holds the MAC → the MAC is used.""" + from types import SimpleNamespace as MigrationEntry + + import entity_stublib + + entity_stublib.load_entity_modules() + modules = sys.modules["nsh.media_player"] + entry = MigrationEntry( + entry_id="cfgid123", + data={CONF_ANDROIDTV_ENTITY: "media_player.shield"}, + options={}, + title="Shield", + unique_id="media_player.shield", + runtime_data=None, + ) + + class _Manager: + async def async_protocol_device_info(self): + return "Shield TV", "aa:bb:cc:dd:ee:ff" + + entry.runtime_data = _Manager() + hass = FakeHass(str(tmp_path)) + hass.config_entries.entries = [ + SimpleNamespace(entry_id="cfgid999", unique_id="other:mac") # different MAC + ] + + await modules._migrate_legacy_unique_id(hass, entry) + + assert entry.unique_id == "aa:bb:cc:dd:ee:ff" # free MAC taken + async def test_never_runs_twice_for_a_migrated_entry(self, tmp_path): """The legacy pattern no longer matches after the one-time move.""" import entity_stublib