From 5a3b9ddf26fc96467272e6728ad581b6a32b5d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Fri, 17 Nov 2023 18:45:40 +0100 Subject: [PATCH] Init proxy tests (#3352) * Init proxy tests * lint --- tests/common.py | 65 +++++++++++++++++-- .../octocat/integration/1.0.0/README.md | 1 + .../octocat/integration/1.0.0/hacs.json | 3 + .../octocat/integration/2.0.0/README.md | 1 + .../octocat/integration/2.0.0/hacs.json | 3 + tests/repositories/test_get_documentation.py | 36 ++++++++++ tests/repositories/test_get_hacs_json.py | 21 ++++++ 7 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/README.md create mode 100644 tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/hacs.json create mode 100644 tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/README.md create mode 100644 tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/hacs.json create mode 100644 tests/repositories/test_get_documentation.py create mode 100644 tests/repositories/test_get_hacs_json.py diff --git a/tests/common.py b/tests/common.py index 23c9b683889..7aabd5587e8 100644 --- a/tests/common.py +++ b/tests/common.py @@ -8,25 +8,24 @@ import os from typing import Any -from aiohttp import ClientWebSocketResponse +from aiohttp import ClientSession, ClientWebSocketResponse +from aiohttp.typedefs import StrOrURL from homeassistant import auth, config_entries, core as ha -from homeassistant.auth import ( - auth_store, - models as auth_models, - permissions as auth_permissions, -) +from homeassistant.auth import auth_store, models as auth_models from homeassistant.components.http import ( CONFIG_SCHEMA as HTTP_CONFIG_SCHEMA, async_setup as http_async_setup, ) from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE from homeassistant.helpers import storage +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.device_registry import DeviceRegistry from homeassistant.helpers.entity_registry import EntityRegistry from homeassistant.helpers.issue_registry import IssueRegistry from homeassistant.setup import async_setup_component import homeassistant.util.dt as date_util from homeassistant.util.unit_system import METRIC_SYSTEM +from yarl import URL from custom_components.hacs.base import HacsBase from custom_components.hacs.repositories.base import HacsManifest, HacsRepository @@ -312,3 +311,57 @@ async def receive_json(self) -> dict[str, Any]: async def send_and_receive_json(self, type: str, payload: dict[str, Any]) -> dict[str, Any]: await self.send_json(type=type, payload=payload) return await self.client.receive_json() + + +class MockedResponse: + def __init__(self, **kwargs) -> None: + self.status = kwargs.get("status", 200) + self.read = kwargs.get("read", AsyncMock()) + self.json = kwargs.get("json", AsyncMock()) + self.exception = kwargs.get("exception", None) + + +class ResponseMocker: + responses: dict[str, MockedResponse] = {} + + def add(self, url: str, response: MockedResponse) -> None: + self.responses[url] = response + + def get(self, url: str) -> MockedResponse: + return self.responses.pop(url, None) + + +async def client_session_proxy(hass: ha.HomeAssistant) -> ClientSession: + """Create a mocked client session.""" + base = async_get_clientsession(hass) + response_mocker = ResponseMocker() + + async def _request(method: str, str_or_url: StrOrURL, *args, **kwargs): + url = URL(str_or_url) + fp = os.path.join( + os.path.dirname(__file__), + f"fixtures/proxy/{url.host}{url.path}{'.json' if url.host == 'api.github.com' else ''}", + ) + print(f"Using fixture {fp} for request to {url.host}") + + if (resp := response_mocker.get(str_or_url)) is not None: + if resp.exception: + raise resp.exception + return resp + + if not os.path.exists(fp): + raise AssertionError(f"Missing fixture for proxy/{url.host}{url.path}") + + async def read(): + with open(fp, encoding="utf-8") as fptr: + return fptr.read().encode("utf-8") + + async def json(): + with open(fp, encoding="utf-8") as fptr: + return json.loads(fptr.read()) + + return AsyncMock(status=200, url=url, read=read, json=json) + + base._request = _request + + return base diff --git a/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/README.md b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/README.md new file mode 100644 index 00000000000..530ac586772 --- /dev/null +++ b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/README.md @@ -0,0 +1 @@ +## Example readme file (1.0.0) \ No newline at end of file diff --git a/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/hacs.json b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/hacs.json new file mode 100644 index 00000000000..7861b59f537 --- /dev/null +++ b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/1.0.0/hacs.json @@ -0,0 +1,3 @@ +{ + "name": "Proxy integration" +} \ No newline at end of file diff --git a/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/README.md b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/README.md new file mode 100644 index 00000000000..67d48bfd57c --- /dev/null +++ b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/README.md @@ -0,0 +1 @@ +## Example readme file (2.0.0) \ No newline at end of file diff --git a/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/hacs.json b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/hacs.json new file mode 100644 index 00000000000..7861b59f537 --- /dev/null +++ b/tests/fixtures/proxy/raw.githubusercontent.com/octocat/integration/2.0.0/hacs.json @@ -0,0 +1,3 @@ +{ + "name": "Proxy integration" +} \ No newline at end of file diff --git a/tests/repositories/test_get_documentation.py b/tests/repositories/test_get_documentation.py new file mode 100644 index 00000000000..0b3bcbb6aca --- /dev/null +++ b/tests/repositories/test_get_documentation.py @@ -0,0 +1,36 @@ +from typing import Any + +import pytest + +from custom_components.hacs.base import HacsBase +from custom_components.hacs.repositories.base import HacsRepository + +from tests.common import client_session_proxy + + +@pytest.mark.parametrize( + "data,result", + [ + ({"installed": True, "installed_version": "1.0.0"}, "Example readme file (1.0.0)"), + ( + {"installed": True, "installed_version": "1.0.0", "last_version": "2.0.0"}, + "Example readme file (1.0.0)", + ), + ({"installed": False, "last_version": "2.0.0"}, "Example readme file (2.0.0)"), + ({"installed": False, "last_version": "99.99.99"}, None), + ], +) +@pytest.mark.asyncio +async def test_validate_repository(hacs: HacsBase, data: dict[str, Any], result: str | None): + repository = HacsRepository(hacs=hacs) + repository.data.full_name = "octocat/integration" + for key, value in data.items(): + setattr(repository.data, key, value) + + hacs.session = await client_session_proxy(hacs.hass) + docs = await repository.get_documentation(filename="README.md") + + if result: + assert result in docs + else: + assert result is None diff --git a/tests/repositories/test_get_hacs_json.py b/tests/repositories/test_get_hacs_json.py new file mode 100644 index 00000000000..a1a5c2eb500 --- /dev/null +++ b/tests/repositories/test_get_hacs_json.py @@ -0,0 +1,21 @@ +import pytest + +from custom_components.hacs.base import HacsBase +from custom_components.hacs.repositories.base import HacsRepository + +from tests.common import client_session_proxy + + +@pytest.mark.parametrize("version,name", [("1.0.0", "Proxy integration"), ("99.99.99", None)]) +@pytest.mark.asyncio +async def test_validate_repository(hacs: HacsBase, version: str, name: str | None): + repository = HacsRepository(hacs=hacs) + repository.data.full_name = "octocat/integration" + + hacs.session = await client_session_proxy(hacs.hass) + manifest = await repository.get_hacs_json(version=version) + + if name: + assert manifest.name == name + else: + assert manifest is None