import pytest from httpx import AsyncClient from src.services.auth.security import create_access_token, hash_password, verify_password from src.domain.entities import UserRole class TestPasswordHashing: def test_hash_and_verify(self): password = "SecurePass123!" hashed = hash_password(password) assert hashed != password assert verify_password(password, hashed) def test_verify_wrong_password(self): hashed = hash_password("CorrectPass123!") assert not verify_password("WrongPass123!", hashed) class TestJWT: def test_create_access_token(self): token = create_access_token( subject="test-user-id", role=UserRole.ADMIN, scopes=["*"], ) assert isinstance(token, str) assert len(token) > 0 def test_decode_access_token(self): from src.services.auth.security import decode_token, validate_token_type, TokenData token = create_access_token( subject="test-user-id", role=UserRole.VIEWER, scopes=["pharmacies:read"], ) payload = decode_token(token) assert payload["sub"] == "test-user-id" assert payload["role"] == "viewer" assert payload["type"] == "access" validated = validate_token_type(payload, "access") assert validated is not None def test_invalid_token_raises(self): from src.core.exceptions import UnauthorizedException from src.services.auth.security import decode_token with pytest.raises(UnauthorizedException): decode_token("invalid.token.here") class TestRBAC: def test_superadmin_can_access_all(self): from src.services.auth.security import RBACGuard, TokenData token_data = TokenData({"sub": "1", "role": "superadmin", "scopes": ["*"], "type": "access", "iss": "pip-platform", "jti": "test"}) assert RBACGuard.has_role(token_data, UserRole.ADMIN) assert RBACGuard.has_role(token_data, UserRole.VIEWER) assert RBACGuard.has_role(token_data, UserRole.OPERATOR) def test_viewer_cannot_access_admin(self): from src.services.auth.security import RBACGuard, TokenData token_data = TokenData({"sub": "2", "role": "viewer", "scopes": [], "type": "access", "iss": "pip-platform", "jti": "test"}) assert not RBACGuard.has_role(token_data, UserRole.ADMIN) def test_wildcard_scope_grants_all(self): from src.services.auth.security import RBACGuard, TokenData token_data = TokenData({"sub": "1", "role": "superadmin", "scopes": ["*"], "type": "access", "iss": "pip-platform", "jti": "test"}) assert RBACGuard.has_scope(token_data, "pharmacies:read") assert RBACGuard.has_scope(token_data, "pharmacies:write") assert RBACGuard.has_scope(token_data, "anything:anything") def test_specific_scope(self): from src.services.auth.security import RBACGuard, TokenData token_data = TokenData({"sub": "2", "role": "viewer", "scopes": ["pharmacies:read"], "type": "access", "iss": "pip-platform", "jti": "test"}) assert RBACGuard.has_scope(token_data, "pharmacies:read") assert not RBACGuard.has_scope(token_data, "pharmacies:write") class TestAPIKey: def test_generate_api_key(self): from src.services.auth.security import generate_api_key, verify_api_key raw_key, prefix, key_hash = generate_api_key() assert len(raw_key) > 20 assert len(prefix) == 8 assert raw_key[:8] == prefix assert verify_api_key(raw_key, key_hash) assert not verify_api_key("wrong-key", key_hash)