33import asyncio
44import os
55import time
6- from datetime import datetime , timezone
6+ from datetime import datetime , timedelta , timezone
77from typing import Any
88from unittest .mock import AsyncMock , MagicMock , patch
99
2121 TadoError ,
2222 TadoReadingError ,
2323)
24- from tadoasync .tadoasync import DeviceActivationStatus
24+ from tadoasync .tadoasync import DEVICE_AUTH_URL , DeviceActivationStatus
2525
2626from syrupy import SnapshotAssertion
2727from tests import load_fixture
@@ -63,7 +63,8 @@ async def test_login_success(responses: aioresponses) -> None:
6363 )
6464 async with aiohttp .ClientSession () as session :
6565 tado = Tado (session = session )
66- await tado .login ()
66+ await tado .async_init ()
67+ await tado .device_activation ()
6768 assert tado ._access_token == "test_access_token"
6869 assert tado ._token_expiry is not None
6970 assert tado ._token_expiry > time .time ()
@@ -79,39 +80,94 @@ async def test_login_success_no_session(responses: aioresponses) -> None:
7980 )
8081 async with aiohttp .ClientSession ():
8182 tado = Tado ()
82- await tado .login ()
83+ await tado .async_init ()
84+ await tado .device_activation ()
8385 assert tado ._access_token == "test_access_token"
8486 assert tado ._token_expiry is not None
8587 assert tado ._token_expiry > time .time ()
8688 assert tado ._refresh_token == "test_refresh_token"
8789
8890
89- async def test_login_timeout (python_tado : Tado , responses : aioresponses ) -> None :
90- """Test login timeout."""
91+ async def test_activation_timeout (responses : aioresponses ) -> None :
92+ """Test activation timeout."""
93+ responses .post (
94+ DEVICE_AUTH_URL ,
95+ status = 200 ,
96+ payload = {
97+ "device_code" : "XXX_code_XXX" ,
98+ "expires_in" : 1 ,
99+ "interval" : 0 ,
100+ "user_code" : "7BQ5ZQ" ,
101+ "verification_uri" : "https://login.tado.com/oauth2/device" ,
102+ "verification_uri_complete" : "https://login.tado.com/oauth2/device?user_code=7BQ5ZQ" ,
103+ },
104+ )
91105 responses .post (
92106 TADO_TOKEN_URL ,
107+ status = 400 ,
108+ payload = {"error" : "authorization_pending" },
109+ )
110+
111+ async with aiohttp .ClientSession () as session :
112+ tado = Tado (session = session )
113+ await tado .async_init ()
114+ tado ._expires_at = datetime .now (timezone .utc ) - timedelta (seconds = 1 )
115+ with pytest .raises (TadoError , match = "User took too long" ):
116+ await tado .device_activation ()
117+
118+
119+ async def test_login_device_flow_timeout (responses : aioresponses ) -> None :
120+ """Test timeout during device auth flow."""
121+ responses ._matches .clear ()
122+ responses .post (
123+ DEVICE_AUTH_URL ,
93124 exception = asyncio .TimeoutError (),
125+ repeat = True ,
94126 )
95- with pytest .raises (TadoConnectionError ):
96- await python_tado .login ()
97127
128+ async with aiohttp .ClientSession () as session :
129+ tado = Tado (session = session )
130+ with pytest .raises (TadoConnectionError ):
131+ await tado .async_init ()
98132
99- async def test_login_invalid_content_type (
100- python_tado : Tado , responses : aioresponses
101- ) -> None :
133+
134+ async def test_login_invalid_content_type (responses : aioresponses ) -> None :
102135 """Test login invalid content type."""
136+ responses ._matches .clear ()
103137 responses .post (
104- TADO_TOKEN_URL ,
138+ DEVICE_AUTH_URL ,
105139 status = 200 ,
106140 headers = {"content-type" : "text/plain" },
107141 body = "Unexpected response" ,
108142 )
109143
110- with pytest .raises (TadoError ):
111- await python_tado .login ()
144+ async with aiohttp .ClientSession () as session :
145+ tado = Tado (session = session )
146+ with pytest .raises (TadoError ):
147+ await tado .async_init ()
148+
149+
150+ async def test_login_invalid_status () -> None :
151+ """Test login with non-200 status triggers."""
152+ mock_response = MagicMock (spec = ClientResponse )
153+ mock_response .status = 400
154+ mock_response .headers = {"content-type" : "application/json" }
155+ mock_response .raise_for_status = MagicMock (return_value = None )
156+ mock_response .json = AsyncMock (return_value = {"error" : "bad_request" })
157+ mock_response .text = AsyncMock (return_value = "Unexpected response" )
158+
159+ async def mock_post (* args : Any , ** kwargs : Any ) -> ClientResponse : # noqa: ARG001 # pylint: disable=unused-argument
160+ return mock_response
161+
162+ async with aiohttp .ClientSession () as session :
163+ tado = Tado (session = session )
164+ with patch ("aiohttp.ClientSession.post" , new = mock_post ), pytest .raises (
165+ TadoError
166+ ):
167+ await tado .async_init ()
112168
113169
114- async def test_login_client_response_error (python_tado : Tado ) -> None :
170+ async def test_login_client_response_error () -> None :
115171 """Test login client response error."""
116172 mock_request_info = MagicMock (spec = RequestInfo )
117173 mock_response = MagicMock (spec = ClientResponse )
@@ -124,10 +180,12 @@ async def test_login_client_response_error(python_tado: Tado) -> None:
124180 async def mock_post (* args : Any , ** kwargs : Any ) -> ClientResponse : # noqa: ARG001 # pylint: disable=unused-argument
125181 return mock_response
126182
127- with patch ("aiohttp.ClientSession.post" , new = mock_post ), pytest .raises (
128- TadoAuthenticationError
129- ):
130- await python_tado .login ()
183+ async with aiohttp .ClientSession () as session :
184+ tado = Tado (session = session )
185+ with patch ("aiohttp.ClientSession.post" , new = mock_post ), pytest .raises (
186+ TadoAuthenticationError
187+ ):
188+ await tado .async_init ()
131189
132190
133191async def test_refresh_auth_success (responses : aioresponses ) -> None :
@@ -188,6 +246,29 @@ async def mock_post(*args: Any, **kwargs: Any) -> ClientResponse: # noqa: ARG00
188246 await python_tado ._refresh_auth ()
189247
190248
249+ async def test_device_flow_sets_verification_url (responses : aioresponses ) -> None :
250+ """Test device flow sets verification URL."""
251+ responses .post (
252+ DEVICE_AUTH_URL ,
253+ status = 200 ,
254+ payload = {
255+ "device_code" : "XXX" ,
256+ "expires_in" : 600 ,
257+ "interval" : 0 ,
258+ "user_code" : "7BQ5ZQ" ,
259+ "verification_uri" : "https://login.tado.com/oauth2/device" ,
260+ },
261+ )
262+
263+ async with aiohttp .ClientSession () as session :
264+ tado = Tado (session = session )
265+ await tado .login_device_flow ()
266+ assert (
267+ tado .device_verification_url
268+ == "https://login.tado.com/oauth2/device?user_code=7BQ5ZQ"
269+ )
270+
271+
191272async def test_get_me (
192273 python_tado : Tado , responses : aioresponses , snapshot : SnapshotAssertion
193274) -> None :
@@ -210,10 +291,6 @@ async def test_get_devices(
210291 body = load_fixture ("devices.json" ),
211292 )
212293 assert await python_tado .get_devices () == snapshot
213- assert (
214- python_tado .device_verification_url
215- == "https://login.tado.com/oauth2/device?user_code=7BQ5ZQ"
216- )
217294
218295
219296async def test_get_mobile_devices (
0 commit comments