* Add comprehensive tests for IMAP email restoration functionality - Implement tests for loading labels manifest, including both old and new formats. - Add tests for extracting flags from the manifest. - Create tests for parsing .eml files, including edge cases. - Develop tests for retrieving .eml files from a directory. - Validate configuration settings and handle missing credentials. - Integrate tests for restoring emails from a backup, including Gmail mode and deletion of orphaned emails. - Test progress caching and backup folder discovery. - Ensure robust handling of email labels and flags during restoration. - Verify that the restore process correctly interacts with a mock IMAP server. * Add comprehensive tests for IMAP utilities and compression support - Introduced tests for `imap_common.py` covering environment variable verification, IMAP connection handling, folder name normalization, MIME header decoding, message details extraction, duplicate detection, filename sanitization, and trash folder detection. - Added tests for `imap_compress.py` to validate the functionality of the `_CompressedSocket` wrapper, ensuring data compression and decompression works as expected. - Implemented integration tests to verify that compression is enabled during IMAP connection setup.
170 lines
6.3 KiB
Python
170 lines
6.3 KiB
Python
"""
|
|
Tests for oauth2_google.py
|
|
|
|
Tests cover:
|
|
- Token acquisition using installed app flow
|
|
- Credentials caching and silent token refresh
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../src")))
|
|
|
|
from auth import oauth2_google
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clear_caches():
|
|
"""Clear module-level caches between tests."""
|
|
oauth2_google._creds_cache.clear()
|
|
yield
|
|
oauth2_google._creds_cache.clear()
|
|
|
|
|
|
class TestAcquireToken:
|
|
"""Tests for acquire_token function."""
|
|
|
|
def test_successful_token(self):
|
|
"""Test successful Google token acquisition."""
|
|
mock_credentials = MagicMock()
|
|
mock_credentials.token = "google_test_token"
|
|
|
|
mock_flow = MagicMock()
|
|
mock_flow.run_local_server.return_value = mock_credentials
|
|
|
|
mock_installed_app_flow = MagicMock()
|
|
mock_installed_app_flow.from_client_config.return_value = mock_flow
|
|
|
|
mock_module = MagicMock()
|
|
mock_module.InstalledAppFlow = mock_installed_app_flow
|
|
|
|
with patch.dict("sys.modules", {"google_auth_oauthlib": MagicMock(), "google_auth_oauthlib.flow": mock_module}):
|
|
result = oauth2_google.acquire_token("client-id", "client-secret")
|
|
|
|
assert result == "google_test_token"
|
|
|
|
def test_missing_library(self):
|
|
"""Test exits when google-auth-oauthlib is not installed."""
|
|
with patch.dict("sys.modules", {"google_auth_oauthlib": None, "google_auth_oauthlib.flow": None}):
|
|
with pytest.raises(SystemExit):
|
|
oauth2_google.acquire_token("client-id", "client-secret")
|
|
|
|
def test_no_token_returned(self):
|
|
"""Test returns None when credentials have no token."""
|
|
mock_credentials = MagicMock()
|
|
mock_credentials.token = None
|
|
|
|
mock_flow = MagicMock()
|
|
mock_flow.run_local_server.return_value = mock_credentials
|
|
|
|
mock_installed_app_flow = MagicMock()
|
|
mock_installed_app_flow.from_client_config.return_value = mock_flow
|
|
|
|
mock_module = MagicMock()
|
|
mock_module.InstalledAppFlow = mock_installed_app_flow
|
|
|
|
with patch.dict("sys.modules", {"google_auth_oauthlib": MagicMock(), "google_auth_oauthlib.flow": mock_module}):
|
|
result = oauth2_google.acquire_token("client-id", "client-secret")
|
|
|
|
assert result is None
|
|
|
|
def test_credentials_cached_on_first_call(self):
|
|
"""Test Google credentials are cached after first call."""
|
|
mock_credentials = MagicMock()
|
|
mock_credentials.token = "google_token"
|
|
|
|
mock_flow = MagicMock()
|
|
mock_flow.run_local_server.return_value = mock_credentials
|
|
|
|
mock_installed_app_flow = MagicMock()
|
|
mock_installed_app_flow.from_client_config.return_value = mock_flow
|
|
|
|
mock_module = MagicMock()
|
|
mock_module.InstalledAppFlow = mock_installed_app_flow
|
|
|
|
with patch.dict("sys.modules", {"google_auth_oauthlib": MagicMock(), "google_auth_oauthlib.flow": mock_module}):
|
|
oauth2_google.acquire_token("client-id", "client-secret")
|
|
|
|
assert ("client-id", "client-secret") in oauth2_google._creds_cache
|
|
|
|
def test_cached_credentials_refreshed_on_second_call(self):
|
|
"""Test second call refreshes cached credentials without opening browser."""
|
|
# Pre-populate cache with credentials that have a refresh token
|
|
mock_creds = MagicMock()
|
|
mock_creds.refresh_token = "refresh_tok"
|
|
mock_creds.token = "refreshed_google_token"
|
|
oauth2_google._creds_cache[("client-id", "client-secret")] = mock_creds
|
|
|
|
mock_request_module = MagicMock()
|
|
with patch.dict(
|
|
"sys.modules",
|
|
{
|
|
"google": MagicMock(),
|
|
"google.auth": MagicMock(),
|
|
"google.auth.transport": MagicMock(),
|
|
"google.auth.transport.requests": mock_request_module,
|
|
},
|
|
):
|
|
result = oauth2_google.acquire_token("client-id", "client-secret")
|
|
|
|
assert result == "refreshed_google_token"
|
|
# Verify refresh was called
|
|
mock_creds.refresh.assert_called_once()
|
|
|
|
def test_falls_back_to_browser_if_refresh_fails(self):
|
|
"""Test falls back to full auth flow if cached token refresh fails."""
|
|
# Pre-populate cache with credentials whose refresh fails
|
|
mock_creds = MagicMock()
|
|
mock_creds.refresh_token = "refresh_tok"
|
|
mock_creds.refresh.side_effect = Exception("Refresh failed")
|
|
oauth2_google._creds_cache[("client-id", "client-secret")] = mock_creds
|
|
|
|
# Set up the full auth flow
|
|
mock_credentials = MagicMock()
|
|
mock_credentials.token = "new_browser_token"
|
|
|
|
mock_flow = MagicMock()
|
|
mock_flow.run_local_server.return_value = mock_credentials
|
|
|
|
mock_installed_app_flow = MagicMock()
|
|
mock_installed_app_flow.from_client_config.return_value = mock_flow
|
|
|
|
mock_module = MagicMock()
|
|
mock_module.InstalledAppFlow = mock_installed_app_flow
|
|
|
|
with patch.dict("sys.modules", {"google_auth_oauthlib": MagicMock(), "google_auth_oauthlib.flow": mock_module}):
|
|
result = oauth2_google.acquire_token("client-id", "client-secret")
|
|
|
|
assert result == "new_browser_token"
|
|
|
|
def test_no_refresh_if_no_refresh_token(self):
|
|
"""Test falls back to browser if cached credentials have no refresh token."""
|
|
# Pre-populate cache with credentials that have no refresh token
|
|
mock_creds = MagicMock()
|
|
mock_creds.refresh_token = None
|
|
oauth2_google._creds_cache[("client-id", "client-secret")] = mock_creds
|
|
|
|
# Set up the full auth flow
|
|
mock_credentials = MagicMock()
|
|
mock_credentials.token = "new_browser_token"
|
|
|
|
mock_flow = MagicMock()
|
|
mock_flow.run_local_server.return_value = mock_credentials
|
|
|
|
mock_installed_app_flow = MagicMock()
|
|
mock_installed_app_flow.from_client_config.return_value = mock_flow
|
|
|
|
mock_module = MagicMock()
|
|
mock_module.InstalledAppFlow = mock_installed_app_flow
|
|
|
|
with patch.dict("sys.modules", {"google_auth_oauthlib": MagicMock(), "google_auth_oauthlib.flow": mock_module}):
|
|
result = oauth2_google.acquire_token("client-id", "client-secret")
|
|
|
|
assert result == "new_browser_token"
|
|
# Refresh should not have been called
|
|
mock_creds.refresh.assert_not_called()
|