From 2e99ffa8f10816f1e9162dcc3895380d131d7bae Mon Sep 17 00:00:00 2001 From: Muhammad Zohaib Date: Fri, 13 Nov 2026 23:47:38 +0300 Subject: [PATCH 1/2] fix(temporal): connect to the namespace in TEMPORAL_NAMESPACE --- src/agentex/lib/core/clients/temporal/auth.py | 5 +++ tests/lib/test_temporal_auth.py | 37 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/agentex/lib/core/clients/temporal/auth.py b/src/agentex/lib/core/clients/temporal/auth.py index 09886d8d4..7c757b1e4 100644 --- a/src/agentex/lib/core/clients/temporal/auth.py +++ b/src/agentex/lib/core/clients/temporal/auth.py @@ -17,6 +17,7 @@ logger = logging.getLogger(__name__) _AUTH_SCOPE_ENV = "TEMPORAL_AUTH_SCOPE" +_NAMESPACE_ENV = "TEMPORAL_NAMESPACE" _REFRESH_MARGIN_SECONDS = 300 _MIN_REFRESH_DELAY_SECONDS = 30 _RETRY_DELAY_SECONDS = 30 @@ -85,6 +86,10 @@ async def _run(self) -> None: async def connect_temporal_client(connect_options: dict[str, Any]) -> Client: """Connect anonymously by default, or with renewable Workload Identity.""" + namespace = os.environ.get(_NAMESPACE_ENV) + if namespace and "namespace" not in connect_options: + connect_options = {**connect_options, "namespace": namespace} + scope = os.environ.get(_AUTH_SCOPE_ENV) if not scope: return await Client.connect(**connect_options) diff --git a/tests/lib/test_temporal_auth.py b/tests/lib/test_temporal_auth.py index 59c7e1bd7..aff6d6bcd 100644 --- a/tests/lib/test_temporal_auth.py +++ b/tests/lib/test_temporal_auth.py @@ -49,6 +49,7 @@ async def close(self): async def test_connect_without_scope_preserves_legacy_path(monkeypatch): monkeypatch.delenv("TEMPORAL_AUTH_SCOPE", raising=False) + monkeypatch.delenv("TEMPORAL_NAMESPACE", raising=False) client = MagicMock() with patch("agentex.lib.core.clients.temporal.auth.WorkloadIdentityCredential") as credential: @@ -63,6 +64,42 @@ async def test_connect_without_scope_preserves_legacy_path(monkeypatch): connect.assert_awaited_once_with(target_host="temporal:7233") +@pytest.mark.parametrize("scope", [None, _SCOPE]) +async def test_connect_uses_namespace_from_environment(monkeypatch, scope): + if scope is None: + monkeypatch.delenv("TEMPORAL_AUTH_SCOPE", raising=False) + else: + monkeypatch.setenv("TEMPORAL_AUTH_SCOPE", scope) + monkeypatch.setenv("TEMPORAL_NAMESPACE", "tenant-a") + + with patch( + "agentex.lib.core.clients.temporal.auth.WorkloadIdentityCredential", + return_value=FakeCredential(AccessToken(_INITIAL, 2_000)), + ): + with patch( + "agentex.lib.core.clients.temporal.auth.Client.connect", + new=AsyncMock(return_value=MagicMock()), + ) as connect: + with patch("agentex.lib.core.clients.temporal.auth.time.time", return_value=1_000): + with patch.object(TemporalTokenSession, "start"): + await connect_temporal_client({"target_host": "temporal:7233"}) + + assert connect.await_args.kwargs["namespace"] == "tenant-a" + + +async def test_connect_keeps_explicit_namespace(monkeypatch): + monkeypatch.delenv("TEMPORAL_AUTH_SCOPE", raising=False) + monkeypatch.setenv("TEMPORAL_NAMESPACE", "tenant-a") + + with patch( + "agentex.lib.core.clients.temporal.auth.Client.connect", + new=AsyncMock(return_value=MagicMock()), + ) as connect: + await connect_temporal_client({"target_host": "temporal:7233", "namespace": "tenant-b"}) + + connect.assert_awaited_once_with(target_host="temporal:7233", namespace="tenant-b") + + async def test_connect_initial_token_failure_blocks_startup(monkeypatch): monkeypatch.setenv("TEMPORAL_AUTH_SCOPE", _SCOPE) credential = FakeCredential(RuntimeError("identity unavailable")) From 9d0ffdc4438cf9348b65f73fd3139306bab568b0 Mon Sep 17 00:00:00 2001 From: Muhammad Zohaib Date: Sat, 14 Nov 2026 00:02:30 +0300 Subject: [PATCH 2/2] test(temporal): clear TEMPORAL_NAMESPACE for every auth test --- tests/lib/test_temporal_auth.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/lib/test_temporal_auth.py b/tests/lib/test_temporal_auth.py index aff6d6bcd..c04c27c52 100644 --- a/tests/lib/test_temporal_auth.py +++ b/tests/lib/test_temporal_auth.py @@ -47,9 +47,13 @@ async def close(self): self.closed = True +@pytest.fixture(autouse=True) +def _clear_temporal_namespace(monkeypatch): + monkeypatch.delenv("TEMPORAL_NAMESPACE", raising=False) + + async def test_connect_without_scope_preserves_legacy_path(monkeypatch): monkeypatch.delenv("TEMPORAL_AUTH_SCOPE", raising=False) - monkeypatch.delenv("TEMPORAL_NAMESPACE", raising=False) client = MagicMock() with patch("agentex.lib.core.clients.temporal.auth.WorkloadIdentityCredential") as credential: