From a6daa643873b3bf274ee94c69a61099dce62ad71 Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Fri, 12 Dec 2025 08:27:44 -0500 Subject: [PATCH] fix: Add missing token extraction in async OAuth authentication - Extract access_token from OAuth response in AsyncAuthMixin.authenticate_oauth() - Add token storage to self.token for subsequent authenticated requests - Update test_async_authenticate_oauth_success to verify token extraction - Add test_async_authenticate_oauth_no_access_token for missing token case - Add test_async_authenticate_oauth_non_dict_response for invalid response case - Update test_async_authenticate_oauth_path to properly mock response with token --- src/ipsdk/platform.py | 9 +++++++ tests/test_platform.py | 55 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/ipsdk/platform.py b/src/ipsdk/platform.py index 1af2feb..c168f23 100644 --- a/src/ipsdk/platform.py +++ b/src/ipsdk/platform.py @@ -516,6 +516,15 @@ async def authenticate_oauth(self) -> None: res = await self.client.post(path, headers=headers, data=data) res.raise_for_status() + # Parse the response to extract the token + response_data = jsonutils.loads(res.text) + if isinstance(response_data, dict): + access_token = response_data.get("access_token") + else: + access_token = None + + self.token = access_token + except httpx.HTTPStatusError as exc: logging.exception(exc) raise exceptions.HTTPStatusError(exc.message, exc) diff --git a/tests/test_platform.py b/tests/test_platform.py index 70f744d..0ba44ea 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -618,8 +618,12 @@ async def test_async_authenticate_oauth_success(): mock_response.raise_for_status = Mock() mixin.client.post.return_value = mock_response - await mixin.authenticate_oauth() + with patch( + "ipsdk.jsonutils.loads", return_value={"access_token": "async_token_123"} + ): + await mixin.authenticate_oauth() + assert mixin.token == "async_token_123" # Verify the post was called with correct params mixin.client.post.assert_awaited_once_with( "/oauth/token", @@ -678,6 +682,48 @@ async def test_async_authenticate_oauth_request_error(): await mixin.authenticate_oauth() +@pytest.mark.asyncio +async def test_async_authenticate_oauth_no_access_token(): + """Test async authenticate_oauth when response has no access_token.""" + mixin = AsyncAuthMixin() + mixin.client_id = "test_id" + mixin.client_secret = "test_secret" + mixin.client = AsyncMock() + + # Mock response without access_token + mock_response = Mock(spec=Response) + mock_response.text = '{"error": "invalid_grant"}' + mock_response.raise_for_status = Mock() + mixin.client.post.return_value = mock_response + + with patch("ipsdk.jsonutils.loads", return_value={"error": "invalid_grant"}): + await mixin.authenticate_oauth() + + # Token should be None when access_token is missing + assert mixin.token is None + + +@pytest.mark.asyncio +async def test_async_authenticate_oauth_non_dict_response(): + """Test async authenticate_oauth when response is not a dict.""" + mixin = AsyncAuthMixin() + mixin.client_id = "test_id" + mixin.client_secret = "test_secret" + mixin.client = AsyncMock() + + # Mock response that parses to a list instead of dict + mock_response = Mock(spec=Response) + mock_response.text = '["invalid", "response"]' + mock_response.raise_for_status = Mock() + mixin.client.post.return_value = mock_response + + with patch("ipsdk.jsonutils.loads", return_value=["invalid", "response"]): + await mixin.authenticate_oauth() + + # Token should be None when response is not a dict + assert mixin.token is None + + @pytest.mark.asyncio async def test_async_authenticate_basicauth_http_status_error(): """Test async authenticate_basicauth raises TypeError due to bug.""" @@ -753,11 +799,16 @@ async def test_async_authenticate_oauth_path(): # Mock successful response mock_response = Mock() mock_response.status_code = 200 + mock_response.text = '{"access_token": "test_token_123"}' mock_response.raise_for_status = Mock() mixin.client.post.return_value = mock_response - await mixin.authenticate() + with patch( + "ipsdk.jsonutils.loads", return_value={"access_token": "test_token_123"} + ): + await mixin.authenticate() + assert mixin.token == "test_token_123" # Verify OAuth was called mixin.client.post.assert_awaited_once_with( "/oauth/token",