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 ,
2424
2525_INTERNAL_ORG_ID = "org_internal_123"
2626_OTHER_ORG_ID = "org_other_456"
27+ _TEST_ROLE_CLAIM = "https://aignostics-platform-bridge/role"
2728_USER_NOT_AUTHENTICATED = "User is not authenticated"
2829_USER_SUB = "auth0|x"
2930_USER_EMAIL = "x@x.com"
3031
3132
3233@pytest .fixture (autouse = True )
3334def _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 .
35+ """Set a real FoundryContext and required AuthSettings env vars for all auth tests .
3536
3637 Yields:
3738 None
3839 """
3940 set_context (make_context ())
41+ os .environ [f"{ TEST_PROJECT_PREFIX } AUTH_INTERNAL_ORG_ID" ] = _INTERNAL_ORG_ID
42+ os .environ [f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" ] = _TEST_ROLE_CLAIM
4043 yield
44+ os .environ .pop (f"{ TEST_PROJECT_PREFIX } AUTH_INTERNAL_ORG_ID" , None )
45+ os .environ .pop (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , None )
4146 reset_context ()
4247
4348
@@ -91,24 +96,23 @@ def test_get_auth_client_returns_client_when_present(self) -> None:
9196
9297@pytest .mark .unit
9398class 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"
99+ """Tests for AuthSettings."""
105100
106101 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 ())
102+ """AuthSettings reads both required fields from env vars using the context's prefix."""
109103 monkeypatch .setenv (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , "https://custom/role" )
110104 settings = AuthSettings ()
111105 assert settings .auth0_role_claim == "https://custom/role"
106+ assert settings .internal_org_id == _INTERNAL_ORG_ID
107+
108+ def test_auth_settings_raises_when_required_fields_absent (self , monkeypatch : pytest .MonkeyPatch ) -> None :
109+ """AuthSettings raises ValidationError when required env vars are absent."""
110+ import pydantic
111+
112+ monkeypatch .delenv (f"{ TEST_PROJECT_PREFIX } AUTH_INTERNAL_ORG_ID" , raising = False )
113+ monkeypatch .delenv (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , raising = False )
114+ with pytest .raises (pydantic .ValidationError ):
115+ AuthSettings ()
112116
113117
114118@pytest .mark .integration
@@ -207,7 +211,7 @@ class TestRequireAdmin:
207211
208212 async def test_no_user_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
209213 """require_admin raises ForbiddenError when no session is available."""
210- monkeypatch .delenv ( "FOUNDRY_AUTH_AUTH0_ROLE_CLAIM " , raising = False )
214+ monkeypatch .setenv ( f" { TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM " , _TEST_ROLE_CLAIM )
211215 request = MagicMock ()
212216 request .app .state = MagicMock (spec = []) # no auth_client → get_user returns None
213217
@@ -216,9 +220,9 @@ async def test_no_user_raises_forbidden_error(self, monkeypatch: pytest.MonkeyPa
216220
217221 async def test_wrong_role_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
218222 """require_admin raises ForbiddenError when user has a non-admin role."""
219- monkeypatch .delenv ( "FOUNDRY_AUTH_AUTH0_ROLE_CLAIM " , raising = False )
223+ monkeypatch .setenv ( f" { TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM " , _TEST_ROLE_CLAIM )
220224 request = MagicMock ()
221- user = {"sub" : _USER_SUB , DEFAULT_AUTH0_ROLE_CLAIM : "viewer" , "exp" : int (time .time ()) + 3600 }
225+ user = {"sub" : _USER_SUB , _TEST_ROLE_CLAIM : "viewer" , "exp" : int (time .time ()) + 3600 }
222226 fake_client = MagicMock ()
223227 fake_client .require_session = AsyncMock (return_value = {"user" : user })
224228 request .app .state .auth_client = fake_client
@@ -228,9 +232,9 @@ async def test_wrong_role_raises_forbidden_error(self, monkeypatch: pytest.Monke
228232
229233 async def test_admin_role_passes (self , monkeypatch : pytest .MonkeyPatch ) -> None :
230234 """require_admin returns None without raising when user has the admin role."""
231- monkeypatch .delenv ( "FOUNDRY_AUTH_AUTH0_ROLE_CLAIM " , raising = False )
235+ monkeypatch .setenv ( f" { TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM " , _TEST_ROLE_CLAIM )
232236 request = MagicMock ()
233- user = {"sub" : _USER_SUB , DEFAULT_AUTH0_ROLE_CLAIM : AUTH0_ROLE_ADMIN , "exp" : int (time .time ()) + 3600 }
237+ user = {"sub" : _USER_SUB , _TEST_ROLE_CLAIM : AUTH0_ROLE_ADMIN , "exp" : int (time .time ()) + 3600 }
234238 fake_client = MagicMock ()
235239 fake_client .require_session = AsyncMock (return_value = {"user" : user })
236240 request .app .state .auth_client = fake_client
@@ -245,7 +249,6 @@ class TestRequireInternal:
245249
246250 async def test_unauthenticated_user_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
247251 """require_internal raises ForbiddenError when no session is available."""
248- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
249252 request = MagicMock ()
250253 request .app .state = MagicMock (spec = []) # no auth_client → get_user returns None
251254
@@ -254,7 +257,6 @@ async def test_unauthenticated_user_raises_forbidden_error(self, monkeypatch: py
254257
255258 async def test_wrong_org_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
256259 """require_internal raises ForbiddenError when user belongs to a different org."""
257- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
258260 request = MagicMock ()
259261 user = {"sub" : _USER_SUB , "org_id" : _OTHER_ORG_ID , "exp" : int (time .time ()) + 3600 }
260262 fake_client = MagicMock ()
@@ -283,7 +285,6 @@ class TestRequireInternalAdmin:
283285
284286 async def test_unauthenticated_user_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
285287 """require_internal_admin raises ForbiddenError when no session is available."""
286- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
287288 request = MagicMock ()
288289 request .app .state = MagicMock (spec = []) # no auth_client → get_user returns None
289290
@@ -292,7 +293,6 @@ async def test_unauthenticated_user_raises_forbidden_error(self, monkeypatch: py
292293
293294 async def test_wrong_org_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
294295 """require_internal_admin raises ForbiddenError when user belongs to a different org."""
295- monkeypatch .setenv ("FOUNDRY_AUTH_INTERNAL_ORG_ID" , _INTERNAL_ORG_ID )
296296 request = MagicMock ()
297297 user = {"sub" : _USER_SUB , "org_id" : _OTHER_ORG_ID , "exp" : int (time .time ()) + 3600 }
298298 fake_client = MagicMock ()
@@ -305,12 +305,12 @@ async def test_wrong_org_raises_forbidden_error(self, monkeypatch: pytest.Monkey
305305 async def test_correct_org_wrong_role_raises_forbidden_error (self , monkeypatch : pytest .MonkeyPatch ) -> None :
306306 """require_internal_admin raises ForbiddenError when user is in internal org but lacks admin role."""
307307 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 )
308+ monkeypatch .setenv (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , _TEST_ROLE_CLAIM )
309309 request = MagicMock ()
310310 user = {
311311 "sub" : _USER_SUB ,
312312 "org_id" : _INTERNAL_ORG_ID ,
313- DEFAULT_AUTH0_ROLE_CLAIM : "viewer" ,
313+ _TEST_ROLE_CLAIM : "viewer" ,
314314 "exp" : int (time .time ()) + 3600 ,
315315 }
316316 fake_client = MagicMock ()
@@ -323,12 +323,12 @@ async def test_correct_org_wrong_role_raises_forbidden_error(self, monkeypatch:
323323 async def test_internal_admin_passes (self , monkeypatch : pytest .MonkeyPatch ) -> None :
324324 """require_internal_admin returns None without raising when user is internal org admin."""
325325 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 )
326+ monkeypatch .setenv (f"{ TEST_PROJECT_PREFIX } AUTH_AUTH0_ROLE_CLAIM" , _TEST_ROLE_CLAIM )
327327 request = MagicMock ()
328328 user = {
329329 "sub" : _USER_SUB ,
330330 "org_id" : _INTERNAL_ORG_ID ,
331- DEFAULT_AUTH0_ROLE_CLAIM : AUTH0_ROLE_ADMIN ,
331+ _TEST_ROLE_CLAIM : AUTH0_ROLE_ADMIN ,
332332 "exp" : int (time .time ()) + 3600 ,
333333 }
334334 fake_client = MagicMock ()
0 commit comments