From 248b811786dce43f3c6a38f3ea76611caf21ed7f Mon Sep 17 00:00:00 2001 From: foefl Date: Wed, 30 Apr 2025 08:42:44 +0200 Subject: [PATCH] include login assertion in requests to eliminate unnecessary calls to the API --- src/delta_barth/api/requests.py | 13 +++--- src/delta_barth/session.py | 42 ------------------- tests/api/test_requests.py | 48 +++++++++++++++------- tests/test_session.py | 73 ++++----------------------------- 4 files changed, 49 insertions(+), 127 deletions(-) diff --git a/src/delta_barth/api/requests.py b/src/delta_barth/api/requests.py index 9d0c710..2adf1cc 100644 --- a/src/delta_barth/api/requests.py +++ b/src/delta_barth/api/requests.py @@ -12,6 +12,8 @@ from delta_barth.errors import STATUS_HANDLER from delta_barth.types import DelBarApiError, ExportResponse, ResponseType, Status if TYPE_CHECKING: + from requests import Response + from delta_barth.session import Session @@ -57,10 +59,10 @@ def get_sales_prognosis_data( start_date: Datetime | None = None, ) -> tuple[SalesPrognosisResponse, Status]: # TODO check elimination of assertion for login, #25 - _, status = session.assert_login() - if status != STATUS_HANDLER.SUCCESS: - response = SalesPrognosisResponse(daten=tuple()) - return response, status + # _, status = session.assert_login() + # if status != STATUS_HANDLER.SUCCESS: + # response = SalesPrognosisResponse(daten=tuple()) + # return response, status ROUTE: Final[str] = "verkauf/umsatzprognosedaten" URL: Final = combine_route(session.base_url, ROUTE) @@ -70,6 +72,7 @@ def get_sales_prognosis_data( BuchungsDatum=start_date, ) empty_response = SalesPrognosisResponse(daten=tuple()) + resp: Response | None = None try: for attempt in range(1, (MAX_LOGIN_RETRIES + 1)): resp = requests.get( @@ -82,7 +85,6 @@ def get_sales_prognosis_data( _, status = session.relogin() if status != STATUS_HANDLER.SUCCESS and attempt == MAX_LOGIN_RETRIES: return empty_response, status - except requests.exceptions.Timeout: return empty_response, STATUS_HANDLER.pipe_states.CONNECTION_TIMEOUT except requests.exceptions.RequestException: @@ -90,6 +92,7 @@ def get_sales_prognosis_data( response: SalesPrognosisResponse status: Status + assert resp is not None, "tried to use not defined response" if resp.status_code == 200: response = SalesPrognosisResponse(**resp.json()) status = STATUS_HANDLER.SUCCESS diff --git a/src/delta_barth/session.py b/src/delta_barth/session.py index 35b8247..6c6dc78 100644 --- a/src/delta_barth/session.py +++ b/src/delta_barth/session.py @@ -300,45 +300,3 @@ class Session: self._remove_session_token() return self.login() - - def assert_login( - self, - ) -> tuple[LoginResponse, Status]: - # check if login token is still valid - # re-login if necessary - if self.session_token is None: - return self.login() - - # use known endpoint which requires a valid token in its header - # evaluate the response to decide if: - # current token is still valid, token is not valid, other errors occurred - ROUTE: Final[str] = "verkauf/umsatzprognosedaten" - URL: Final = combine_route(self.base_url, ROUTE) - params: dict[str, int] = {"FirmaId": 999999} - empty_response = LoginResponse(token="") - try: - resp = requests.get( - URL, - params=params, - headers=self.headers, # type: ignore - timeout=API_CON_TIMEOUT, - ) - except requests.exceptions.Timeout: # pragma: no cover - return empty_response, STATUS_HANDLER.pipe_states.CONNECTION_TIMEOUT - except requests.exceptions.RequestException: # pragma: no cover - return empty_response, STATUS_HANDLER.pipe_states.CONNECTION_ERROR - - response: LoginResponse - status: Status - if resp.status_code == 200: - response = LoginResponse(token=self.session_token) - status = STATUS_HANDLER.SUCCESS - elif resp.status_code == 401: - self._remove_session_token() - response, status = self.login() - else: - response = empty_response - err = DelBarApiError(status_code=resp.status_code, **resp.json()) - status = STATUS_HANDLER.api_error(err) - - return response, status diff --git a/tests/api/test_requests.py b/tests/api/test_requests.py index 14ba14c..e9f63c0 100644 --- a/tests/api/test_requests.py +++ b/tests/api/test_requests.py @@ -4,7 +4,6 @@ import pytest import requests from delta_barth.api import requests as requests_ -from delta_barth.api.common import LoginResponse @pytest.mark.api_con_required @@ -54,12 +53,11 @@ def test_get_sales_prognosis_data_Success(session): @pytest.mark.api_con_required -def test_get_sales_prognosis_data_FailLogin(session, mock_get): - session.login() - code = 500 +def test_get_sales_prognosis_data_NoAuth(session, mock_get): + code = 401 json = { "message": "ServerError", - "code": "TestExternalServerError", + "code": "TestFailAuth", "hints": "TestCase", } mock_get.return_value.status_code = code @@ -76,6 +74,36 @@ def test_get_sales_prognosis_data_FailLogin(session, mock_get): assert status.api_server_error.hints == json["hints"] +def test_get_sales_prognosis_data_FailLogin(session, mock_get, mock_put): + code = 401 + json = { + "message": "ServerError", + "code": "TestFailAuth", + "hints": "TestCase", + } + mock_get.return_value.status_code = code + mock_get.return_value.json.return_value = json + + code_put = 500 + json_put = { + "message": "ServerError", + "code": "TestUnknownError", + "hints": "TestCase", + } + mock_put.return_value.status_code = code_put + mock_put.return_value.json.return_value = json_put + + resp, status = requests_.get_sales_prognosis_data(session, None, None) + assert resp is not None + assert len(resp.daten) == 0 + assert status.code == 400 + assert status.api_server_error is not None + assert status.api_server_error.status_code == code_put + assert status.api_server_error.message == json_put["message"] + assert status.api_server_error.code == json_put["code"] + assert status.api_server_error.hints == json_put["hints"] + + @pytest.mark.api_con_required def test_get_sales_prognosis_data_FailApiServer(session, mock_get): code = 405 @@ -101,11 +129,6 @@ def test_get_sales_prognosis_data_FailApiServer(session, mock_get): def test_get_sales_prognosis_data_FailGetTimeout(session, mock_get): mock_get.side_effect = requests.exceptions.Timeout("Test timeout") - def assert_login(): - return LoginResponse(token=""), requests_.STATUS_HANDLER.SUCCESS - - session.assert_login = assert_login - resp, status = requests_.get_sales_prognosis_data(session, None, None) assert resp is not None assert len(resp.daten) == 0 @@ -115,11 +138,6 @@ def test_get_sales_prognosis_data_FailGetTimeout(session, mock_get): def test_get_sales_prognosis_data_FailGetRequestException(session, mock_get): mock_get.side_effect = requests.exceptions.RequestException("Test not timeout") - def assert_login(): - return LoginResponse(token=""), requests_.STATUS_HANDLER.SUCCESS - - session.assert_login = assert_login - resp, status = requests_.get_sales_prognosis_data(session, None, None) assert resp is not None assert len(resp.daten) == 0 diff --git a/tests/test_session.py b/tests/test_session.py index 0b27402..0b5e042 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -314,11 +314,11 @@ def test_login_logout_FailApiServer(session, mock_put): @pytest.mark.api_con_required -def test_assert_login_SuccessLoggedOut(session): +def test_relogin_SuccessLoggedOut(session): assert session.session_token is None assert session._creds is not None # test logged out state - resp, status = session.assert_login() + resp, status = session.relogin() assert resp is not None assert status.code == 0 assert session.session_token is not None @@ -327,74 +327,17 @@ def test_assert_login_SuccessLoggedOut(session): @pytest.mark.api_con_required -def test_assert_login_SuccessStillLoggedIn(session): +def test_relogin_SuccessStillLoggedIn(session): assert session.session_token is None assert session._creds is not None resp, status = session.login() - resp, status = session.assert_login() + old_token = session.session_token + assert old_token is not None + resp, status = session.relogin() assert resp is not None assert status.code == 0 assert session.session_token is not None + assert session.session_token != old_token + resp, status = session.logout() assert status.code == 0 - - -@pytest.mark.api_con_required -def test_assert_login_ReloginNoValidAuth(session, mock_get): - code = 401 - json = { - "message": "AuthentificationError", - "code": "TestAssertLoginAfter", - "hints": "TestCase", - } - mock_get.return_value.status_code = code - mock_get.return_value.json.return_value = json - - resp, status = session.login() - - resp, status = session.assert_login() - assert resp is not None - assert status.code == 0 - assert session.session_token is not None - resp, status = session.logout() - assert status.code == 0 - - -@pytest.mark.api_con_required -def test_assert_login_ReloginWrongToken(session): - # triggers code 401 - assert session.session_token is None - assert session._creds is not None - _, status = session.login() - assert status.code == 0 - session._session_token = "WRONGTOKEN" - resp, status = session.assert_login() - assert resp is not None - assert status.code == 0 - assert session.session_token is not None - resp, status = session.logout() - assert status.code == 0 - - -@pytest.mark.api_con_required -def test_assert_login_FailApiServer(session, mock_get): - code = 500 - json = { - "message": "ServerError", - "code": "TestExternalServerError", - "hints": "TestCase", - } - mock_get.return_value.status_code = code - mock_get.return_value.json.return_value = json - - resp, status = session.login() - - resp, status = session.assert_login() - assert resp is not None - assert not resp.token - assert status.code == 400 - assert status.api_server_error is not None - assert status.api_server_error.status_code == code - assert status.api_server_error.message == json["message"] - assert status.api_server_error.code == json["code"] - assert status.api_server_error.hints == json["hints"]