diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b03911 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.venv/ +.pytest_cache/ +__pycache__/ +*.pyc +.coverage +artifacts/ diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..4c4b748 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +markers = + upstream: fetches and validates pinned upstream source + docker: builds or runs the add-on image diff --git a/repository.yaml b/repository.yaml new file mode 100644 index 0000000..e500113 --- /dev/null +++ b/repository.yaml @@ -0,0 +1,3 @@ +name: Stelloauth for Home Assistant +url: https://git.radixadm.dk/dennis/homeassistant-stelloauth-addon.git +maintainer: Dennis / Radix ApS diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..4758e4f --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +pytest==8.4.2 +PyYAML==6.0.2 diff --git a/stelloauth/config.yaml b/stelloauth/config.yaml new file mode 100644 index 0000000..6950576 --- /dev/null +++ b/stelloauth/config.yaml @@ -0,0 +1,24 @@ +name: Stelloauth +version: "0.1.0" +slug: stelloauth +description: Local OAuth worker for Stellantis Vehicles using CloakBrowser +url: https://git.radixadm.dk/dennis/homeassistant-stelloauth-addon +arch: + - amd64 + - aarch64 +startup: application +boot: auto +init: true +watchdog: http://[HOST]:[PORT:8080]/ +ports: + 8080/tcp: null +ports_description: + 8080/tcp: Temporary LAN access for troubleshooting only +options: + queue_timeout: 60s + rate_limit_count: 5 + rate_limit_duration: 1h +schema: + queue_timeout: match(^[1-9][0-9]*(ms|s|m|h)$) + rate_limit_count: int(1,20) + rate_limit_duration: match(^[1-9][0-9]*(ms|s|m|h)$) diff --git a/stelloauth/translations/da.yaml b/stelloauth/translations/da.yaml new file mode 100644 index 0000000..894559e --- /dev/null +++ b/stelloauth/translations/da.yaml @@ -0,0 +1,10 @@ +configuration: + queue_timeout: + name: Køventetid + description: Angiver hvor længe et loginforsøg må vente i køen. + rate_limit_count: + name: Loginforsøg + description: Angiver det maksimale antal loginforsøg i hver periode. + rate_limit_duration: + name: Rate limit-periode + description: Angiver periodens længde for begrænsning af loginforsøg. diff --git a/stelloauth/translations/en.yaml b/stelloauth/translations/en.yaml new file mode 100644 index 0000000..337eb73 --- /dev/null +++ b/stelloauth/translations/en.yaml @@ -0,0 +1,10 @@ +configuration: + queue_timeout: + name: Queue timeout + description: Sets how long a login attempt may wait in the queue. + rate_limit_count: + name: Login attempts + description: Sets the maximum number of login attempts in each period. + rate_limit_duration: + name: Rate limit period + description: Sets the length of the login attempt rate limit period. diff --git a/tests/test_addon_metadata.py b/tests/test_addon_metadata.py new file mode 100644 index 0000000..05111fa --- /dev/null +++ b/tests/test_addon_metadata.py @@ -0,0 +1,56 @@ +from __future__ import annotations +import hashlib +from pathlib import Path +import yaml + +ROOT = Path(__file__).parents[1] +REPOSITORY_URL = "https://git.radixadm.dk/dennis/homeassistant-stelloauth-addon.git" + +def load_yaml(path: str) -> dict: + with (ROOT / path).open(encoding="utf-8") as handle: + value = yaml.safe_load(handle) + assert isinstance(value, dict) + return value + +def test_repository_metadata() -> None: + metadata = load_yaml("repository.yaml") + assert metadata["url"] == REPOSITORY_URL + assert metadata["name"] == "Stelloauth for Home Assistant" + assert metadata["maintainer"] == "Dennis / Radix ApS" + +def test_addon_contract() -> None: + config = load_yaml("stelloauth/config.yaml") + assert config["slug"] == "stelloauth" + assert config["version"] == "0.1.0" + assert config["arch"] == ["amd64", "aarch64"] + assert config["startup"] == "application" + assert config["boot"] == "auto" + assert config["init"] is True + assert config["watchdog"] == "http://[HOST]:[PORT:8080]/" + assert config["ports"] == {"8080/tcp": None} + for key in ("ingress", "host_network", "privileged", "full_access", "docker_api", "devices", "map"): + assert key not in config + +def test_defaults_and_schema_are_aligned() -> None: + config = load_yaml("stelloauth/config.yaml") + assert config["options"] == { + "queue_timeout": "60s", + "rate_limit_count": 5, + "rate_limit_duration": "1h", + } + assert set(config["schema"]) == set(config["options"]) + assert config["schema"]["rate_limit_count"] == "int(1,20)" + +def test_repository_hostname_derivation() -> None: + repository_id = hashlib.sha1(REPOSITORY_URL.lower().encode()).hexdigest()[:8] + assert repository_id == "0031621f" + assert f"{repository_id}-stelloauth" == "0031621f-stelloauth" + +def test_translations_cover_every_option() -> None: + keys = set(load_yaml("stelloauth/config.yaml")["options"]) + for language in ("da", "en"): + translation = load_yaml(f"stelloauth/translations/{language}.yaml") + assert set(translation["configuration"]) == keys + for entry in translation["configuration"].values(): + assert set(entry) == {"name", "description"} + assert all(isinstance(value, str) and value.strip() for value in entry.values())