Add authentication support to Gatus integration (#178663)

This commit is contained in:
Hamish
2026-08-26 20:12:21 +02:00
committed by GitHub
parent af87bac7f7
commit bd38ea70fc
6 changed files with 314 additions and 16 deletions
+142 -1
View File
@@ -2,7 +2,7 @@
from unittest.mock import AsyncMock
from gatus_api import GatusClientError
from gatus_api import GatusAuthError, GatusClientError
import pytest
from homeassistant import config_entries
@@ -65,6 +65,7 @@ async def test_form_success_with_path(
("side_effect", "error_key"),
[
(GatusClientError("Cannot connect"), "cannot_connect"),
(GatusAuthError("401 Unauthorized"), "invalid_auth"),
(Exception("Unexpected backend explosion"), "unknown"),
],
)
@@ -148,6 +149,7 @@ async def test_flow_reconfigure(
("side_effect", "error_key"),
[
(GatusClientError("Cannot connect"), "cannot_connect"),
(GatusAuthError("401 Unauthorized"), "invalid_auth"),
(Exception("Unexpected backend explosion"), "unknown"),
],
)
@@ -215,3 +217,142 @@ async def test_flow_reconfigure_already_configured(
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@pytest.mark.usefixtures("mock_gatus_client")
async def test_form_success_with_credentials(
hass: HomeAssistant, mock_setup_entry: AsyncMock
) -> None:
"""Test setup with username, password, and token credentials."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_URL: "http://gatus.example.com:8080",
"username": "user",
"password": "pass",
"token": "secret_token",
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {
CONF_URL: "http://gatus.example.com:8080",
"username": "user",
"password": "pass",
"token": "secret_token",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_gatus_client")
async def test_flow_reconfigure_credentials(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test reconfigure flow updating stored credentials."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_URL: "http://gatus.example.com:8080",
"username": "new_user",
"password": "new_password",
"token": "new_token",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config_entry.data == {
CONF_URL: "http://gatus.example.com:8080",
"username": "new_user",
"password": "new_password",
"token": "new_token",
}
@pytest.mark.usefixtures("mock_gatus_client")
async def test_flow_reauth(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test reauth flow."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reauth_confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"username": "reauth_user",
"password": "reauth_password",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
assert mock_config_entry.data == {
CONF_URL: "http://gatus.example.com:8080",
"username": "reauth_user",
"password": "reauth_password",
}
@pytest.mark.parametrize(
("side_effect", "error_key"),
[
(GatusClientError("Cannot connect"), "cannot_connect"),
(GatusAuthError("401 Unauthorized"), "invalid_auth"),
(Exception("Unexpected backend explosion"), "unknown"),
],
)
async def test_flow_reauth_errors(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_gatus_client: AsyncMock,
side_effect: Exception,
error_key: str,
) -> None:
"""Test reauth flow errors and recovery."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reauth_confirm"
mock_gatus_client.get_endpoints_statuses.side_effect = side_effect
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"username": "user", "password": "wrong_password"},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error_key}
mock_gatus_client.get_endpoints_statuses.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"username": "user", "password": "correct_password"},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
assert mock_config_entry.data == {
CONF_URL: "http://gatus.example.com:8080",
"username": "user",
"password": "correct_password",
}
+16 -1
View File
@@ -3,7 +3,7 @@
from unittest.mock import AsyncMock
from freezegun.api import FrozenDateTimeFactory
from gatus_api import GatusClientError
from gatus_api import GatusAuthError, GatusClientError
import pytest
from homeassistant.components.gatus.coordinator import GatusDataUpdateCoordinator
@@ -48,6 +48,21 @@ async def test_setup_failure_retry(
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
async def test_setup_failure_auth(
hass: HomeAssistant,
mock_gatus_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that an authentication failure places the entry in SETUP_ERROR state."""
mock_gatus_client.get_endpoints_statuses.side_effect = GatusAuthError(
"401 Unauthorized"
)
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
@pytest.mark.usefixtures("mock_gatus_client")
async def test_remove_stale_device_runtime(
hass: HomeAssistant,