diff --git a/src/saic_ismart_client_ng/exceptions.py b/src/saic_ismart_client_ng/exceptions.py index 93e266c..c7fad69 100644 --- a/src/saic_ismart_client_ng/exceptions.py +++ b/src/saic_ismart_client_ng/exceptions.py @@ -16,6 +16,10 @@ class SaicLogoutException(SaicApiException): pass +class SaicApiClosedException(SaicApiException): + pass + + class SaicApiRetryException(SaicApiException): def __init__( self, msg: str, *, event_id: str, return_code: int | None = None diff --git a/src/saic_ismart_client_ng/net/client/__init__.py b/src/saic_ismart_client_ng/net/client/__init__.py index af19147..47b0918 100644 --- a/src/saic_ismart_client_ng/net/client/__init__.py +++ b/src/saic_ismart_client_ng/net/client/__init__.py @@ -7,6 +7,7 @@ import httpx from httpx import Request, Response, Timeout +from saic_ismart_client_ng.exceptions import SaicApiClosedException from saic_ismart_client_ng.net.httpx import ( decrypt_httpx_response, encrypt_httpx_request, @@ -27,6 +28,7 @@ def __init__( self.__listener = listener self.__logger = logging.getLogger(__name__) self.__user_token: str = "" + self.__closed = False self.__client = httpx.AsyncClient( timeout=Timeout(timeout=configuration.read_timeout), event_hooks={ @@ -36,9 +38,13 @@ def __init__( ) async def send(self, request: Request) -> Response: + if self.__closed: + msg = "The API client has been closed and cannot be reused" + raise SaicApiClosedException(msg) return await self.__client.send(request) async def close(self) -> None: + self.__closed = True await self.__client.aclose() @property diff --git a/tests/test_close.py b/tests/test_close.py index d0012fe..807ecd2 100644 --- a/tests/test_close.py +++ b/tests/test_close.py @@ -1,13 +1,28 @@ +from __future__ import annotations + import unittest +import pytest + from saic_ismart_client_ng import SaicApi +from saic_ismart_client_ng.exceptions import SaicApiClosedException from saic_ismart_client_ng.model import SaicApiConfiguration +def _build_api() -> SaicApi: + return SaicApi( + SaicApiConfiguration(username="user", password="pass"), # noqa: S106 + ) + + class TestClose(unittest.IsolatedAsyncioTestCase): async def test_close_is_idempotent(self) -> None: - api = SaicApi( - SaicApiConfiguration(username="user", password="pass"), - ) + api = _build_api() + await api.close() await api.close() + + async def test_login_after_close_raises(self) -> None: + api = _build_api() await api.close() + with pytest.raises(SaicApiClosedException): + await api.login()