mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-30 05:30:31 -05:00
- 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.
222 lines
7.4 KiB
Python
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"
|