feat: implement mvp with email-first login flow and langgraph architecture

This commit is contained in:
Yunxiao Xu
2026-02-09 23:22:30 -08:00
parent af227d40e6
commit 5a943b902a
79 changed files with 8200 additions and 1 deletions

87
tests/test_oidc_client.py Normal file
View File

@@ -0,0 +1,87 @@
import pytest
from unittest.mock import MagicMock, patch
from ea_chatbot.auth import OIDCClient
@pytest.fixture
def oidc_config():
return {
"client_id": "test_id",
"client_secret": "test_secret",
"server_metadata_url": "https://example.com/.well-known/openid-configuration",
"redirect_uri": "http://localhost:8501"
}
@pytest.fixture
def mock_metadata():
return {
"authorization_endpoint": "https://example.com/auth",
"token_endpoint": "https://example.com/token",
"userinfo_endpoint": "https://example.com/userinfo"
}
def test_oidc_fetch_metadata(oidc_config, mock_metadata):
client = OIDCClient(**oidc_config)
with patch("requests.get") as mock_get:
mock_response = MagicMock()
mock_response.json.return_value = mock_metadata
mock_get.return_value = mock_response
metadata = client.fetch_metadata()
assert metadata == mock_metadata
mock_get.assert_called_once_with(oidc_config["server_metadata_url"])
# Second call should use cache
client.fetch_metadata()
assert mock_get.call_count == 1
def test_oidc_get_login_url(oidc_config, mock_metadata):
client = OIDCClient(**oidc_config)
client.metadata = mock_metadata
with patch.object(client.oauth_session, "create_authorization_url") as mock_create_url:
mock_create_url.return_value = ("https://example.com/auth?state=xyz", "xyz")
url = client.get_login_url()
assert url == "https://example.com/auth?state=xyz"
mock_create_url.assert_called_once_with(mock_metadata["authorization_endpoint"])
def test_oidc_get_login_url_missing_endpoint(oidc_config):
client = OIDCClient(**oidc_config)
client.metadata = {"some_other": "field"}
with pytest.raises(ValueError, match="authorization_endpoint not found"):
client.get_login_url()
def test_oidc_exchange_code_for_token(oidc_config, mock_metadata):
client = OIDCClient(**oidc_config)
client.metadata = mock_metadata
with patch.object(client.oauth_session, "fetch_token") as mock_fetch_token:
mock_fetch_token.return_value = {"access_token": "abc"}
token = client.exchange_code_for_token("test_code")
assert token == {"access_token": "abc"}
mock_fetch_token.assert_called_once_with(
mock_metadata["token_endpoint"],
code="test_code",
client_secret=oidc_config["client_secret"]
)
def test_oidc_get_user_info(oidc_config, mock_metadata):
client = OIDCClient(**oidc_config)
client.metadata = mock_metadata
with patch.object(client.oauth_session, "get") as mock_get:
mock_response = MagicMock()
mock_response.json.return_value = {"sub": "user123"}
mock_get.return_value = mock_response
user_info = client.get_user_info({"access_token": "abc"})
assert user_info == {"sub": "user123"}
assert client.oauth_session.token == {"access_token": "abc"}
mock_get.assert_called_once_with(mock_metadata["userinfo_endpoint"])