From 55cd68d3addb3be9adcc83f6535a18c63d83826a Mon Sep 17 00:00:00 2001 From: Brooks Travis Date: Thu, 22 Jan 2026 13:38:32 -0600 Subject: [PATCH 1/2] Fix logout failure with ECS tenants by resetting tenant ID on logout --- src/folioclient/FolioClient.py | 1 + tests/integration_tests.py | 17 +++++++++++++++++ tests/test_folio_client.py | 23 +++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/folioclient/FolioClient.py b/src/folioclient/FolioClient.py index a6cb36a..b2b6919 100644 --- a/src/folioclient/FolioClient.py +++ b/src/folioclient/FolioClient.py @@ -331,6 +331,7 @@ def __exit__(self, exc_type, exc_value, traceback): and not self.httpx_client.is_closed ): logger.info("logging out...") + self.folio_auth.reset_tenant_id() logout = self.httpx_client.post( urljoin(self.gateway_url, "authn/logout"), ) diff --git a/tests/integration_tests.py b/tests/integration_tests.py index f014291..3620b5f 100644 --- a/tests/integration_tests.py +++ b/tests/integration_tests.py @@ -26,6 +26,7 @@ FolioPermissionError, FolioResourceNotFoundError, FolioValidationError, + FolioClientClosed, ) @@ -451,6 +452,22 @@ def test_ecs_members_property(self, folio_client): assert isinstance(member, dict) assert "id" in member + def test_context_manager_logout_after_tenant_switch(self, folio_client_ecs): + """Verify __exit__ logs out cleanly after switching to a member tenant.""" + members = folio_client_ecs.ecs_members + if not members: + pytest.skip("No ECS member tenants available for logout test") + + member_tenant = members[0]["id"] + + with folio_client_ecs as client: + client.tenant_id = member_tenant + assert client.tenant_id == member_tenant + + assert client.is_closed is True + with pytest.raises(FolioClientClosed): + _ = client.cookies + @pytest.mark.skip(reason="ECS functionality not available in snapshot environment") def test_ecs_central_tenant_id_setter(self, folio_client): """Test setting ecs_central_tenant_id.""" diff --git a/tests/test_folio_client.py b/tests/test_folio_client.py index 4cd0241..9aec860 100644 --- a/tests/test_folio_client.py +++ b/tests/test_folio_client.py @@ -2,6 +2,7 @@ import pytest from httpx import HTTPError, UnsupportedProtocol from unittest.mock import Mock, patch, MagicMock, AsyncMock +from urllib.parse import urljoin import httpx # Import shared test utilities @@ -280,6 +281,28 @@ def test_context_manager_closes_client(): assert fc.is_closed is True +@patch.object(FolioClient, '_initial_ecs_check') +def test_context_manager_logs_out_on_exit(mock_ecs_check): + with folio_auth_patcher(): + mock_logout_response = Mock() + mock_logout_response.raise_for_status.return_value = None + + mock_httpx_client = Mock() + mock_httpx_client.is_closed = False + mock_httpx_client.post.return_value = mock_logout_response + mock_httpx_client.close = Mock() + + logout_url = urljoin("https://example.com", "authn/logout") + + with patch.object(FolioClient, 'get_folio_http_client', return_value=mock_httpx_client): + with FolioClient("https://example.com", "tenant", "user", "pass") as fc: + fc.folio_auth.reset_tenant_id = Mock() + + mock_httpx_client.post.assert_called_once_with(logout_url) + mock_httpx_client.close.assert_called_once() + fc.folio_auth.reset_tenant_id.assert_called_once() + + @pytest.mark.asyncio async def test_async_context_manager_closes_client(): with folio_auth_patcher() as mock_folio_auth: From 77cdcd14f08bf3f73b4aca6bf68705f603310f20 Mon Sep 17 00:00:00 2001 From: Brooks Travis Date: Thu, 22 Jan 2026 13:38:38 -0600 Subject: [PATCH 2/2] Bump version to 1.0.5 in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0f980eb..f123e02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "folioclient" -version = "1.0.4" +version = "1.0.5" description = "An API wrapper over the FOLIO LSP API Suite (Formerly OKAPI)." authors = [ { name = "Theodor Tolstoy", email = "github.teddes@tolstoy.se" },