From 5c354c1ff5d8980a460a5e7496ef97191650178c Mon Sep 17 00:00:00 2001 From: dev_agent <17+dev_agent@code.cubecraftcreations.com> Date: Thu, 10 Sep 2026 16:50:59 -0400 Subject: [PATCH] feat: create custom_components/nestquest package structure and manifest.json (#1) --- custom_components/nestquest/__init__.py | 28 +++++++++ custom_components/nestquest/const.py | 2 + custom_components/nestquest/manifest.json | 12 ++++ pyproject.toml | 16 +++++ tests/test_manifest.py | 74 +++++++++++++++++++++++ 5 files changed, 132 insertions(+) create mode 100644 custom_components/nestquest/__init__.py create mode 100644 custom_components/nestquest/const.py create mode 100644 custom_components/nestquest/manifest.json create mode 100644 pyproject.toml create mode 100644 tests/test_manifest.py diff --git a/custom_components/nestquest/__init__.py b/custom_components/nestquest/__init__.py new file mode 100644 index 0000000..a3753ea --- /dev/null +++ b/custom_components/nestquest/__init__.py @@ -0,0 +1,28 @@ +"""The NestQuest integration.""" +from __future__ import annotations + +import logging +from typing import Any + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool: + """Set up the NestQuest integration. YAML configuration is not used, returns True.""" + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up NestQuest from a config entry.""" + hass.data.setdefault(DOMAIN, {}) + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + return True diff --git a/custom_components/nestquest/const.py b/custom_components/nestquest/const.py new file mode 100644 index 0000000..5da908d --- /dev/null +++ b/custom_components/nestquest/const.py @@ -0,0 +1,2 @@ +"""Constants for the NestQuest integration.""" +DOMAIN = "nestquest" diff --git a/custom_components/nestquest/manifest.json b/custom_components/nestquest/manifest.json new file mode 100644 index 0000000..9336ee0 --- /dev/null +++ b/custom_components/nestquest/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "nestquest", + "name": "NestQuest", + "codeowners": ["@overseer"], + "config_flow": true, + "documentation": "https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest", + "integration_type": "hub", + "iot_class": "local_polling", + "issue_tracker": "https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest/issues", + "requirements": [], + "version": "0.1.0" +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c34f265 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[project] +name = "nestquest" +version = "0.1.0" +description = "NestQuest Home Assistant Integration" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [] + +[dependency-groups] +dev = [ + "pytest>=8.0.0", +] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["."] diff --git a/tests/test_manifest.py b/tests/test_manifest.py new file mode 100644 index 0000000..e442a23 --- /dev/null +++ b/tests/test_manifest.py @@ -0,0 +1,74 @@ +"""Tests for the manifest.json file of NestQuest.""" +from __future__ import annotations + +import json +from pathlib import Path +import sys +from unittest.mock import MagicMock + +# Mock homeassistant modules if not installed in environment +for _mod in ("homeassistant", "homeassistant.core", "homeassistant.config_entries"): + sys.modules.setdefault(_mod, MagicMock()) + +import pytest + +from custom_components.nestquest.const import DOMAIN + +MANIFEST_PATH = Path(__file__).parent.parent / "custom_components" / "nestquest" / "manifest.json" + + +@pytest.fixture +def manifest() -> dict: + """Load and parse manifest.json.""" + assert MANIFEST_PATH.exists(), f"manifest.json does not exist at {MANIFEST_PATH}" + with open(MANIFEST_PATH, encoding="utf-8") as f: + data = json.load(f) + return data + + +def test_manifest_is_valid_json(manifest: dict) -> None: + """Verify manifest.json is valid JSON and parsed into a dictionary.""" + assert isinstance(manifest, dict) + + +def test_manifest_domain(manifest: dict) -> None: + """Verify domain is nestquest.""" + assert manifest.get("domain") == "nestquest" + + +def test_manifest_domain_matches_const(manifest: dict) -> None: + """Verify domain matches DOMAIN constant from const.py.""" + assert manifest.get("domain") == DOMAIN + + +def test_manifest_version(manifest: dict) -> None: + """Verify version is a non-empty string.""" + assert isinstance(manifest.get("version"), str) + assert len(manifest["version"]) > 0 + + +def test_manifest_config_flow(manifest: dict) -> None: + """Verify config_flow is True.""" + assert manifest.get("config_flow") is True + + +def test_manifest_iot_class(manifest: dict) -> None: + """Verify iot_class is local_polling.""" + assert manifest.get("iot_class") == "local_polling" + + +def test_manifest_requirements(manifest: dict) -> None: + """Verify requirements is an empty list.""" + assert manifest.get("requirements") == [] + + +@pytest.mark.parametrize( + "key", + ["name", "version", "documentation", "issue_tracker", "codeowners"], +) +def test_manifest_required_keys_exist_and_non_empty(manifest: dict, key: str) -> None: + """Verify required keys exist and are non-empty.""" + assert key in manifest, f"Key '{key}' missing from manifest.json" + val = manifest[key] + assert val is not None, f"Key '{key}' is None" + assert len(val) > 0, f"Key '{key}' is empty"