diff --git a/tests/auth/test_google_auth_pkce.py b/tests/auth/test_google_auth_pkce.py new file mode 100644 index 0000000..57e775e --- /dev/null +++ b/tests/auth/test_google_auth_pkce.py @@ -0,0 +1,118 @@ +"""Regression tests for OAuth PKCE flow wiring.""" + +import os +import sys +from unittest.mock import patch + + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))) + +from auth.google_auth import create_oauth_flow # noqa: E402 + + +DUMMY_CLIENT_CONFIG = { + "web": { + "client_id": "dummy-client-id.apps.googleusercontent.com", + "client_secret": "dummy-secret", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + } +} + + +def test_create_oauth_flow_autogenerates_verifier_when_missing(): + expected_flow = object() + with ( + patch( + "auth.google_auth.load_client_secrets_from_env", + return_value=DUMMY_CLIENT_CONFIG, + ), + patch( + "auth.google_auth.Flow.from_client_config", + return_value=expected_flow, + ) as mock_from_client_config, + ): + flow = create_oauth_flow( + scopes=["openid"], + redirect_uri="http://localhost/callback", + state="oauth-state-1", + ) + + assert flow is expected_flow + args, kwargs = mock_from_client_config.call_args + assert args[0] == DUMMY_CLIENT_CONFIG + assert kwargs["autogenerate_code_verifier"] is True + assert "code_verifier" not in kwargs + + +def test_create_oauth_flow_preserves_callback_verifier(): + expected_flow = object() + with ( + patch( + "auth.google_auth.load_client_secrets_from_env", + return_value=DUMMY_CLIENT_CONFIG, + ), + patch( + "auth.google_auth.Flow.from_client_config", + return_value=expected_flow, + ) as mock_from_client_config, + ): + flow = create_oauth_flow( + scopes=["openid"], + redirect_uri="http://localhost/callback", + state="oauth-state-2", + code_verifier="saved-verifier", + ) + + assert flow is expected_flow + args, kwargs = mock_from_client_config.call_args + assert args[0] == DUMMY_CLIENT_CONFIG + assert kwargs["code_verifier"] == "saved-verifier" + assert kwargs["autogenerate_code_verifier"] is False + + +def test_create_oauth_flow_file_config_still_enables_pkce(): + expected_flow = object() + with ( + patch("auth.google_auth.load_client_secrets_from_env", return_value=None), + patch("auth.google_auth.os.path.exists", return_value=True), + patch( + "auth.google_auth.Flow.from_client_secrets_file", + return_value=expected_flow, + ) as mock_from_file, + ): + flow = create_oauth_flow( + scopes=["openid"], + redirect_uri="http://localhost/callback", + state="oauth-state-3", + ) + + assert flow is expected_flow + _args, kwargs = mock_from_file.call_args + assert kwargs["autogenerate_code_verifier"] is True + assert "code_verifier" not in kwargs + + +def test_create_oauth_flow_allows_disabling_autogenerate_without_verifier(): + expected_flow = object() + with ( + patch( + "auth.google_auth.load_client_secrets_from_env", + return_value=DUMMY_CLIENT_CONFIG, + ), + patch( + "auth.google_auth.Flow.from_client_config", + return_value=expected_flow, + ) as mock_from_client_config, + ): + flow = create_oauth_flow( + scopes=["openid"], + redirect_uri="http://localhost/callback", + state="oauth-state-4", + autogenerate_code_verifier=False, + ) + + assert flow is expected_flow + _args, kwargs = mock_from_client_config.call_args + assert kwargs["autogenerate_code_verifier"] is False + assert "code_verifier" not in kwargs