Files
archon/python/tests/test_supabase_validation.py
Rasmus Widing 3800280f2e Add Supabase key validation and simplify frontend state management
- Add backend validation to detect and warn about anon vs service keys
- Prevent startup with incorrect Supabase key configuration
- Consolidate frontend state management following KISS principles
- Remove duplicate state tracking and sessionStorage polling
- Add clear error display when backend fails to start
- Improve .env.example documentation with detailed key selection guide
- Add comprehensive test coverage for validation logic
- Remove unused test results checking to eliminate 404 errors

The implementation now warns users about key misconfiguration while
maintaining backward compatibility. Frontend state is simplified with
MainLayout as the single source of truth for backend status.
2025-08-16 00:10:23 +03:00

222 lines
7.4 KiB
Python

"""
Unit tests for Supabase key validation functionality.
Tests the JWT-based validation of anon vs service keys.
"""
import pytest
import jwt
from unittest.mock import patch, MagicMock
from src.server.config.config import (
validate_supabase_key,
ConfigurationError,
load_environment_config,
)
def test_validate_anon_key():
"""Test validation detects anon key correctly."""
# Create mock anon key JWT
anon_payload = {"role": "anon", "iss": "supabase"}
anon_token = jwt.encode(anon_payload, "secret", algorithm="HS256")
is_valid, msg = validate_supabase_key(anon_token)
assert is_valid == False
assert msg == "ANON_KEY_DETECTED"
def test_validate_service_key():
"""Test validation detects service key correctly."""
# Create mock service key JWT
service_payload = {"role": "service_role", "iss": "supabase"}
service_token = jwt.encode(service_payload, "secret", algorithm="HS256")
is_valid, msg = validate_supabase_key(service_token)
assert is_valid == True
assert msg == "VALID_SERVICE_KEY"
def test_validate_unknown_key():
"""Test validation handles unknown key roles."""
# Create mock key with unknown role
unknown_payload = {"role": "custom", "iss": "supabase"}
unknown_token = jwt.encode(unknown_payload, "secret", algorithm="HS256")
is_valid, msg = validate_supabase_key(unknown_token)
assert is_valid == False
assert "UNKNOWN_KEY_TYPE" in msg
assert "custom" in msg
def test_validate_invalid_jwt():
"""Test validation handles invalid JWT format gracefully."""
is_valid, msg = validate_supabase_key("not-a-jwt")
# Should allow invalid JWT to proceed (might be new format)
assert is_valid == True
assert msg == "UNABLE_TO_VALIDATE"
def test_validate_empty_key():
"""Test validation handles empty key."""
is_valid, msg = validate_supabase_key("")
assert is_valid == False
assert msg == "EMPTY_KEY"
def test_config_raises_on_anon_key():
"""Test that configuration loading raises error when anon key detected."""
# Create a mock anon key JWT
anon_payload = {"role": "anon", "iss": "supabase"}
mock_anon_key = jwt.encode(anon_payload, "secret", algorithm="HS256")
with patch.dict(
"os.environ",
{
"SUPABASE_URL": "https://test.supabase.co",
"SUPABASE_SERVICE_KEY": mock_anon_key,
"OPENAI_API_KEY": "" # Clear any existing key
},
clear=True # Clear all env vars to ensure isolation
):
with pytest.raises(ConfigurationError) as exc_info:
load_environment_config()
error_message = str(exc_info.value)
assert "CRITICAL: You are using a Supabase ANON key" in error_message
assert "service_role" in error_message
assert "permission denied" in error_message
def test_config_accepts_service_key():
"""Test that configuration loading accepts service key."""
# Create a mock service key JWT
service_payload = {"role": "service_role", "iss": "supabase"}
mock_service_key = jwt.encode(service_payload, "secret", algorithm="HS256")
with patch.dict(
"os.environ",
{
"SUPABASE_URL": "https://test.supabase.co",
"SUPABASE_SERVICE_KEY": mock_service_key,
"PORT": "8051", # Required for config
"OPENAI_API_KEY": "" # Clear any existing key
},
clear=True # Clear all env vars to ensure isolation
):
# Should not raise an exception
config = load_environment_config()
assert config.supabase_service_key == mock_service_key
def test_config_handles_invalid_jwt():
"""Test that configuration loading handles invalid JWT gracefully."""
with patch.dict(
"os.environ",
{
"SUPABASE_URL": "https://test.supabase.co",
"SUPABASE_SERVICE_KEY": "invalid-jwt-key",
"PORT": "8051", # Required for config
"OPENAI_API_KEY": "" # Clear any existing key
},
clear=True # Clear all env vars to ensure isolation
):
with patch("builtins.print") as mock_print:
# Should not raise an exception for invalid JWT
config = load_environment_config()
assert config.supabase_service_key == "invalid-jwt-key"
def test_config_warns_on_unknown_role():
"""Test that configuration loading warns for unknown roles.
NOTE: This currently prints a warning but doesn't fail.
TODO: Per alpha principles, unknown key types should fail fast, not just warn.
"""
# Create a mock key with unknown role
unknown_payload = {"role": "custom_role", "iss": "supabase"}
mock_unknown_key = jwt.encode(unknown_payload, "secret", algorithm="HS256")
with patch.dict(
"os.environ",
{
"SUPABASE_URL": "https://test.supabase.co",
"SUPABASE_SERVICE_KEY": mock_unknown_key,
"PORT": "8051", # Required for config
"OPENAI_API_KEY": "" # Clear any existing key
},
clear=True # Clear all env vars to ensure isolation
):
with patch("builtins.print") as mock_print:
# Should not raise an exception but should print warning
config = load_environment_config()
assert config.supabase_service_key == mock_unknown_key
# Check that warning was printed
mock_print.assert_called_once()
call_args = mock_print.call_args[0][0]
assert "WARNING: Unknown Supabase key role 'custom_role'" in call_args
def test_config_raises_on_anon_key_with_port():
"""Test that anon key detection works properly with all required env vars."""
# Create a mock anon key JWT
anon_payload = {"role": "anon", "iss": "supabase"}
mock_anon_key = jwt.encode(anon_payload, "secret", algorithm="HS256")
with patch.dict(
"os.environ",
{
"SUPABASE_URL": "https://test.supabase.co",
"SUPABASE_SERVICE_KEY": mock_anon_key,
"PORT": "8051",
"OPENAI_API_KEY": "sk-test123" # Valid OpenAI key
},
clear=True
):
# Should still raise ConfigurationError for anon key even with valid OpenAI key
with pytest.raises(ConfigurationError) as exc_info:
load_environment_config()
error_message = str(exc_info.value)
assert "CRITICAL: You are using a Supabase ANON key" in error_message
def test_jwt_decoding_with_real_structure():
"""Test JWT decoding with realistic Supabase JWT structure."""
# More realistic Supabase JWT payload structure
realistic_anon_payload = {
"aud": "authenticated",
"exp": 1999999999,
"iat": 1234567890,
"iss": "supabase",
"ref": "abcdefghij",
"role": "anon",
}
realistic_service_payload = {
"aud": "authenticated",
"exp": 1999999999,
"iat": 1234567890,
"iss": "supabase",
"ref": "abcdefghij",
"role": "service_role",
}
anon_token = jwt.encode(realistic_anon_payload, "secret", algorithm="HS256")
service_token = jwt.encode(realistic_service_payload, "secret", algorithm="HS256")
# Test anon key detection
is_valid_anon, msg_anon = validate_supabase_key(anon_token)
assert is_valid_anon == False
assert msg_anon == "ANON_KEY_DETECTED"
# Test service key detection
is_valid_service, msg_service = validate_supabase_key(service_token)
assert is_valid_service == True
assert msg_service == "VALID_SERVICE_KEY"