From eb487480381bff3aa87f4d80145b162863dc0a27 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Mon, 24 Jan 2022 09:27:24 +0200 Subject: [PATCH] Add webostv 100% tests coverage for init (#64801) --- .coveragerc | 1 - homeassistant/components/webostv/__init__.py | 2 - tests/components/webostv/__init__.py | 55 +++++++- tests/components/webostv/conftest.py | 32 ++++- tests/components/webostv/test_init.py | 141 +++++++++++++++++++ 5 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 tests/components/webostv/test_init.py diff --git a/.coveragerc b/.coveragerc index 645a2dae2bb..3c94f89e09d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1299,7 +1299,6 @@ omit = homeassistant/components/waze_travel_time/__init__.py homeassistant/components/waze_travel_time/helpers.py homeassistant/components/waze_travel_time/sensor.py - homeassistant/components/webostv/__init__.py homeassistant/components/whois/__init__.py homeassistant/components/whois/sensor.py homeassistant/components/wiffi/* diff --git a/homeassistant/components/webostv/__init__.py b/homeassistant/components/webostv/__init__.py index cb35f51e087..9f78dcd0964 100644 --- a/homeassistant/components/webostv/__init__.py +++ b/homeassistant/components/webostv/__init__.py @@ -169,8 +169,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: await asyncio.sleep(wait_time) except WebOsTvPairError: return - else: - break ent_reg = entity_registry.async_get(hass) if not ( diff --git a/tests/components/webostv/__init__.py b/tests/components/webostv/__init__.py index 7a4ca9d694d..08855c0e273 100644 --- a/tests/components/webostv/__init__.py +++ b/tests/components/webostv/__init__.py @@ -1,16 +1,25 @@ """Tests for the WebOS TV integration.""" +from pickle import dumps from unittest.mock import patch +import sqlalchemy as db +from sqlalchemy import create_engine + from homeassistant.components.media_player import DOMAIN as MP_DOMAIN from homeassistant.components.webostv.const import DOMAIN from homeassistant.const import CONF_CLIENT_SECRET, CONF_HOST +from homeassistant.helpers import entity_registry from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry -TV_NAME = "fake" +FAKE_UUID = "some-fake-uuid" +TV_NAME = "fake_webos" ENTITY_ID = f"{MP_DOMAIN}.{TV_NAME}" -MOCK_CLIENT_KEYS = {"1.2.3.4": "some-secret"} +HOST = "1.2.3.4" +CLIENT_KEY = "some-secret" +MOCK_CLIENT_KEYS = {HOST: CLIENT_KEY} +MOCK_JSON = '{"1.2.3.4": "some-secret"}' CHANNEL_1 = { "channelNumber": "1", @@ -29,7 +38,7 @@ async def setup_webostv(hass, unique_id="some-unique-id"): entry = MockConfigEntry( domain=DOMAIN, data={ - CONF_HOST: "1.2.3.4", + CONF_HOST: HOST, CONF_CLIENT_SECRET: "0123456789", }, title=TV_NAME, @@ -44,8 +53,46 @@ async def setup_webostv(hass, unique_id="some-unique-id"): await async_setup_component( hass, DOMAIN, - {DOMAIN: {CONF_HOST: "1.2.3.4"}}, + {DOMAIN: {CONF_HOST: HOST}}, ) await hass.async_block_till_done() return entry + + +async def setup_legacy_component(hass, create_entity=True): + """Initialize webostv component with legacy entity.""" + if create_entity: + ent_reg = entity_registry.async_get(hass) + assert ent_reg.async_get_or_create(MP_DOMAIN, DOMAIN, CLIENT_KEY) + + assert await async_setup_component( + hass, + DOMAIN, + {DOMAIN: {CONF_HOST: HOST}}, + ) + await hass.async_block_till_done() + + +def create_memory_sqlite_engine(url): + """Create fake db keys file in memory.""" + mem_eng = create_engine("sqlite:///:memory:") + table = db.Table( + "unnamed", + db.MetaData(), + db.Column("key", db.String), + db.Column("value", db.String), + ) + table.create(mem_eng) + query = db.insert(table).values(key=HOST, value=dumps(CLIENT_KEY)) + connection = mem_eng.connect() + connection.execute(query) + return mem_eng + + +def is_entity_unique_id_updated(hass): + """Check if entity has new unique_id from UUID.""" + ent_reg = entity_registry.async_get(hass) + return ent_reg.async_get_entity_id( + MP_DOMAIN, DOMAIN, FAKE_UUID + ) and not ent_reg.async_get_entity_id(MP_DOMAIN, DOMAIN, CLIENT_KEY) diff --git a/tests/components/webostv/conftest.py b/tests/components/webostv/conftest.py index b99a04f41c3..a4d6b6d8e21 100644 --- a/tests/components/webostv/conftest.py +++ b/tests/components/webostv/conftest.py @@ -1,11 +1,12 @@ """Common fixtures and objects for the LG webOS integration tests.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, Mock, patch import pytest from homeassistant.components.webostv.const import LIVE_TV_APP_ID +from homeassistant.helpers import entity_registry -from . import CHANNEL_1, CHANNEL_2 +from . import CHANNEL_1, CHANNEL_2, FAKE_UUID from tests.common import async_mock_service @@ -23,7 +24,7 @@ def client_fixture(): "homeassistant.components.webostv.WebOsClient", autospec=True ) as mock_client_class: client = mock_client_class.return_value - client.hello_info = {"deviceUUID": "some-fake-uuid"} + client.hello_info = {"deviceUUID": FAKE_UUID} client.software_info = {"major_ver": "major", "minor_ver": "minor"} client.system_info = {"modelName": "TVFAKE"} client.client_key = "0123456789" @@ -55,3 +56,28 @@ def client_fixture(): client.mock_state_update = AsyncMock(side_effect=mock_state_update_callback) yield client + + +@pytest.fixture(name="client_entity_removed") +def client_entity_removed_fixture(hass): + """Patch of client library, entity removed waiting for connect.""" + with patch( + "homeassistant.components.webostv.WebOsClient", autospec=True + ) as mock_client_class: + client = mock_client_class.return_value + client.hello_info = {"deviceUUID": "some-fake-uuid"} + client.connected = False + + def mock_is_conneted(): + return client.connected + + client.is_connected = Mock(side_effect=mock_is_conneted) + + async def mock_conneted(): + ent_reg = entity_registry.async_get(hass) + ent_reg.async_remove("media_player.webostv_some_secret") + client.connected = True + + client.connect = AsyncMock(side_effect=mock_conneted) + + yield client diff --git a/tests/components/webostv/test_init.py b/tests/components/webostv/test_init.py new file mode 100644 index 00000000000..eeb1e2fa0ee --- /dev/null +++ b/tests/components/webostv/test_init.py @@ -0,0 +1,141 @@ +"""The tests for the WebOS TV platform.""" + +from unittest.mock import Mock, mock_open, patch + +from aiowebostv import WebOsTvPairError + +from homeassistant.components.media_player import DOMAIN as MP_DOMAIN +from homeassistant.components.webostv import DOMAIN + +from . import ( + MOCK_JSON, + create_memory_sqlite_engine, + is_entity_unique_id_updated, + setup_legacy_component, +) + + +async def test_missing_keys_file_abort(hass, client, caplog): + """Test abort import when no pairing keys file.""" + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=False) + ): + await setup_legacy_component(hass) + + assert "No pairing keys, Not importing" in caplog.text + assert not is_entity_unique_id_updated(hass) + + +async def test_empty_json_abort(hass, client, caplog): + """Test abort import when keys file is empty.""" + m_open = mock_open(read_data="[]") + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True): + await setup_legacy_component(hass) + + assert "No pairing keys, Not importing" in caplog.text + assert not is_entity_unique_id_updated(hass) + + +async def test_valid_json_migrate_not_needed(hass, client, caplog): + """Test import from valid json entity already migrated or removed.""" + m_open = mock_open(read_data=MOCK_JSON) + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True): + await setup_legacy_component(hass, False) + + assert "Migrating webOS Smart TV entity" not in caplog.text + assert not is_entity_unique_id_updated(hass) + + +async def test_valid_json_missing_host_key(hass, client, caplog): + """Test import from valid json missing host key.""" + m_open = mock_open(read_data='{"1.2.3.5": "other-key"}') + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True): + await setup_legacy_component(hass) + + assert "Not importing webOS Smart TV host" in caplog.text + assert not is_entity_unique_id_updated(hass) + + +async def test_not_connected_import(hass, client, caplog, monkeypatch): + """Test import while device is not connected.""" + m_open = mock_open(read_data=MOCK_JSON) + monkeypatch.setattr(client, "is_connected", Mock(return_value=False)) + monkeypatch.setattr(client, "connect", Mock(side_effect=OSError)) + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True): + await setup_legacy_component(hass) + + assert f"Please make sure webOS TV {MP_DOMAIN}.{DOMAIN}" in caplog.text + assert not is_entity_unique_id_updated(hass) + + +async def test_pair_error_import_abort(hass, client, caplog, monkeypatch): + """Test abort import if device is not paired.""" + m_open = mock_open(read_data=MOCK_JSON) + monkeypatch.setattr(client, "is_connected", Mock(return_value=False)) + monkeypatch.setattr(client, "connect", Mock(side_effect=WebOsTvPairError)) + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True): + await setup_legacy_component(hass) + + assert f"Please make sure webOS TV {MP_DOMAIN}.{DOMAIN}" not in caplog.text + assert not is_entity_unique_id_updated(hass) + + +async def test_entity_removed_import_abort(hass, client_entity_removed, caplog): + """Test abort import if entity removed by user during import.""" + m_open = mock_open(read_data=MOCK_JSON) + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True): + await setup_legacy_component(hass) + + assert "Not updating webOSTV Smart TV entity" in caplog.text + assert not is_entity_unique_id_updated(hass) + + +async def test_json_import(hass, client, caplog, monkeypatch): + """Test import from json keys file.""" + m_open = mock_open(read_data=MOCK_JSON) + monkeypatch.setattr(client, "is_connected", Mock(return_value=True)) + monkeypatch.setattr(client, "connect", Mock(return_value=True)) + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True): + await setup_legacy_component(hass) + + assert "imported from YAML config" in caplog.text + assert is_entity_unique_id_updated(hass) + + +async def test_sqlite_import(hass, client, caplog, monkeypatch): + """Test import from sqlite keys file.""" + m_open = mock_open(read_data="will raise JSONDecodeError") + monkeypatch.setattr(client, "is_connected", Mock(return_value=True)) + monkeypatch.setattr(client, "connect", Mock(return_value=True)) + + with patch( + "homeassistant.components.webostv.os.path.isfile", Mock(return_value=True) + ), patch("homeassistant.components.webostv.open", m_open, create=True), patch( + "homeassistant.components.webostv.db.create_engine", + side_effect=create_memory_sqlite_engine, + ): + await setup_legacy_component(hass) + + assert "imported from YAML config" in caplog.text + assert is_entity_unique_id_updated(hass)