650 lines
23 KiB
Python
650 lines
23 KiB
Python
"""Tests for the UI config flow"""
|
|
|
|
import pytest
|
|
|
|
from homeassistant import config_entries
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.data_entry_flow import FlowResultType
|
|
|
|
|
|
from custom_components.auth_oidc import DOMAIN
|
|
from custom_components.auth_oidc.config.const import (
|
|
OIDC_PROVIDERS,
|
|
CLIENT_ID,
|
|
CLIENT_SECRET,
|
|
DISCOVERY_URL,
|
|
DISPLAY_NAME,
|
|
FEATURES,
|
|
FEATURES_AUTOMATIC_USER_LINKING,
|
|
FEATURES_AUTOMATIC_PERSON_CREATION,
|
|
FEATURES_INCLUDE_GROUPS_SCOPE,
|
|
CLAIMS,
|
|
CLAIMS_DISPLAY_NAME,
|
|
CLAIMS_GROUPS,
|
|
CLAIMS_USERNAME,
|
|
ROLES,
|
|
ROLE_ADMINS,
|
|
ROLE_USERS,
|
|
)
|
|
|
|
from .mocks.oidc_server import MockOIDCServer, mock_oidc_responses
|
|
|
|
DEMO_CLIENT_ID = "testing_example_client_id"
|
|
DEMO_CLIENT_SECRET = "faz"
|
|
DEMO_ADMIN_ROLE = "boo"
|
|
DEMO_USER_ROLE = "far"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_full_config_flow_success(hass: HomeAssistant):
|
|
"""Test a successful full config flow."""
|
|
|
|
with mock_oidc_responses():
|
|
# 1. Start the user step
|
|
# This simulates clicking "Add Integration" in the UI.
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
|
|
# Assert that it's a form and expects user input for the 'user' step
|
|
# 'user' is always the first step if it is user triggered
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "user"
|
|
assert result["data_schema"] is not None
|
|
schema = result["data_schema"]
|
|
# Extract the schema dict from voluptuous Schema
|
|
schema_dict = schema.schema
|
|
# Assert 'provider' is a key in the schema
|
|
assert "provider" in schema_dict
|
|
# Assert 'authentik' is one of the allowed values for 'provider'
|
|
provider_field = schema_dict["provider"]
|
|
# If provider_field is a voluptuous In validator, get its container
|
|
allowed_providers = getattr(provider_field, "container", None)
|
|
assert "authentik" in OIDC_PROVIDERS
|
|
assert allowed_providers is not None and "authentik" in allowed_providers
|
|
|
|
assert result["errors"] == {}
|
|
|
|
# 2. Submit user input for the 'user' step
|
|
# This simulates the user filling out host/port
|
|
user_input_step_user = {"provider": "authentik"}
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], user_input_step_user
|
|
)
|
|
|
|
# Assert that it proceeds to the 'auth' step
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "discovery_url"
|
|
assert result["data_schema"] is not None
|
|
assert result["errors"] == {}
|
|
|
|
# Fill in the discovery URL
|
|
user_input_step_discovery = {
|
|
"discovery_url": MockOIDCServer.get_discovery_url()
|
|
}
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], user_input_step_discovery
|
|
)
|
|
|
|
# Assert that it proceeds to the 'credentials' step
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "validate_connection"
|
|
|
|
# Assert that it validates correctly with our mock
|
|
assert result["errors"] == {}
|
|
|
|
# Send in continue
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"action": "continue"}
|
|
)
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "client_config"
|
|
assert result["data_schema"] is not None
|
|
assert result["errors"] == {}
|
|
|
|
# Fill in the client config
|
|
user_input_step_client_config = {
|
|
"client_id": DEMO_CLIENT_ID,
|
|
"client_secret": DEMO_CLIENT_SECRET,
|
|
}
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], user_input_step_client_config
|
|
)
|
|
|
|
# Assert that we are at groups_config
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "groups_config"
|
|
assert result["data_schema"] is not None
|
|
assert result["errors"] == {}
|
|
|
|
# Fill in the groups config
|
|
user_input_step_groups_config = {
|
|
"admin_group": DEMO_ADMIN_ROLE,
|
|
"user_group": DEMO_USER_ROLE,
|
|
}
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], user_input_step_groups_config
|
|
)
|
|
|
|
# Assert that were are at user_linking config
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "user_linking"
|
|
assert result["data_schema"] is not None
|
|
assert result["errors"] == {}
|
|
|
|
# Fill in the user linking config
|
|
user_input_step_user_linking = {"enable_user_linking": False}
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], user_input_step_user_linking
|
|
)
|
|
|
|
# Finally, assert that the flow is complete and a config entry is created
|
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
|
assert result["title"] == OIDC_PROVIDERS["authentik"]["name"]
|
|
|
|
expected_data = {
|
|
"provider": "authentik",
|
|
CLIENT_ID: DEMO_CLIENT_ID,
|
|
CLIENT_SECRET: DEMO_CLIENT_SECRET,
|
|
DISCOVERY_URL: MockOIDCServer.get_discovery_url(),
|
|
DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["name"],
|
|
FEATURES: {
|
|
FEATURES_AUTOMATIC_USER_LINKING: False,
|
|
FEATURES_AUTOMATIC_PERSON_CREATION: True,
|
|
FEATURES_INCLUDE_GROUPS_SCOPE: True,
|
|
},
|
|
CLAIMS: {
|
|
CLAIMS_DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["claims"][
|
|
"display_name"
|
|
],
|
|
CLAIMS_USERNAME: OIDC_PROVIDERS["authentik"]["claims"]["username"],
|
|
CLAIMS_GROUPS: OIDC_PROVIDERS["authentik"]["claims"]["groups"],
|
|
},
|
|
ROLES: {ROLE_ADMINS: DEMO_ADMIN_ROLE, ROLE_USERS: DEMO_USER_ROLE},
|
|
}
|
|
|
|
assert result["data"] == expected_data
|
|
|
|
# Verify that the config entry was loaded into Home Assistant
|
|
entries = hass.config_entries.async_entries(DOMAIN)
|
|
assert len(entries) == 1
|
|
assert entries[0].data == expected_data
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_options_flow_success(hass: HomeAssistant):
|
|
"""Test a successful options flow."""
|
|
|
|
# First, set up an initial config entry as in the full config flow
|
|
initial_data = {
|
|
"provider": "authentik",
|
|
CLIENT_ID: DEMO_CLIENT_ID,
|
|
CLIENT_SECRET: DEMO_CLIENT_SECRET,
|
|
DISCOVERY_URL: MockOIDCServer.get_discovery_url(),
|
|
DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["name"],
|
|
FEATURES: {
|
|
FEATURES_AUTOMATIC_USER_LINKING: False,
|
|
FEATURES_AUTOMATIC_PERSON_CREATION: True,
|
|
FEATURES_INCLUDE_GROUPS_SCOPE: True,
|
|
},
|
|
CLAIMS: {
|
|
CLAIMS_DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["claims"]["display_name"],
|
|
CLAIMS_USERNAME: OIDC_PROVIDERS["authentik"]["claims"]["username"],
|
|
CLAIMS_GROUPS: OIDC_PROVIDERS["authentik"]["claims"]["groups"],
|
|
},
|
|
ROLES: {ROLE_ADMINS: DEMO_ADMIN_ROLE, ROLE_USERS: DEMO_USER_ROLE},
|
|
}
|
|
|
|
entry = config_entries.ConfigEntry(
|
|
version=1,
|
|
minor_version=0,
|
|
domain=DOMAIN,
|
|
title=OIDC_PROVIDERS["authentik"]["name"],
|
|
data=initial_data,
|
|
source=config_entries.SOURCE_USER,
|
|
entry_id="1",
|
|
unique_id="test_unique_id",
|
|
options={},
|
|
pref_disable_new_entities=False,
|
|
pref_disable_polling=False,
|
|
discovery_keys=[],
|
|
subentries_data=None,
|
|
)
|
|
|
|
await hass.config_entries.async_add(entry)
|
|
|
|
# Start the reconfigure flow
|
|
result = await hass.config_entries.options.async_init(entry.entry_id)
|
|
|
|
# Should start the options flow
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "init"
|
|
assert result["data_schema"] is not None
|
|
|
|
# Assert that the schema is as expected
|
|
# Schema contains enable_user_linking, enable_groups, admin_group & user_groups and no other keys
|
|
schema = result["data_schema"]
|
|
schema_dict = schema.schema
|
|
# Assert that the schema contains the expected keys
|
|
expected_keys = {
|
|
"admin_group",
|
|
"enable_user_linking",
|
|
"enable_groups",
|
|
"user_group",
|
|
}
|
|
assert set(schema_dict.keys()) == expected_keys
|
|
|
|
# Change the client_id and client_secret
|
|
new_enable_linking = True
|
|
new_enable_groups = True
|
|
new_admin_group = "bazzbbb"
|
|
new_user_group = "foobar"
|
|
|
|
result = await hass.config_entries.options.async_configure(
|
|
result["flow_id"],
|
|
{
|
|
"enable_user_linking": new_enable_linking,
|
|
"enable_groups": new_enable_groups,
|
|
"admin_group": new_admin_group,
|
|
"user_group": new_user_group,
|
|
},
|
|
)
|
|
|
|
# Should finish and update the entry options
|
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
|
|
|
# Optionally, check that the entry options are updated
|
|
updated_entry = hass.config_entries.async_get_entry(entry.entry_id)
|
|
assert updated_entry is not None
|
|
|
|
# Verify that the config entry was loaded into Home Assistant
|
|
entries = hass.config_entries.async_entries(DOMAIN)
|
|
assert len(entries) == 1
|
|
|
|
assert (
|
|
entries[0].data[FEATURES][FEATURES_AUTOMATIC_USER_LINKING] == new_enable_linking
|
|
)
|
|
assert entries[0].data[FEATURES][FEATURES_INCLUDE_GROUPS_SCOPE] == new_enable_groups
|
|
assert entries[0].data[ROLES][ROLE_ADMINS] == new_admin_group
|
|
assert entries[0].data[ROLES][ROLE_USERS] == new_user_group
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reconfigure_flow_success(hass: HomeAssistant):
|
|
"""Test a successful reconfigure flow."""
|
|
|
|
# First, set up an initial config entry as in the full config flow
|
|
initial_data = {
|
|
"provider": "authentik",
|
|
CLIENT_ID: DEMO_CLIENT_ID,
|
|
CLIENT_SECRET: DEMO_CLIENT_SECRET,
|
|
DISCOVERY_URL: MockOIDCServer.get_discovery_url(),
|
|
DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["name"],
|
|
FEATURES: {
|
|
FEATURES_AUTOMATIC_USER_LINKING: False,
|
|
FEATURES_AUTOMATIC_PERSON_CREATION: True,
|
|
FEATURES_INCLUDE_GROUPS_SCOPE: True,
|
|
},
|
|
CLAIMS: {
|
|
CLAIMS_DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["claims"]["display_name"],
|
|
CLAIMS_USERNAME: OIDC_PROVIDERS["authentik"]["claims"]["username"],
|
|
CLAIMS_GROUPS: OIDC_PROVIDERS["authentik"]["claims"]["groups"],
|
|
},
|
|
ROLES: {ROLE_ADMINS: DEMO_ADMIN_ROLE, ROLE_USERS: DEMO_USER_ROLE},
|
|
}
|
|
|
|
entry = config_entries.ConfigEntry(
|
|
version=1,
|
|
minor_version=0,
|
|
domain=DOMAIN,
|
|
title=OIDC_PROVIDERS["authentik"]["name"],
|
|
data=initial_data,
|
|
source=config_entries.SOURCE_USER,
|
|
entry_id="1",
|
|
unique_id="test_unique_id",
|
|
options={},
|
|
pref_disable_new_entities=False,
|
|
pref_disable_polling=False,
|
|
discovery_keys=[],
|
|
subentries_data=None,
|
|
)
|
|
|
|
await hass.config_entries.async_add(entry)
|
|
|
|
# Start async_step_reconfigure to reconfigure the entry
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={
|
|
"source": config_entries.SOURCE_RECONFIGURE,
|
|
"entry_id": entry.entry_id,
|
|
},
|
|
)
|
|
|
|
# Should start the reconfigure flow
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "reconfigure"
|
|
assert result["data_schema"] is not None
|
|
|
|
# Assert that the schema is client_id & client_secret
|
|
schema = result["data_schema"]
|
|
schema_dict = schema.schema
|
|
# Assert that the schema contains the expected keys
|
|
expected_keys = {
|
|
"client_id",
|
|
"client_secret",
|
|
}
|
|
assert set(schema_dict.keys()) == expected_keys
|
|
|
|
# Change the client_id and client_secret
|
|
new_client_id = "newclientid"
|
|
new_client_secret = "newclientsecret"
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{
|
|
"client_id": new_client_id,
|
|
"client_secret": new_client_secret,
|
|
},
|
|
)
|
|
|
|
# Should finish and update the entry data
|
|
assert result["type"] == FlowResultType.ABORT
|
|
assert result["reason"] == "reconfigure_successful"
|
|
|
|
# Verify that the config entry was loaded into Home Assistant
|
|
entries = hass.config_entries.async_entries(DOMAIN)
|
|
assert len(entries) == 1
|
|
assert entries[0].data[CLIENT_ID] == new_client_id
|
|
assert entries[0].data[CLIENT_SECRET] == new_client_secret
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reconfigure_flow_rejects_invalid_client_id(hass: HomeAssistant):
|
|
"""Reconfigure should keep the form open when the client ID is invalid."""
|
|
initial_data = {
|
|
"provider": "authentik",
|
|
CLIENT_ID: DEMO_CLIENT_ID,
|
|
CLIENT_SECRET: DEMO_CLIENT_SECRET,
|
|
DISCOVERY_URL: MockOIDCServer.get_discovery_url(),
|
|
DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["name"],
|
|
FEATURES: {
|
|
FEATURES_AUTOMATIC_USER_LINKING: False,
|
|
FEATURES_AUTOMATIC_PERSON_CREATION: True,
|
|
FEATURES_INCLUDE_GROUPS_SCOPE: True,
|
|
},
|
|
CLAIMS: {
|
|
CLAIMS_DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["claims"]["display_name"],
|
|
CLAIMS_USERNAME: OIDC_PROVIDERS["authentik"]["claims"]["username"],
|
|
CLAIMS_GROUPS: OIDC_PROVIDERS["authentik"]["claims"]["groups"],
|
|
},
|
|
ROLES: {ROLE_ADMINS: DEMO_ADMIN_ROLE, ROLE_USERS: DEMO_USER_ROLE},
|
|
}
|
|
|
|
entry = config_entries.ConfigEntry(
|
|
version=1,
|
|
minor_version=0,
|
|
domain=DOMAIN,
|
|
title=OIDC_PROVIDERS["authentik"]["name"],
|
|
data=initial_data,
|
|
source=config_entries.SOURCE_USER,
|
|
entry_id="1",
|
|
unique_id="test_unique_id",
|
|
options={},
|
|
pref_disable_new_entities=False,
|
|
pref_disable_polling=False,
|
|
discovery_keys=[],
|
|
subentries_data=None,
|
|
)
|
|
|
|
await hass.config_entries.async_add(entry)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={
|
|
"source": config_entries.SOURCE_RECONFIGURE,
|
|
"entry_id": entry.entry_id,
|
|
},
|
|
)
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"client_id": " ", "client_secret": DEMO_CLIENT_SECRET},
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "reconfigure"
|
|
assert result["errors"]["client_id"] == "invalid_client_id"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reconfigure_flow_keeps_client_secret_when_blank(hass: HomeAssistant):
|
|
"""Submitting a blank secret should keep the existing client secret."""
|
|
initial_data = {
|
|
"provider": "authentik",
|
|
CLIENT_ID: DEMO_CLIENT_ID,
|
|
CLIENT_SECRET: DEMO_CLIENT_SECRET,
|
|
DISCOVERY_URL: MockOIDCServer.get_discovery_url(),
|
|
DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["name"],
|
|
FEATURES: {
|
|
FEATURES_AUTOMATIC_USER_LINKING: False,
|
|
FEATURES_AUTOMATIC_PERSON_CREATION: True,
|
|
FEATURES_INCLUDE_GROUPS_SCOPE: True,
|
|
},
|
|
CLAIMS: {
|
|
CLAIMS_DISPLAY_NAME: OIDC_PROVIDERS["authentik"]["claims"]["display_name"],
|
|
CLAIMS_USERNAME: OIDC_PROVIDERS["authentik"]["claims"]["username"],
|
|
CLAIMS_GROUPS: OIDC_PROVIDERS["authentik"]["claims"]["groups"],
|
|
},
|
|
ROLES: {ROLE_ADMINS: DEMO_ADMIN_ROLE, ROLE_USERS: DEMO_USER_ROLE},
|
|
}
|
|
|
|
entry = config_entries.ConfigEntry(
|
|
version=1,
|
|
minor_version=0,
|
|
domain=DOMAIN,
|
|
title=OIDC_PROVIDERS["authentik"]["name"],
|
|
data=initial_data,
|
|
source=config_entries.SOURCE_USER,
|
|
entry_id="1",
|
|
unique_id="test_unique_id",
|
|
options={},
|
|
pref_disable_new_entities=False,
|
|
pref_disable_polling=False,
|
|
discovery_keys=[],
|
|
subentries_data=None,
|
|
)
|
|
|
|
await hass.config_entries.async_add(entry)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={
|
|
"source": config_entries.SOURCE_RECONFIGURE,
|
|
"entry_id": entry.entry_id,
|
|
},
|
|
)
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"client_id": DEMO_CLIENT_ID, "client_secret": ""},
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.ABORT
|
|
assert result["reason"] == "reconfigure_successful"
|
|
|
|
updated_entry = hass.config_entries.async_get_entry(entry.entry_id)
|
|
assert updated_entry is not None
|
|
assert updated_entry.data[CLIENT_SECRET] == DEMO_CLIENT_SECRET
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validation_actions_route_to_other_steps(hass: HomeAssistant):
|
|
"""Validation actions should route to the requested flow step."""
|
|
with mock_oidc_responses():
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"provider": "authentik"}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"discovery_url": MockOIDCServer.get_discovery_url()},
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "validate_connection"
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"action": "fix_discovery"}
|
|
)
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "discovery_url"
|
|
|
|
with mock_oidc_responses():
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"provider": "authentik"}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"discovery_url": MockOIDCServer.get_discovery_url()},
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "validate_connection"
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"action": "change_provider"}
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "user"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_user_flow_aborts_when_yaml_configured(hass: HomeAssistant):
|
|
"""The user flow should abort when YAML config already owns the provider."""
|
|
hass.data[DOMAIN] = {"yaml_config": {"client_id": DEMO_CLIENT_ID}}
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.ABORT
|
|
assert result["reason"] == "yaml_configured"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_user_flow_aborts_when_entry_already_exists(hass: HomeAssistant):
|
|
"""The flow should not create a second OIDC config entry."""
|
|
entry = config_entries.ConfigEntry(
|
|
version=1,
|
|
minor_version=0,
|
|
domain=DOMAIN,
|
|
title=OIDC_PROVIDERS["authentik"]["name"],
|
|
data={"provider": "authentik"},
|
|
source=config_entries.SOURCE_USER,
|
|
entry_id="1",
|
|
unique_id="test_unique_id",
|
|
options={},
|
|
pref_disable_new_entities=False,
|
|
pref_disable_polling=False,
|
|
discovery_keys=[],
|
|
subentries_data=None,
|
|
)
|
|
await hass.config_entries.async_add(entry)
|
|
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.ABORT
|
|
assert result["reason"] == "single_instance_allowed"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_discovery_url_validation_rejects_invalid_url(hass: HomeAssistant):
|
|
"""Discovery URL validation should reject malformed inputs."""
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"provider": "authentik"}
|
|
)
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"discovery_url": "not-a-valid-oidc-url"}
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "discovery_url"
|
|
assert result["errors"]["discovery_url"] == "invalid_url_format"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generic_provider_skips_groups_config(hass: HomeAssistant):
|
|
"""Providers without group support should go straight to user linking."""
|
|
with mock_oidc_responses():
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"provider": "generic"}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"discovery_url": MockOIDCServer.get_discovery_url()},
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"action": "continue"}
|
|
)
|
|
assert result["step_id"] == "client_config"
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"client_id": DEMO_CLIENT_ID, "client_secret": DEMO_CLIENT_SECRET},
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.FORM
|
|
assert result["step_id"] == "user_linking"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_groups_disabled_skips_roles_and_creates_entry(hass: HomeAssistant):
|
|
"""Disabling groups should skip role configuration and omit roles from entry data."""
|
|
with mock_oidc_responses():
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"provider": "authentik"}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"discovery_url": MockOIDCServer.get_discovery_url()},
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"action": "continue"}
|
|
)
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"],
|
|
{"client_id": DEMO_CLIENT_ID, "client_secret": DEMO_CLIENT_SECRET},
|
|
)
|
|
assert result["step_id"] == "groups_config"
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"enable_groups": False}
|
|
)
|
|
assert result["step_id"] == "user_linking"
|
|
|
|
result = await hass.config_entries.flow.async_configure(
|
|
result["flow_id"], {"enable_user_linking": True}
|
|
)
|
|
|
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
|
assert "roles" not in result["data"]
|
|
assert result["data"][FEATURES][FEATURES_INCLUDE_GROUPS_SCOPE] is False
|