"""Unit tests for cloud mode authentication exceptions."""
from sso_mcp_server.auth.exceptions import (
CloudAuthError,
CloudTokenExpiredError,
InsufficientScopeError,
InvalidAudienceError,
InvalidIssuerError,
InvalidTokenError,
JWKSFetchError,
MissingAuthorizationError,
TokenSignatureError,
)
class TestCloudAuthError:
"""Tests for CloudAuthError base class."""
def test_default_values(self):
"""Test CloudAuthError default values."""
error = CloudAuthError(message="Test error")
assert error.message == "Test error"
assert error.http_status == 401
assert error.error == "invalid_token"
assert error.code == "CLOUD_AUTH_ERROR"
def test_custom_values(self):
"""Test CloudAuthError with custom values."""
error = CloudAuthError(
message="Custom error",
action="Do something",
code="CUSTOM_CODE",
http_status=403,
error="insufficient_scope",
error_description="Need more scopes",
)
assert error.message == "Custom error"
assert error.action == "Do something"
assert error.code == "CUSTOM_CODE"
assert error.http_status == 403
assert error.error == "insufficient_scope"
assert error.error_description == "Need more scopes"
class TestMissingAuthorizationError:
"""Tests for MissingAuthorizationError."""
def test_default_values(self):
"""Test MissingAuthorizationError default values."""
error = MissingAuthorizationError()
assert error.code == "MISSING_AUTHORIZATION"
assert error.http_status == 401
assert error.error == "invalid_request"
assert "Authorization header is required" in error.message
assert "Bearer" in error.action
def test_error_description(self):
"""Test error_description is set."""
error = MissingAuthorizationError()
assert "Missing Authorization header" in error.error_description
class TestInvalidTokenError:
"""Tests for InvalidTokenError."""
def test_default_message(self):
"""Test InvalidTokenError with no reason."""
error = InvalidTokenError()
assert error.code == "INVALID_TOKEN"
assert error.http_status == 401
assert error.error == "invalid_token"
assert "access token is invalid" in error.message
def test_with_reason(self):
"""Test InvalidTokenError with reason."""
error = InvalidTokenError(reason="Token is malformed")
assert "Token is malformed" in error.message
assert error.error_description == "Token is malformed"
class TestTokenSignatureError:
"""Tests for TokenSignatureError."""
def test_default_values(self):
"""Test TokenSignatureError default values."""
error = TokenSignatureError()
assert error.code == "INVALID_SIGNATURE"
assert error.http_status == 401
assert error.error == "invalid_token"
assert "signature verification failed" in error.message.lower()
class TestCloudTokenExpiredError:
"""Tests for CloudTokenExpiredError."""
def test_default_values(self):
"""Test CloudTokenExpiredError default values."""
error = CloudTokenExpiredError()
assert error.code == "TOKEN_EXPIRED"
assert error.http_status == 401
assert error.error == "invalid_token"
assert "expired" in error.message.lower()
assert "expired" in error.error_description.lower()
class TestInvalidAudienceError:
"""Tests for InvalidAudienceError."""
def test_with_string_audience(self):
"""Test InvalidAudienceError with string audience."""
error = InvalidAudienceError(
expected="https://expected.example.com",
actual="https://actual.example.com",
)
assert error.code == "INVALID_AUDIENCE"
assert error.http_status == 401
assert "https://expected.example.com" in error.message
assert "https://actual.example.com" in error.message
def test_with_list_audience(self):
"""Test InvalidAudienceError with list audience."""
error = InvalidAudienceError(
expected="https://expected.example.com",
actual=["https://aud1.example.com", "https://aud2.example.com"],
)
assert "https://aud1.example.com" in error.message
assert "https://aud2.example.com" in error.message
class TestInvalidIssuerError:
"""Tests for InvalidIssuerError."""
def test_default_values(self):
"""Test InvalidIssuerError default values."""
error = InvalidIssuerError(issuer="https://untrusted.example.com")
assert error.code == "INVALID_ISSUER"
assert error.http_status == 401
assert error.error == "invalid_token"
assert "https://untrusted.example.com" in error.message
assert "not trusted" in error.message.lower()
class TestInsufficientScopeError:
"""Tests for InsufficientScopeError."""
def test_default_values(self):
"""Test InsufficientScopeError default values."""
error = InsufficientScopeError(
required_scopes=["read", "write"],
actual_scopes=["read"],
)
assert error.code == "INSUFFICIENT_SCOPE"
assert error.http_status == 403 # Note: 403, not 401
assert error.error == "insufficient_scope"
assert "read write" in error.message or "read" in error.message
assert error.required_scopes == ["read", "write"]
assert error.actual_scopes == ["read"]
def test_with_empty_actual_scopes(self):
"""Test InsufficientScopeError with no actual scopes."""
error = InsufficientScopeError(
required_scopes=["admin"],
actual_scopes=[],
)
assert "(none)" in error.message
assert "admin" in error.error_description
class TestJWKSFetchError:
"""Tests for JWKSFetchError."""
def test_without_reason(self):
"""Test JWKSFetchError without reason."""
error = JWKSFetchError(issuer="https://issuer.example.com")
assert error.code == "JWKS_FETCH_ERROR"
assert error.http_status == 401
assert "https://issuer.example.com" in error.message
def test_with_reason(self):
"""Test JWKSFetchError with reason."""
error = JWKSFetchError(
issuer="https://issuer.example.com",
reason="Connection timeout",
)
assert "Connection timeout" in error.message
class TestExceptionInheritance:
"""Tests for exception inheritance hierarchy."""
def test_cloud_errors_inherit_from_cloud_auth_error(self):
"""Test all cloud errors inherit from CloudAuthError."""
assert issubclass(MissingAuthorizationError, CloudAuthError)
assert issubclass(InvalidTokenError, CloudAuthError)
assert issubclass(TokenSignatureError, CloudAuthError)
assert issubclass(CloudTokenExpiredError, CloudAuthError)
assert issubclass(InvalidAudienceError, CloudAuthError)
assert issubclass(InvalidIssuerError, CloudAuthError)
assert issubclass(InsufficientScopeError, CloudAuthError)
assert issubclass(JWKSFetchError, CloudAuthError)
def test_can_catch_all_cloud_errors(self):
"""Test can catch all cloud errors with CloudAuthError."""
errors = [
MissingAuthorizationError(),
InvalidTokenError(),
TokenSignatureError(),
CloudTokenExpiredError(),
InvalidAudienceError("expected", "actual"),
InvalidIssuerError("issuer"),
InsufficientScopeError(["read"], []),
JWKSFetchError("issuer"),
]
for error in errors:
try:
raise error
except CloudAuthError as e:
assert e is error