diff --git a/spond/base.py b/spond/base.py index e3de081..cff6473 100644 --- a/spond/base.py +++ b/spond/base.py @@ -35,11 +35,17 @@ async def wrapper(self, *args, **kwargs): return wrapper async def login(self) -> None: - login_url = f"{self.api_url}login" + login_url = f"{self.api_url}auth2/login" data = {"email": self.username, "password": self.password} async with self.clientsession.post(login_url, json=data) as r: login_result = await r.json() - self.token = login_result.get("loginToken") - if self.token is None: - err_msg = f"Login failed. Response received: {login_result}" - raise AuthenticationError(err_msg) + self.token = self._extract_access_token(login_result) + + @staticmethod + def _extract_access_token(login_result: dict) -> str: + access = login_result.get("accessToken") + if isinstance(access, dict): + token = access.get("token") + if isinstance(token, str) and token: + return token + raise AuthenticationError(f"Login failed. Response received: {login_result}") diff --git a/tests/test_spond.py b/tests/test_spond.py index f55a499..c629b08 100644 --- a/tests/test_spond.py +++ b/tests/test_spond.py @@ -7,6 +7,7 @@ import pytest +from spond import AuthenticationError from spond.base import _SpondBase from spond.spond import Spond @@ -340,3 +341,64 @@ async def test_get_posts__api_error_raises(self, mock_get, mock_token) -> None: with pytest.raises(ValueError, match="401"): await s.get_posts() + + +class TestLogin: + @pytest.mark.parametrize( + ("login_result", "expected"), + [ + ( + {"accessToken": {"token": "ABC", "expiration": "2026-05-14T12:00:00Z"}}, + "ABC", + ), + ], + ) + def test_extract_access_token__happy_path(self, login_result, expected) -> None: + assert _SpondBase._extract_access_token(login_result) == expected + + @pytest.mark.parametrize( + "login_result", + [ + {"error": "Invalid credentials"}, + {"accessToken": None}, + {"accessToken": {}}, + {"accessToken": {"token": ""}}, + {"accessToken": {"token": None}}, + ], + ) + def test_extract_access_token__bad_shape_raises(self, login_result) -> None: + with pytest.raises(AuthenticationError): + _SpondBase._extract_access_token(login_result) + + @pytest.mark.asyncio + @patch("aiohttp.ClientSession.post") + async def test_login__happy_path(self, mock_post) -> None: + mock_response = { + "accessToken": {"token": "ABC", "expiration": "2026-05-14T12:00:00Z"}, + "refreshToken": {"token": "REF", "expiration": "2026-08-11T12:00:00Z"}, + "passwordToken": {"token": "PWD", "expiration": "2026-05-13T13:00:00Z"}, + } + mock_post.return_value.__aenter__.return_value.json = AsyncMock( + return_value=mock_response + ) + + s = Spond(MOCK_USERNAME, MOCK_PASSWORD) + await s.login() + + mock_post.assert_called_once_with( + "https://api.spond.com/core/v1/auth2/login", + json={"email": MOCK_USERNAME, "password": MOCK_PASSWORD}, + ) + assert s.token == "ABC" + + @pytest.mark.asyncio + @patch("aiohttp.ClientSession.post") + async def test_login__error_response_raises(self, mock_post) -> None: + mock_post.return_value.__aenter__.return_value.json = AsyncMock( + return_value={"error": "Invalid credentials"} + ) + + s = Spond(MOCK_USERNAME, MOCK_PASSWORD) + with pytest.raises(AuthenticationError): + await s.login() + assert s.token is None