diff --git a/.github/workflows/BackendTest.yml b/.github/workflows/BackendTest.yml index 82c9a8030..322fa6741 100644 --- a/.github/workflows/BackendTest.yml +++ b/.github/workflows/BackendTest.yml @@ -10,12 +10,7 @@ jobs: name: Home Assistant Check strategy: matrix: - python-version: [ - 3.6, - 3.7, - # Python 3.8 can not yet be enabled. https://github.com/actions/setup-python/issues/30 - #3.8 - ] + python-version: [3.6, 3.7, 3.8] ha-version: ["stable", "beta", "dev"] runs-on: ubuntu-latest steps: @@ -56,4 +51,10 @@ jobs: echo "Testing with this configuration:" cat configuration.yaml echo "" - hass --script check_config --config . \ No newline at end of file + hass --script check_config --config . + + - name: Install test dependencies + run: python -m pip install -r requirements.txt + + - name: Run pytest + run: python -m pytest \ No newline at end of file diff --git a/custom_components/hacs/hacsbase/__init__.py b/custom_components/hacs/hacsbase/__init__.py index 517d8dfb1..5a1003be8 100644 --- a/custom_components/hacs/hacsbase/__init__.py +++ b/custom_components/hacs/hacsbase/__init__.py @@ -77,6 +77,11 @@ class Hacs: tasks = [] common = HacsCommon() + @staticmethod + def init(hass, github_token): + """Return a initialized HACS object.""" + return Hacs() + def get_by_id(self, repository_id): """Get repository by ID.""" try: diff --git a/custom_components/hacs/hacsbase/configuration.py b/custom_components/hacs/hacsbase/configuration.py index 9e3e8b7e9..7545c7ea7 100644 --- a/custom_components/hacs/hacsbase/configuration.py +++ b/custom_components/hacs/hacsbase/configuration.py @@ -1,6 +1,8 @@ """HACS Configuration.""" import attr +from custom_components.hacs.hacsbase.exceptions import HacsUserScrewupException + @attr.s(auto_attribs=True) class Configuration: @@ -18,8 +20,8 @@ class Configuration: plugin_path: str = "www/community/" python_script_path: str = "python_scripts/" python_script: bool = False - sidepanel_icon: str = "" - sidepanel_title: str = "" + sidepanel_icon: str = "mdi:alpha-c-box" + sidepanel_title: str = "Community" theme_path: str = "themes/" theme: bool = False token: str = "" @@ -32,18 +34,22 @@ class Configuration: @staticmethod def from_dict(configuration: dict, options: dict): """Set attributes from dicts.""" + if isinstance(options, bool) or isinstance(configuration.get("options"), bool): + raise HacsUserScrewupException("Configuration is not valid.") + if options is None: options = {} - return Configuration( - config=configuration, - options=options, - appdaemon=configuration.get("appdaemon", False), - python_script=configuration.get("python_script", False), - sidepanel_icon=configuration.get("sidepanel_icon", "mdi:alpha-c-box"), - sidepanel_title=configuration.get("sidepanel_title", "community"), - theme=configuration.get("theme", False), - token=configuration.get("token"), - country=options.get("country", "ALL"), - experimental=options.get("experimental", False), - release_limit=options.get("release_limit", 5), - ) + + if not configuration: + raise HacsUserScrewupException("Configuration is not valid.") + + config = Configuration() + + config.config = configuration + config.options = options + + for conf_type in [configuration, options]: + for key in conf_type: + setattr(config, key, conf_type[key]) + + return config diff --git a/custom_components/hacs/repositories/manifest.py b/custom_components/hacs/repositories/manifest.py index 86b889d02..b639d4262 100644 --- a/custom_components/hacs/repositories/manifest.py +++ b/custom_components/hacs/repositories/manifest.py @@ -6,6 +6,8 @@ https://hacs.xyz/docs/publish/start#hacsjson from typing import List import attr +from custom_components.hacs.hacsbase.exceptions import HacsRepositoryInfo + @attr.s(auto_attribs=True) class HacsManifest: @@ -16,6 +18,7 @@ class HacsManifest: zip_release: bool = False filename: str = None manifest: dict = {} + hacs: str = "" domains: List[str] = [] country: List[str] = [] homeassistant: str = None @@ -26,15 +29,13 @@ class HacsManifest: @staticmethod def from_dict(manifest: dict): """Set attributes from dicts.""" - return HacsManifest( - manifest=manifest, - name=manifest.get("name"), - content_in_root=manifest.get("content_in_root"), - filename=manifest.get("filename"), - domains=manifest.get("domains"), - country=manifest.get("country"), - homeassistant=manifest.get("homeassistant"), - persistent_directory=manifest.get("persistent_directory"), - iot_class=manifest.get("iot_class"), - render_readme=manifest.get("render_readme"), - ) + if manifest is None: + raise HacsRepositoryInfo("Missing manifest data") + + manifest_data = HacsManifest() + + manifest_data.manifest = manifest + + for key in manifest: + setattr(manifest_data, key, manifest[key]) + return manifest_data diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..bb46546f5 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +addopts = --maxfail=2 -rxf -v +filterwarnings = + ignore::DeprecationWarning +testpaths = tests +python_files = + test_*.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2d33767e8..aeaaf5bb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ aiofiles==0.4.0 backoff==1.8.0 aiogithubapi==0.4.2 integrationhelper==0.2.2 -attrs==19.3.0 \ No newline at end of file +attrs==19.3.0 +pytest==5.2.2 \ No newline at end of file diff --git a/tests/test_configuration.py b/tests/test_configuration.py new file mode 100644 index 000000000..9942c6b21 --- /dev/null +++ b/tests/test_configuration.py @@ -0,0 +1,55 @@ +"""Configuration Test Suite.""" +# pylint: disable=missing-docstring +import pytest +from custom_components.hacs.hacsbase.configuration import Configuration +from custom_components.hacs.hacsbase.exceptions import HacsUserScrewupException + + +def test_configuration_and_option(): + config = Configuration.from_dict({"token": "xxxxxxxxxx"}, {}) + + assert isinstance(config.options, dict) + assert isinstance(config.config, dict) + + assert isinstance(config.token, str) + assert config.token == "xxxxxxxxxx" + + assert isinstance(config.sidepanel_title, str) + assert config.sidepanel_title == "Community" + + assert isinstance(config.sidepanel_icon, str) + assert config.sidepanel_icon == "mdi:alpha-c-box" + + assert isinstance(config.appdaemon, bool) + assert not config.appdaemon + + assert isinstance(config.python_script, bool) + assert not config.python_script + + assert isinstance(config.theme, bool) + assert not config.theme + + assert isinstance(config.options, dict) + + assert isinstance(config.country, str) + assert config.country == "ALL" + + assert isinstance(config.release_limit, int) + assert config.release_limit == 5 + + assert isinstance(config.experimental, bool) + assert not config.experimental + + +def test_edge_option_only_pass_empty_dict_as_configuration(): + with pytest.raises(HacsUserScrewupException): + assert Configuration.from_dict({}, {"experimental": True}) + + +def test_edge_configuration_only_pass_none_as_option(): + assert Configuration.from_dict({"token": "xxxxxxxxxx"}, None) + + +def test_edge_options_true(): + with pytest.raises(HacsUserScrewupException): + assert Configuration.from_dict({"options": True}, None) diff --git a/tests/test_hacs_manifest.py b/tests/test_hacs_manifest.py new file mode 100644 index 000000000..4d92bf2eb --- /dev/null +++ b/tests/test_hacs_manifest.py @@ -0,0 +1,49 @@ +"""HACS Manifest Test Suite.""" +# pylint: disable=missing-docstring +import pytest +from custom_components.hacs.hacsbase.exceptions import HacsRepositoryInfo +from custom_components.hacs.repositories.manifest import HacsManifest + + +def test_manifest_structure(): + manifest = HacsManifest.from_dict({"name": "TEST"}) + + assert isinstance(manifest.manifest, dict) + + assert isinstance(manifest.name, str) + assert manifest.name == "TEST" + + assert isinstance(manifest.content_in_root, bool) + assert not manifest.content_in_root + + assert isinstance(manifest.zip_release, bool) + assert not manifest.zip_release + + assert isinstance(manifest.filename, type(None)) + assert manifest.filename is None + + assert isinstance(manifest.domains, list) + assert not manifest.domains + + assert isinstance(manifest.country, list) + assert not manifest.country + + assert isinstance(manifest.homeassistant, type(None)) + assert manifest.homeassistant is None + + assert isinstance(manifest.persistent_directory, type(None)) + assert manifest.persistent_directory is None + + assert isinstance(manifest.iot_class, type(None)) + assert manifest.iot_class is None + + assert isinstance(manifest.render_readme, bool) + assert not manifest.render_readme + + assert isinstance(manifest.hacs, str) + assert not manifest.hacs + + +def test_edge_pass_none(): + with pytest.raises(HacsRepositoryInfo): + assert HacsManifest.from_dict(None)