Skip to content

Commit 0c45b7f

Browse files
authored
Add reconfiguration flow to senz (home-assistant#156539)
1 parent bfa1116 commit 0c45b7f

File tree

4 files changed

+105
-5
lines changed

4 files changed

+105
-5
lines changed

homeassistant/components/senz/config_flow.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
import jwt
88

9-
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
9+
from homeassistant.config_entries import (
10+
SOURCE_REAUTH,
11+
SOURCE_RECONFIGURE,
12+
ConfigFlowResult,
13+
)
1014
from homeassistant.helpers import config_entry_oauth2_flow
1115

1216
from .const import DOMAIN
@@ -47,6 +51,12 @@ async def async_step_reauth_confirm(
4751

4852
return await self.async_step_user()
4953

54+
async def async_step_reconfigure(
55+
self, user_input: Mapping[str, Any] | None = None
56+
) -> ConfigFlowResult:
57+
"""User initiated reconfiguration."""
58+
return await self.async_step_user()
59+
5060
async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult:
5161
"""Create or update the config entry."""
5262

@@ -62,5 +72,11 @@ async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult:
6272
self._get_reauth_entry(), data=data
6373
)
6474

75+
if self.source == SOURCE_RECONFIGURE:
76+
self._abort_if_unique_id_mismatch(reason="account_mismatch")
77+
return self.async_update_reload_and_abort(
78+
self._get_reconfigure_entry(), data=data
79+
)
80+
6581
self._abort_if_unique_id_configured()
6682
return await super().async_oauth_create_entry(data)

homeassistant/components/senz/strings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
1212
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
1313
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
14-
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
14+
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
15+
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
1516
},
1617
"create_entry": {
1718
"default": "[%key:common::config_flow::create_entry::authenticated%]"

tests/components/senz/conftest.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,18 @@ async def setup_credentials(hass: HomeAssistant) -> None:
114114

115115

116116
@pytest.fixture
117-
async def access_token(hass: HomeAssistant) -> str:
117+
def unique_id() -> str:
118+
"""Return a unique ID."""
119+
return ENTRY_UNIQUE_ID
120+
121+
122+
@pytest.fixture
123+
async def access_token(hass: HomeAssistant, unique_id: str) -> str:
118124
"""Return a valid access token."""
119125
return config_entry_oauth2_flow._encode_jwt(
120126
hass,
121127
{
122-
"sub": ENTRY_UNIQUE_ID,
128+
"sub": unique_id,
123129
"aud": [],
124130
"scp": [
125131
"rest_api",

tests/components/senz/test_config_flow.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from homeassistant.helpers import config_entry_oauth2_flow
1717
from homeassistant.setup import async_setup_component
1818

19-
from .const import CLIENT_ID, CLIENT_SECRET
19+
from .const import CLIENT_ID, CLIENT_SECRET, ENTRY_UNIQUE_ID
2020

2121
from tests.common import MockConfigEntry
2222
from tests.test_util.aiohttp import AiohttpClientMocker
@@ -202,3 +202,80 @@ async def test_reauth_flow(
202202
assert result.get("reason") == "reauth_successful"
203203

204204
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
205+
206+
207+
@pytest.mark.usefixtures("current_request_with_host")
208+
@pytest.mark.parametrize(
209+
("unique_id", "expected_result"),
210+
[
211+
(ENTRY_UNIQUE_ID, "reconfigure_successful"),
212+
("different_unique_id", "account_mismatch"),
213+
],
214+
)
215+
async def test_reconfiguration_flow(
216+
hass: HomeAssistant,
217+
hass_client_no_auth: ClientSessionGenerator,
218+
aioclient_mock: AiohttpClientMocker,
219+
mock_config_entry: MockConfigEntry,
220+
access_token: str,
221+
unique_id: str,
222+
expected_result: str,
223+
expires_at: float,
224+
) -> None:
225+
"""Test reconfigure step with correct params."""
226+
227+
CURRENT_TOKEN = {
228+
"auth_implementation": DOMAIN,
229+
"token": {
230+
"access_token": access_token,
231+
"expires_in": 86399,
232+
"refresh_token": "3012bc9f-7a65-4240-b817-9154ffdcc30f",
233+
"token_type": "Bearer",
234+
"expires_at": expires_at,
235+
},
236+
}
237+
assert hass.config_entries.async_update_entry(
238+
mock_config_entry,
239+
data=CURRENT_TOKEN,
240+
)
241+
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
242+
243+
result = await mock_config_entry.start_reconfigure_flow(hass)
244+
245+
assert result["step_id"] == "auth"
246+
247+
state = config_entry_oauth2_flow._encode_jwt(
248+
hass,
249+
{
250+
"flow_id": result["flow_id"],
251+
"redirect_uri": REDIRECT_URL,
252+
},
253+
)
254+
assert result["url"] == (
255+
f"{AUTHORIZATION_ENDPOINT}?response_type=code&client_id={CLIENT_ID}"
256+
f"&redirect_uri={REDIRECT_URL}"
257+
f"&state={state}&scope=restapi+offline_access"
258+
)
259+
260+
client = await hass_client_no_auth()
261+
resp = await client.get(f"{REDIRECT_PATH}?code=abcd&state={state}")
262+
assert resp.status == 200
263+
assert resp.headers["content-type"] == "text/html; charset=utf-8"
264+
265+
aioclient_mock.post(
266+
TOKEN_ENDPOINT,
267+
json={
268+
"refresh_token": "updated-refresh-token",
269+
"access_token": access_token,
270+
"type": "Bearer",
271+
"expires_in": "60",
272+
},
273+
)
274+
275+
result = await hass.config_entries.flow.async_configure(result["flow_id"])
276+
await hass.async_block_till_done()
277+
278+
assert result.get("type") is FlowResultType.ABORT
279+
assert result.get("reason") == expected_result
280+
281+
assert len(hass.config_entries.async_entries(DOMAIN)) == 1

0 commit comments

Comments
 (0)