From 7f40c54535076c7e198f22762fbb622eb2830d72 Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:21:14 +0200 Subject: [PATCH 1/9] add 'test' Github action --- .github/workflows/test.yaml | 33 ++++++++++++++ tests/requirements-dev.txt | 4 ++ tests/test_config_flow.py | 90 +++++++++++++++++++++++++++++++++++++ tests/test_options_flow.py | 60 +++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 .github/workflows/test.yaml create mode 100644 tests/requirements-dev.txt create mode 100644 tests/test_config_flow.py create mode 100644 tests/test_options_flow.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..7cd5a2f9 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,33 @@ +name: Luxtronik Integration Tests + +on: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r tests/requirements-dev.txt + + - name: Run tests with coverage + run: | + pytest --cov=custom_components.luxtronik --cov-report=xml --cov-report=term tests/ + + - name: Upload coverage report + uses: actions/upload-artifact@v3 + with: + name: coverage-report + path: coverage.xml + diff --git a/tests/requirements-dev.txt b/tests/requirements-dev.txt new file mode 100644 index 00000000..6de9fdeb --- /dev/null +++ b/tests/requirements-dev.txt @@ -0,0 +1,4 @@ +pytest +pytest-asyncio +pytest-homeassistant-custom-components +pytest-cov \ No newline at end of file diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py new file mode 100644 index 00000000..0540170c --- /dev/null +++ b/tests/test_config_flow.py @@ -0,0 +1,90 @@ +import pytest +from unittest.mock import AsyncMock, MagicMock + +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo + +from custom_components.luxtronik.config_flow import LuxtronikFlowHandler, LuxtronikOptionsFlowHandler +from custom_components.luxtronik.const import DOMAIN, CONF_HOST, CONF_PORT, DEFAULT_PORT + +@pytest.fixture +def mock_hass(): + hass = MagicMock(spec=HomeAssistant) + hass.async_add_executor_job = AsyncMock() + hass.config_entries.async_entries = MagicMock(return_value=[]) + hass.config_entries.async_update_entry = AsyncMock() + hass.config_entries.async_reload = AsyncMock() + return hass + +@pytest.fixture +def flow_handler(mock_hass): + handler = LuxtronikFlowHandler() + handler.hass = mock_hass + return handler + +@pytest.mark.asyncio +async def test_async_step_user_with_available_devices(flow_handler): + flow_handler._async_current_entries = MagicMock(return_value=[]) + flow_handler._discover_devices = AsyncMock(return_value=[("192.168.1.100", 8888)]) + + result = await flow_handler.async_step_user() + + assert result["type"] == "form" + assert result["step_id"] == "select_devices" + +@pytest.mark.asyncio +async def test_async_step_user_with_no_available_devices(flow_handler): + flow_handler._async_current_entries = MagicMock(return_value=[ + MagicMock(data={CONF_HOST: "192.168.1.100", CONF_PORT: 8888}) + ]) + flow_handler._discover_devices = AsyncMock(return_value=[("192.168.1.100", 8888)]) + + result = await flow_handler.async_step_user() + + assert result["type"] == "form" + assert result["step_id"] == "manual_entry" + +@pytest.mark.asyncio +async def test_async_step_select_devices_success(flow_handler): + flow_handler._connect_and_get_coordinator = AsyncMock(return_value=MagicMock(unique_id="123")) + flow_handler._set_unique_id_or_abort = AsyncMock(return_value=True) + + result = await flow_handler.async_step_select_devices({"selected_devices": ["192.168.1.100:8888"]}) + + assert result["type"] == "create_entry" + assert result["title"] == "192.168.1.100:8888" + +@pytest.mark.asyncio +async def test_async_step_manual_entry_success(flow_handler): + flow_handler._connect_and_get_coordinator = AsyncMock(return_value=MagicMock(unique_id="123")) + flow_handler._set_unique_id_or_abort = AsyncMock(return_value=True) + + result = await flow_handler.async_step_manual_entry({CONF_HOST: "192.168.1.100", CONF_PORT: 8888}) + + assert result["type"] == "create_entry" + assert result["title"] == "192.168.1.100:8888" + +@pytest.mark.asyncio +async def test_async_step_dhcp_success(flow_handler): + flow_handler._discover_devices = AsyncMock(return_value=[("192.168.1.100", 8888)]) + flow_handler._connect_and_get_coordinator = AsyncMock(return_value=MagicMock(unique_id="123")) + flow_handler._set_unique_id_or_abort = AsyncMock(return_value=True) + + dhcp_info = MagicMock(ip="192.168.1.100", hostname="luxtronik") + result = await flow_handler.async_step_dhcp(dhcp_info) + + assert result["type"] == "create_entry" + assert result["title"] == "192.168.1.100:8888" + +@pytest.mark.asyncio +async def test_options_flow_update_options(): + config_entry = MagicMock(data={CONF_HOST: "192.168.1.100", CONF_PORT: 8888}, options={}) + handler = LuxtronikOptionsFlowHandler(config_entry) + handler.hass = MagicMock() + handler.hass.config_entries.async_update_entry = AsyncMock() + handler.hass.config_entries.async_reload = AsyncMock() + + result = await handler.async_step_user({}) + + assert result["type"] == "create_entry" diff --git a/tests/test_options_flow.py b/tests/test_options_flow.py new file mode 100644 index 00000000..b15f41bb --- /dev/null +++ b/tests/test_options_flow.py @@ -0,0 +1,60 @@ +import pytest +from homeassistant import data_entry_flow +from homeassistant.core import HomeAssistant +from custom_components.luxtronik.config_flow import LuxtronikOptionsFlowHandler +from custom_components.luxtronik.const import DOMAIN, CONF_HA_SENSOR_INDOOR_TEMPERATURE, CONF_HOST, CONF_PORT + +@pytest.fixture +def mock_config_entry(): + class MockConfigEntry: + data = { + CONF_HOST: "192.168.1.100", + CONF_PORT: 8888, + } + options = {} + entry_id = "test_entry" + return MockConfigEntry() + +@pytest.fixture +def mock_connect(monkeypatch): + class MockCoordinator: + manufacturer = "Luxtronik" + model = "HP" + serial_number = "123456789" + monkeypatch.setattr( + "custom_components.luxtronik.coordinator.LuxtronikCoordinator.connect", + lambda hass, config: MockCoordinator() + ) + return MockCoordinator() + +@pytest.mark.asyncio +async def test_options_flow_form_rendering(hass: HomeAssistant, mock_config_entry, mock_connect): + flow = LuxtronikOptionsFlowHandler(mock_config_entry) + flow.hass = hass + + result = await flow.async_step_user() + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert "data_schema" in result + +@pytest.mark.asyncio +async def test_options_flow_updates_options(hass: HomeAssistant, mock_config_entry, mock_connect): + flow = LuxtronikOptionsFlowHandler(mock_config_entry) + flow.hass = hass + + user_input = { + CONF_HA_SENSOR_INDOOR_TEMPERATURE: "sensor.indoor_temp" + } + + result = await flow.async_step_user(user_input) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == {} + +@pytest.mark.asyncio +async def test_options_flow_init_redirects_to_user(hass: HomeAssistant, mock_config_entry): + flow = LuxtronikOptionsFlowHandler(mock_config_entry) + flow.hass = hass + + result = await flow.async_step_init() + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" From 080a6fa7ac2e781b9fd5aa137a65d4ecde4cc15c Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:34:33 +0200 Subject: [PATCH 2/9] fix import problem --- tests/test_config_flow.py | 6 +++++- tests/test_options_flow.py | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 0540170c..072826d9 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -1,12 +1,16 @@ +pytestmark = pytest.mark.enable_custom_integrations + import pytest from unittest.mock import AsyncMock, MagicMock from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo +from homeassistant.const import CONF_HOST, CONF_PORT, DEFAULT_PORT from custom_components.luxtronik.config_flow import LuxtronikFlowHandler, LuxtronikOptionsFlowHandler -from custom_components.luxtronik.const import DOMAIN, CONF_HOST, CONF_PORT, DEFAULT_PORT +from custom_components.luxtronik.const import DOMAIN + @pytest.fixture def mock_hass(): diff --git a/tests/test_options_flow.py b/tests/test_options_flow.py index b15f41bb..9c868941 100644 --- a/tests/test_options_flow.py +++ b/tests/test_options_flow.py @@ -1,8 +1,13 @@ +pytestmark = pytest.mark.enable_custom_integrations + import pytest from homeassistant import data_entry_flow from homeassistant.core import HomeAssistant -from custom_components.luxtronik.config_flow import LuxtronikOptionsFlowHandler -from custom_components.luxtronik.const import DOMAIN, CONF_HA_SENSOR_INDOOR_TEMPERATURE, CONF_HOST, CONF_PORT +from homeassistant.const import CONF_HOST, CONF_PORT, DEFAULT_PORT + +from custom_components.luxtronik.config_flow import LuxtronikFlowHandler, LuxtronikOptionsFlowHandler +from custom_components.luxtronik.const import DOMAIN,CONF_HA_SENSOR_INDOOR_TEMPERATURE + @pytest.fixture def mock_config_entry(): From 34861cfae06a53b29bdf93349c01c28e3741ed65 Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:37:32 +0200 Subject: [PATCH 3/9] fix import pytest order --- tests/test_config_flow.py | 2 +- tests/test_options_flow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 072826d9..ad443cd7 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -1,6 +1,6 @@ +import pytest pytestmark = pytest.mark.enable_custom_integrations -import pytest from unittest.mock import AsyncMock, MagicMock from homeassistant.core import HomeAssistant diff --git a/tests/test_options_flow.py b/tests/test_options_flow.py index 9c868941..0d64689a 100644 --- a/tests/test_options_flow.py +++ b/tests/test_options_flow.py @@ -1,6 +1,6 @@ +import pytest pytestmark = pytest.mark.enable_custom_integrations -import pytest from homeassistant import data_entry_flow from homeassistant.core import HomeAssistant from homeassistant.const import CONF_HOST, CONF_PORT, DEFAULT_PORT From f0f4b73e6d12a8d371760d70a8f7ae42145ca1db Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:40:28 +0200 Subject: [PATCH 4/9] fix DEFAULT_PORT import --- tests/test_config_flow.py | 4 ++-- tests/test_options_flow.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index ad443cd7..72176679 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -6,10 +6,10 @@ from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo -from homeassistant.const import CONF_HOST, CONF_PORT, DEFAULT_PORT +from homeassistant.const import CONF_HOST, CONF_PORT from custom_components.luxtronik.config_flow import LuxtronikFlowHandler, LuxtronikOptionsFlowHandler -from custom_components.luxtronik.const import DOMAIN +from custom_components.luxtronik.const import DOMAIN, DEFAULT_PORT @pytest.fixture diff --git a/tests/test_options_flow.py b/tests/test_options_flow.py index 0d64689a..54b7de6c 100644 --- a/tests/test_options_flow.py +++ b/tests/test_options_flow.py @@ -3,10 +3,10 @@ from homeassistant import data_entry_flow from homeassistant.core import HomeAssistant -from homeassistant.const import CONF_HOST, CONF_PORT, DEFAULT_PORT +from homeassistant.const import CONF_HOST, CONF_PORT from custom_components.luxtronik.config_flow import LuxtronikFlowHandler, LuxtronikOptionsFlowHandler -from custom_components.luxtronik.const import DOMAIN,CONF_HA_SENSOR_INDOOR_TEMPERATURE +from custom_components.luxtronik.const import DOMAIN,CONF_HA_SENSOR_INDOOR_TEMPERATURE, DEFAULT_PORT @pytest.fixture From a716f5c18d620e82965a956571955845f7779561 Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:49:42 +0200 Subject: [PATCH 5/9] Update python-package.yml add tests/requirements-dev.txt --- .github/workflows/python-package.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 9fe5d356..24829cd1 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -29,6 +29,7 @@ jobs: python -m pip install --upgrade pip python -m pip install flake8 pytest homeassistant luxtronik==0.3.14 requests>=2.28.2 getmac==0.8.2 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + if [ -f tests/requirements-dev.txt ]; then pip install -r tests/requirements-dev.txt; fi - name: Set PYTHONPATH run: echo "PYTHONPATH=$PYTHONPATH:$(pwd)" >> $GITHUB_ENV - name: Lint with flake8 From f66f4543d2f58e2a6613ae6bd7b500e8f3b2089d Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:58:02 +0200 Subject: [PATCH 6/9] fix pip install pytest-homeassistant-custom-components --- tests/requirements-dev.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/requirements-dev.txt b/tests/requirements-dev.txt index 6de9fdeb..46e8ca82 100644 --- a/tests/requirements-dev.txt +++ b/tests/requirements-dev.txt @@ -1,4 +1,4 @@ pytest pytest-asyncio -pytest-homeassistant-custom-components -pytest-cov \ No newline at end of file +pytest-cov +git+https://github.com/MatthewFlamm/pytest-homeassistant-custom-components.git \ No newline at end of file From 7e080c923e4278f325f3b0caba6a9c64d504d729 Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 02:03:27 +0200 Subject: [PATCH 7/9] more pip install fixes... --- .github/workflows/python-package.yml | 1 + .github/workflows/test.yaml | 1 + tests/requirements-dev.txt | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 24829cd1..b4756e44 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -30,6 +30,7 @@ jobs: python -m pip install flake8 pytest homeassistant luxtronik==0.3.14 requests>=2.28.2 getmac==0.8.2 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f tests/requirements-dev.txt ]; then pip install -r tests/requirements-dev.txt; fi + pip install git+https://github.com/MatthewFlamm/pytest-homeassistant-custom-components.git - name: Set PYTHONPATH run: echo "PYTHONPATH=$PYTHONPATH:$(pwd)" >> $GITHUB_ENV - name: Lint with flake8 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7cd5a2f9..cd713493 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,6 +20,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r tests/requirements-dev.txt + pip install git+https://github.com/MatthewFlamm/pytest-homeassistant-custom-components.git - name: Run tests with coverage run: | diff --git a/tests/requirements-dev.txt b/tests/requirements-dev.txt index 46e8ca82..796995a9 100644 --- a/tests/requirements-dev.txt +++ b/tests/requirements-dev.txt @@ -1,4 +1,3 @@ pytest pytest-asyncio -pytest-cov -git+https://github.com/MatthewFlamm/pytest-homeassistant-custom-components.git \ No newline at end of file +pytest-cov \ No newline at end of file From 0028abd521ccaa724943acd876f7caa1f16d5324 Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 02:17:22 +0200 Subject: [PATCH 8/9] More tricks to install pytest-homeassistant-custom-components --- .github/workflows/python-package.yml | 10 +++++++++- .github/workflows/test.yaml | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index b4756e44..ee5707c7 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -24,13 +24,21 @@ jobs: uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} + + - name: Checkout plugin repo + uses: actions/checkout@v5 + with: + repository: MatthewFlamm/pytest-homeassistant-custom-components + path: pytest-homeassistant-custom-components + - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install flake8 pytest homeassistant luxtronik==0.3.14 requests>=2.28.2 getmac==0.8.2 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f tests/requirements-dev.txt ]; then pip install -r tests/requirements-dev.txt; fi - pip install git+https://github.com/MatthewFlamm/pytest-homeassistant-custom-components.git + pip install ./pytest-homeassistant-custom-components + - name: Set PYTHONPATH run: echo "PYTHONPATH=$PYTHONPATH:$(pwd)" >> $GITHUB_ENV - name: Lint with flake8 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index cd713493..f6310ad9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,11 +16,17 @@ jobs: with: python-version: '3.11' + - name: Checkout plugin repo + uses: actions/checkout@v3 + with: + repository: MatthewFlamm/pytest-homeassistant-custom-components + path: pytest-homeassistant-custom-components + - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r tests/requirements-dev.txt - pip install git+https://github.com/MatthewFlamm/pytest-homeassistant-custom-components.git + pip install ./pytest-homeassistant-custom-components - name: Run tests with coverage run: | From b275dc944adfa3663bdacc57bdebdf5ad2227d56 Mon Sep 17 00:00:00 2001 From: rhammen <75572839+rhammen@users.noreply.github.com> Date: Wed, 17 Sep 2025 02:45:18 +0200 Subject: [PATCH 9/9] pip install pytest-homeassistant-custom-component --- .github/workflows/python-package.yml | 7 ------- .github/workflows/{test.yaml => test.yml} | 7 ------- tests/requirements-dev.txt | 3 ++- 3 files changed, 2 insertions(+), 15 deletions(-) rename .github/workflows/{test.yaml => test.yml} (72%) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ee5707c7..e8457c48 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -25,19 +25,12 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Checkout plugin repo - uses: actions/checkout@v5 - with: - repository: MatthewFlamm/pytest-homeassistant-custom-components - path: pytest-homeassistant-custom-components - - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install flake8 pytest homeassistant luxtronik==0.3.14 requests>=2.28.2 getmac==0.8.2 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f tests/requirements-dev.txt ]; then pip install -r tests/requirements-dev.txt; fi - pip install ./pytest-homeassistant-custom-components - name: Set PYTHONPATH run: echo "PYTHONPATH=$PYTHONPATH:$(pwd)" >> $GITHUB_ENV diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yml similarity index 72% rename from .github/workflows/test.yaml rename to .github/workflows/test.yml index f6310ad9..7cd5a2f9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yml @@ -16,17 +16,10 @@ jobs: with: python-version: '3.11' - - name: Checkout plugin repo - uses: actions/checkout@v3 - with: - repository: MatthewFlamm/pytest-homeassistant-custom-components - path: pytest-homeassistant-custom-components - - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r tests/requirements-dev.txt - pip install ./pytest-homeassistant-custom-components - name: Run tests with coverage run: | diff --git a/tests/requirements-dev.txt b/tests/requirements-dev.txt index 796995a9..6a3c93ee 100644 --- a/tests/requirements-dev.txt +++ b/tests/requirements-dev.txt @@ -1,3 +1,4 @@ pytest pytest-asyncio -pytest-cov \ No newline at end of file +pytest-cov +pytest-homeassistant-custom-component \ No newline at end of file