11"""Tests for aignostics_foundry_core.api.auth."""
22
3+ import os
34import time
45from collections .abc import Generator
56from unittest .mock import AsyncMock , MagicMock
89
910from aignostics_foundry_core .api .auth import (
1011 AUTH0_ROLE_ADMIN ,
11- DEFAULT_AUTH0_ROLE_CLAIM ,
1212 AuthSettings ,
1313 ForbiddenError ,
1414 UnauthenticatedError ,
2020 require_internal_admin ,
2121)
2222from aignostics_foundry_core .foundry import reset_context , set_context
23- from tests .conftest import TEST_PROJECT_PREFIX , make_context
23+ from tests .aignostics_foundry_core .api import AUTH0_ROLE_CLAIM_VAR_NAME , INTERNAL_ORG_ID_VAR_NAME
24+ from tests .conftest import make_context
2425
2526_INTERNAL_ORG_ID = "org_internal_123"
2627_OTHER_ORG_ID = "org_other_456"
28+ _TEST_ROLE_CLAIM = "https://aignostics-platform-bridge/role"
2729_USER_NOT_AUTHENTICATED = "User is not authenticated"
2830_USER_SUB = "auth0|x"
2931_USER_EMAIL = "x@x.com"
3032
3133
3234@pytest .fixture (autouse = True )
3335def _auth_context () -> Generator [None , None , None ]: # pyright: ignore[reportUnusedFunction]
34- """Set a real FoundryContext for all auth tests to preserve FOUNDRY_AUTH_* env var names .
36+ """Set a real FoundryContext and required AuthSettings env vars for all auth tests .
3537
3638 Yields:
3739 None
3840 """
3941 set_context (make_context ())
42+ os .environ [INTERNAL_ORG_ID_VAR_NAME ] = _INTERNAL_ORG_ID
43+ os .environ [AUTH0_ROLE_CLAIM_VAR_NAME ] = _TEST_ROLE_CLAIM
4044 yield
45+ os .environ .pop (INTERNAL_ORG_ID_VAR_NAME , None )
46+ os .environ .pop (AUTH0_ROLE_CLAIM_VAR_NAME , None )
4147 reset_context ()
4248
4349
@@ -91,24 +97,23 @@ def test_get_auth_client_returns_client_when_present(self) -> None:
9197
9298@pytest .mark .unit
9399class TestAuthSettings :
94- """Tests for AuthSettings defaults."""
95-
96- def test_auth_settings_defaults (self ) -> None :
97- """AuthSettings.auth0_role_claim has the expected default role claim URL."""
98- settings = AuthSettings ()
99- assert settings .auth0_role_claim == DEFAULT_AUTH0_ROLE_CLAIM
100- assert settings .internal_org_id is None
101-
102- def test_auth_settings_role_claim_value (self ) -> None :
103- """The default role claim is the Aignostics platform bridge claim URL."""
104- assert DEFAULT_AUTH0_ROLE_CLAIM == "https://aignostics-platform-bridge/role"
100+ """Tests for AuthSettings."""
105101
106102 def test_auth_settings_uses_context_env_prefix (self , monkeypatch : pytest .MonkeyPatch ) -> None :
107- """AuthSettings reads env vars from the prefix supplied by FoundryContext."""
108- set_context (make_context ())
109- monkeypatch .setenv (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , "https://custom/role" )
103+ """AuthSettings reads both required fields from env vars using the context's prefix."""
104+ monkeypatch .setenv (AUTH0_ROLE_CLAIM_VAR_NAME , "https://custom/role" )
110105 settings = AuthSettings ()
111106 assert settings .auth0_role_claim == "https://custom/role"
107+ assert settings .internal_org_id == _INTERNAL_ORG_ID
108+
109+ def test_auth_settings_raises_when_required_fields_absent (self , monkeypatch : pytest .MonkeyPatch ) -> None :
110+ """AuthSettings raises ValidationError when required env vars are absent."""
111+ import pydantic
112+
113+ monkeypatch .delenv (INTERNAL_ORG_ID_VAR_NAME , raising = False )
114+ monkeypatch .delenv (AUTH0_ROLE_CLAIM_VAR_NAME , raising = False )
115+ with pytest .raises (pydantic .ValidationError ):
116+ AuthSettings ()
112117
113118
114119@pytest .mark .integration
@@ -207,7 +212,7 @@ class TestRequireAdmin:
207212
208213 async def test_no_user_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
209214 """require_admin raises ForbiddenError when no session is available."""
210- monkeypatch .delenv ( "FOUNDRY_AUTH_AUTH0_ROLE_CLAIM" , raising = False )
215+ monkeypatch .setenv ( AUTH0_ROLE_CLAIM_VAR_NAME , _TEST_ROLE_CLAIM )
211216 request = MagicMock ()
212217 request .app .state = MagicMock (spec = []) # no auth_client → get_user returns None
213218
@@ -216,9 +221,9 @@ async def test_no_user_raises_forbidden_error(self, monkeypatch: pytest.MonkeyPa
216221
217222 async def test_wrong_role_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
218223 """require_admin raises ForbiddenError when user has a non-admin role."""
219- monkeypatch .delenv ( "FOUNDRY_AUTH_AUTH0_ROLE_CLAIM" , raising = False )
224+ monkeypatch .setenv ( AUTH0_ROLE_CLAIM_VAR_NAME , _TEST_ROLE_CLAIM )
220225 request = MagicMock ()
221- user = {"sub" : _USER_SUB , DEFAULT_AUTH0_ROLE_CLAIM : "viewer" , "exp" : int (time .time ()) + 3600 }
226+ user = {"sub" : _USER_SUB , _TEST_ROLE_CLAIM : "viewer" , "exp" : int (time .time ()) + 3600 }
222227 fake_client = MagicMock ()
223228 fake_client .require_session = AsyncMock (return_value = {"user" : user })
224229 request .app .state .auth_client = fake_client
@@ -228,9 +233,9 @@ async def test_wrong_role_raises_forbidden_error(self, monkeypatch: pytest.Monke
228233
229234 async def test_admin_role_passes (self , monkeypatch : pytest .MonkeyPatch ) -> None :
230235 """require_admin returns None without raising when user has the admin role."""
231- monkeypatch .delenv ( "FOUNDRY_AUTH_AUTH0_ROLE_CLAIM" , raising = False )
236+ monkeypatch .setenv ( AUTH0_ROLE_CLAIM_VAR_NAME , _TEST_ROLE_CLAIM )
232237 request = MagicMock ()
233- user = {"sub" : _USER_SUB , DEFAULT_AUTH0_ROLE_CLAIM : AUTH0_ROLE_ADMIN , "exp" : int (time .time ()) + 3600 }
238+ user = {"sub" : _USER_SUB , _TEST_ROLE_CLAIM : AUTH0_ROLE_ADMIN , "exp" : int (time .time ()) + 3600 }
234239 fake_client = MagicMock ()
235240 fake_client .require_session = AsyncMock (return_value = {"user" : user })
236241 request .app .state .auth_client = fake_client
@@ -245,7 +250,6 @@ class TestRequireInternal:
245250
246251 async def test_unauthenticated_user_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
247252 """require_internal raises ForbiddenError when no session is available."""
248- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
249253 request = MagicMock ()
250254 request .app .state = MagicMock (spec = []) # no auth_client → get_user returns None
251255
@@ -254,7 +258,6 @@ async def test_unauthenticated_user_raises_forbidden_error(self, monkeypatch: py
254258
255259 async def test_wrong_org_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
256260 """require_internal raises ForbiddenError when user belongs to a different org."""
257- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
258261 request = MagicMock ()
259262 user = {"sub" : _USER_SUB , "org_id" : _OTHER_ORG_ID , "exp" : int (time .time ()) + 3600 }
260263 fake_client = MagicMock ()
@@ -266,7 +269,7 @@ async def test_wrong_org_raises_forbidden_error(self, monkeypatch: pytest.Monkey
266269
267270 async def test_internal_org_member_passes (self , monkeypatch : pytest .MonkeyPatch ) -> None :
268271 """require_internal returns None without raising when user is in the internal org."""
269- monkeypatch .setenv (f" { TEST_PROJECT_PREFIX } AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
272+ monkeypatch .setenv (INTERNAL_ORG_ID_VAR_NAME , _INTERNAL_ORG_ID )
270273 request = MagicMock ()
271274 user = {"sub" : _USER_SUB , "org_id" : _INTERNAL_ORG_ID , "exp" : int (time .time ()) + 3600 }
272275 fake_client = MagicMock ()
@@ -283,7 +286,6 @@ class TestRequireInternalAdmin:
283286
284287 async def test_unauthenticated_user_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
285288 """require_internal_admin raises ForbiddenError when no session is available."""
286- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
287289 request = MagicMock ()
288290 request .app .state = MagicMock (spec = []) # no auth_client → get_user returns None
289291
@@ -292,7 +294,6 @@ async def test_unauthenticated_user_raises_forbidden_error(self, monkeypatch: py
292294
293295 async def test_wrong_org_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
294296 """require_internal_admin raises ForbiddenError when user belongs to a different org."""
295- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
296297 request = MagicMock ()
297298 user = {"sub" : _USER_SUB , "org_id" : _OTHER_ORG_ID , "exp" : int (time .time ()) + 3600 }
298299 fake_client = MagicMock ()
@@ -304,13 +305,11 @@ async def test_wrong_org_raises_forbidden_error(self, monkeypatch: pytest.Monkey
304305
305306 async def test_correct_org_wrong_role_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
306307 """require_internal_admin raises ForbiddenError when user is in internal org but lacks admin role."""
307- monkeypatch .setenv (f"{ TEST_PROJECT_PREFIX } AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
308- monkeypatch .delenv (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , raising = False )
309308 request = MagicMock ()
310309 user = {
311310 "sub" : _USER_SUB ,
312311 "org_id" : _INTERNAL_ORG_ID ,
313- DEFAULT_AUTH0_ROLE_CLAIM : "viewer" ,
312+ _TEST_ROLE_CLAIM : "viewer" ,
314313 "exp" : int (time .time ()) + 3600 ,
315314 }
316315 fake_client = MagicMock ()
@@ -322,13 +321,11 @@ async def test_correct_org_wrong_role_raises_forbidden_error(self, monkeypatch:
322321
323322 async def test_internal_admin_passes (self , monkeypatch : pytest .MonkeyPatch ) -> None :
324323 """require_internal_admin returns None without raising when user is internal org admin."""
325- monkeypatch .setenv (f"{ TEST_PROJECT_PREFIX } AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
326- monkeypatch .delenv (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , raising = False )
327324 request = MagicMock ()
328325 user = {
329326 "sub" : _USER_SUB ,
330327 "org_id" : _INTERNAL_ORG_ID ,
331- DEFAULT_AUTH0_ROLE_CLAIM : AUTH0_ROLE_ADMIN ,
328+ _TEST_ROLE_CLAIM : AUTH0_ROLE_ADMIN ,
332329 "exp" : int (time .time ()) + 3600 ,
333330 }
334331 fake_client = MagicMock ()
0 commit comments