From 28b3b8d14464e5551a730feb5282071d14a8d594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20F=C3=B6rster?= Date: Fri, 14 Mar 2025 11:55:29 +0100 Subject: [PATCH] refactoring and adding test cases for test coverage --- {src/delta_barth => archive}/_workflow.py | 0 src/delta_barth/analysis/forecast.py | 3 +- src/delta_barth/api/common.py | 2 +- src/delta_barth/api/requests.py | 6 +- tests/api/conftest.py | 14 +++ tests/api/test_common.py | 109 ++++++++++++++++++++-- tests/api/test_requests.py | 88 ++++++++++++----- 7 files changed, 191 insertions(+), 31 deletions(-) rename {src/delta_barth => archive}/_workflow.py (100%) diff --git a/src/delta_barth/_workflow.py b/archive/_workflow.py similarity index 100% rename from src/delta_barth/_workflow.py rename to archive/_workflow.py diff --git a/src/delta_barth/analysis/forecast.py b/src/delta_barth/analysis/forecast.py index 5fea086..8bce364 100644 --- a/src/delta_barth/analysis/forecast.py +++ b/src/delta_barth/analysis/forecast.py @@ -23,7 +23,6 @@ from delta_barth.constants import ( ) from delta_barth.errors import STATUS_HANDLER, wrap_result from delta_barth.types import ( - CustomerDataSalesForecast, DualDict, PipeResult, ) @@ -328,6 +327,6 @@ def pipeline_sales_dummy( session: Session, company_id: int | None = None, start_date: Datetime | None = None, -) -> SalesPrognosisResultsExport: +) -> SalesPrognosisResultsExport: # pragma: no cover """prototype dummy function for tests by DelBar""" ... diff --git a/src/delta_barth/api/common.py b/src/delta_barth/api/common.py index 26d9c0b..8cea9bc 100644 --- a/src/delta_barth/api/common.py +++ b/src/delta_barth/api/common.py @@ -173,7 +173,7 @@ class Session: elif resp.status_code == 401: self._remove_session_token() response, status = self.login() - else: # pragma: no cover + else: response = LoginResponse(token="") err = DelBarApiError(status_code=resp.status_code, **resp.json()) status = STATUS_HANDLER.api_error(err) diff --git a/src/delta_barth/api/requests.py b/src/delta_barth/api/requests.py index b6625be..fdad891 100644 --- a/src/delta_barth/api/requests.py +++ b/src/delta_barth/api/requests.py @@ -54,7 +54,11 @@ def get_sales_prognosis_data( company_id: int | None = None, start_date: Datetime | None = None, ) -> tuple[SalesPrognosisResponse, Status]: - session.assert_login() + resp, 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) diff --git a/tests/api/conftest.py b/tests/api/conftest.py index ac6338c..b824bb8 100644 --- a/tests/api/conftest.py +++ b/tests/api/conftest.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + import pytest from delta_barth.api import common @@ -16,3 +18,15 @@ def session(credentials, api_base_url) -> common.Session: ) return session + + +@pytest.fixture +def mock_put(): + with patch("requests.put") as mock: + yield mock + + +@pytest.fixture +def mock_get(): + with patch("requests.get") as mock: + yield mock diff --git a/tests/api/test_common.py b/tests/api/test_common.py index b7f9029..c9f3aa1 100644 --- a/tests/api/test_common.py +++ b/tests/api/test_common.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + import pytest from pydantic import ValidationError @@ -8,6 +10,23 @@ from delta_barth.errors import ( ) from delta_barth.types import HttpRequestTypes +# @pytest.fixture(scope="session") +# def auth_response() -> MockResponse: +# json_data = {"message": "GenericError", "code": "TestLogin", "hints": "TestCase"} +# return MockResponse(401, json_data) + + +# @pytest.fixture(scope="session") +# def fail_response() -> MockResponse: +# json_data = {"message": "GenericError", "code": "Test123", "hints": "TestCase"} +# return MockResponse(500, json_data) + + +# @pytest.fixture +# def mock_response(): +# with patch("requests.put") as mock_get: +# yield mock_get + @pytest.mark.parametrize( ["case", "expect"], @@ -112,7 +131,7 @@ def test_session(credentials, api_base_url): @pytest.mark.api_con_required -def test_login_logout(session, credentials): +def test_login_logout_Success(session, credentials): assert not session.logged_in resp, status = session.login() @@ -139,8 +158,37 @@ def test_login_logout(session, credentials): assert status.api_server_error.message == "Nutzer oder Passwort falsch." +def test_login_logout_FailApiServer(session, mock_put): + code = 401 + json = { + "message": "GenericError", + "code": "TestLogin", + "hints": "TestCase", + } + + mock_put.return_value.status_code = code + mock_put.return_value.json.return_value = json + resp, status = session.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"] + resp, status = session.logout() + assert resp is None + 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"] + + @pytest.mark.api_con_required -def test_assert_login_while_logged_out(session): +def test_assert_login_SuccessLoggedOut(session): assert session.session_token is None assert session._creds is not None # test logged out state @@ -151,11 +199,12 @@ def test_assert_login_while_logged_out(session): resp, status = session.logout() assert status.code == 0 - # test already logged in + +@pytest.mark.api_con_required +def test_assert_login_SuccessStillLoggedIn(session): assert session.session_token is None assert session._creds is not None - _, status = session.login() - assert status.code == 0 + resp, status = session.login() resp, status = session.assert_login() assert resp is not None assert status.code == 0 @@ -163,7 +212,31 @@ def test_assert_login_while_logged_out(session): resp, status = session.logout() assert status.code == 0 - # test invalid token + +@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() @@ -175,3 +248,27 @@ def test_assert_login_while_logged_out(session): 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"] diff --git a/tests/api/test_requests.py b/tests/api/test_requests.py index 7ccc46f..d4ad288 100644 --- a/tests/api/test_requests.py +++ b/tests/api/test_requests.py @@ -6,46 +6,92 @@ from delta_barth.api import requests as requests_ @pytest.mark.api_con_required -def test_get_sales_prognosis_data(session): - resp, state = session.login() +def test_get_sales_prognosis_data_Success(session): + resp, status = session.login() # test without company ID - assert state.code == 0 + assert status.code == 0 date = Datetime(2022, 6, 1) - resp, state = requests_.get_sales_prognosis_data(session, None, date) - assert state.code == 0 + resp, status = requests_.get_sales_prognosis_data(session, None, date) + assert status.code == 0 assert len(resp.daten) > 0 date = Datetime(2030, 1, 1) - resp, state = requests_.get_sales_prognosis_data(session, None, date) - assert state.code == 0 + resp, status = requests_.get_sales_prognosis_data(session, None, date) + assert status.code == 0 assert len(resp.daten) == 0 # test with company ID - assert state.code == 0 + assert status.code == 0 date = Datetime(2022, 6, 1) company_id = 1024 - resp, state = requests_.get_sales_prognosis_data(session, company_id, date) - assert state.code == 0 + resp, status = requests_.get_sales_prognosis_data(session, company_id, date) + assert status.code == 0 assert len(resp.daten) > 0 date = Datetime(2030, 1, 1) - resp, state = requests_.get_sales_prognosis_data(session, company_id, date) - assert state.code == 0 + resp, status = requests_.get_sales_prognosis_data(session, company_id, date) + assert status.code == 0 assert len(resp.daten) == 0 # test with non-existent company ID - assert state.code == 0 + assert status.code == 0 date = Datetime(2022, 6, 1) company_id = 1000024 - resp, state = requests_.get_sales_prognosis_data(session, company_id, date) + resp, status = requests_.get_sales_prognosis_data(session, company_id, date) # TODO check if this behaviour is still considered "successful" - assert state.code == 0 + assert status.code == 0 assert len(resp.daten) == 0 # test without date company_id = 1024 - resp, state = requests_.get_sales_prognosis_data(session, company_id, None) - assert state.code == 0 + resp, status = requests_.get_sales_prognosis_data(session, company_id, None) + assert status.code == 0 assert len(resp.daten) > 0 # test without filters - resp, state = requests_.get_sales_prognosis_data(session, None, None) - assert state.code == 0 + resp, status = requests_.get_sales_prognosis_data(session, None, None) + assert status.code == 0 assert len(resp.daten) > 0 # close connection - resp, state = session.logout() - assert state.code == 0 + resp, status = session.logout() + assert status.code == 0 + + +@pytest.mark.api_con_required +def test_get_sales_prognosis_data_FailLogin(session, mock_get): + session.login() + 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 = 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 + assert status.api_server_error.message == json["message"] + assert status.api_server_error.code == json["code"] + assert status.api_server_error.hints == json["hints"] + + +@pytest.mark.new +@pytest.mark.api_con_required +def test_get_sales_prognosis_data_FailApiServer(session, mock_get): + code = 405 + json = { + "message": "ServerError", + "code": "TestExternalServerError", + "hints": "TestCase", + } + mock_get.return_value.status_code = code + mock_get.return_value.json.return_value = json + + 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 + assert status.api_server_error.message == json["message"] + assert status.api_server_error.code == json["code"] + assert status.api_server_error.hints == json["hints"]