Files
FastAnime/tests/interactive/menus/test_auth.py
2025-07-15 00:02:55 +03:00

434 lines
17 KiB
Python

"""
Tests for the authentication menu functionality.
"""
import pytest
from unittest.mock import Mock, patch, MagicMock
from fastanime.cli.interactive.menus.auth import auth
from fastanime.cli.interactive.state import ControlFlow, State
from fastanime.libs.api.types import UserProfile
class TestAuthMenu:
"""Test cases for the authentication menu."""
def test_auth_menu_not_authenticated(self, mock_context, empty_state):
"""Test auth menu when user is not authenticated."""
# User not authenticated
mock_context.media_api.user_profile = None
mock_context.selector.choose.return_value = None
result = auth(mock_context, empty_state)
# Should go back when no choice is made
assert result == ControlFlow.BACK
# Verify selector was called with login options
mock_context.selector.choose.assert_called_once()
call_args = mock_context.selector.choose.call_args
choices = call_args[1]['choices']
# Should contain login options
login_options = ["Login to AniList", "How to Get Token", "Back to Main Menu"]
for option in login_options:
assert any(option in choice for choice in choices)
def test_auth_menu_authenticated(self, mock_context, empty_state):
"""Test auth menu when user is authenticated."""
# User authenticated
mock_context.media_api.user_profile = UserProfile(
id=12345,
name="TestUser",
avatar="https://example.com/avatar.jpg"
)
mock_context.selector.choose.return_value = None
result = auth(mock_context, empty_state)
# Should go back when no choice is made
assert result == ControlFlow.BACK
# Verify selector was called with authenticated options
mock_context.selector.choose.assert_called_once()
call_args = mock_context.selector.choose.call_args
choices = call_args[1]['choices']
# Should contain authenticated user options
auth_options = ["View Profile Details", "Logout", "Back to Main Menu"]
for option in auth_options:
assert any(option in choice for choice in choices)
def test_auth_menu_login_selection(self, mock_context, empty_state):
"""Test selecting login from auth menu."""
mock_context.media_api.user_profile = None
# Setup selector to return login choice
login_choice = "🔐 Login to AniList"
mock_context.selector.choose.return_value = login_choice
with patch('fastanime.cli.interactive.menus.auth._handle_login') as mock_login:
mock_login.return_value = State(menu_name="MAIN")
result = auth(mock_context, empty_state)
# Should call login handler
mock_login.assert_called_once()
assert isinstance(result, State)
def test_auth_menu_logout_selection(self, mock_context, empty_state):
"""Test selecting logout from auth menu."""
mock_context.media_api.user_profile = UserProfile(
id=12345,
name="TestUser",
avatar="https://example.com/avatar.jpg"
)
# Setup selector to return logout choice
logout_choice = "🔓 Logout"
mock_context.selector.choose.return_value = logout_choice
with patch('fastanime.cli.interactive.menus.auth._handle_logout') as mock_logout:
mock_logout.return_value = ControlFlow.CONTINUE
result = auth(mock_context, empty_state)
# Should call logout handler
mock_logout.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_auth_menu_view_profile_selection(self, mock_context, empty_state):
"""Test selecting view profile from auth menu."""
mock_context.media_api.user_profile = UserProfile(
id=12345,
name="TestUser",
avatar="https://example.com/avatar.jpg"
)
# Setup selector to return profile choice
profile_choice = "👤 View Profile Details"
mock_context.selector.choose.return_value = profile_choice
with patch('fastanime.cli.interactive.menus.auth._display_user_profile_details') as mock_display:
with patch('fastanime.cli.interactive.menus.auth.create_feedback_manager') as mock_feedback:
mock_feedback_obj = Mock()
mock_feedback.return_value = mock_feedback_obj
result = auth(mock_context, empty_state)
# Should display profile details and continue
mock_display.assert_called_once()
mock_feedback_obj.pause_for_user.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_auth_menu_token_help_selection(self, mock_context, empty_state):
"""Test selecting token help from auth menu."""
mock_context.media_api.user_profile = None
# Setup selector to return help choice
help_choice = "❓ How to Get Token"
mock_context.selector.choose.return_value = help_choice
with patch('fastanime.cli.interactive.menus.auth._display_token_help') as mock_help:
with patch('fastanime.cli.interactive.menus.auth.create_feedback_manager') as mock_feedback:
mock_feedback_obj = Mock()
mock_feedback.return_value = mock_feedback_obj
result = auth(mock_context, empty_state)
# Should display token help and continue
mock_help.assert_called_once()
mock_feedback_obj.pause_for_user.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_auth_menu_back_selection(self, mock_context, empty_state):
"""Test selecting back from auth menu."""
mock_context.media_api.user_profile = None
# Setup selector to return back choice
back_choice = "↩️ Back to Main Menu"
mock_context.selector.choose.return_value = back_choice
result = auth(mock_context, empty_state)
assert result == ControlFlow.BACK
def test_auth_menu_icons_enabled(self, mock_context, empty_state):
"""Test auth menu with icons enabled."""
mock_context.config.general.icons = True
mock_context.media_api.user_profile = None
mock_context.selector.choose.return_value = None
result = auth(mock_context, empty_state)
# Should work with icons enabled
assert result == ControlFlow.BACK
def test_auth_menu_icons_disabled(self, mock_context, empty_state):
"""Test auth menu with icons disabled."""
mock_context.config.general.icons = False
mock_context.media_api.user_profile = None
mock_context.selector.choose.return_value = None
result = auth(mock_context, empty_state)
# Should work with icons disabled
assert result == ControlFlow.BACK
class TestAuthMenuHelperFunctions:
"""Test the helper functions in auth menu."""
def test_display_auth_status_authenticated(self, mock_context):
"""Test displaying auth status when authenticated."""
from fastanime.cli.interactive.menus.auth import _display_auth_status
console = Mock()
user_profile = UserProfile(
id=12345,
name="TestUser",
avatar="https://example.com/avatar.jpg"
)
_display_auth_status(console, user_profile, True)
# Should print panel with user info
console.print.assert_called()
# Check that panel was created with user information
panel_call = console.print.call_args_list[0][0][0]
assert "TestUser" in str(panel_call)
def test_display_auth_status_not_authenticated(self, mock_context):
"""Test displaying auth status when not authenticated."""
from fastanime.cli.interactive.menus.auth import _display_auth_status
console = Mock()
_display_auth_status(console, None, True)
# Should print panel with login info
console.print.assert_called()
# Check that panel was created with login information
panel_call = console.print.call_args_list[0][0][0]
assert "Log in to access" in str(panel_call)
def test_handle_login_flow_selection(self, mock_context):
"""Test handling login with flow selection."""
from fastanime.cli.interactive.menus.auth import _handle_login
auth_manager = Mock()
feedback = Mock()
# Mock selector to choose OAuth flow
mock_context.selector.choose.return_value = "🔗 OAuth Browser Flow"
with patch('fastanime.cli.interactive.menus.auth._handle_oauth_flow') as mock_oauth:
mock_oauth.return_value = ControlFlow.CONTINUE
result = _handle_login(mock_context, auth_manager, feedback, True)
# Should call OAuth flow handler
mock_oauth.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_handle_login_token_selection(self, mock_context):
"""Test handling login with token input."""
from fastanime.cli.interactive.menus.auth import _handle_login
auth_manager = Mock()
feedback = Mock()
# Mock selector to choose token input
mock_context.selector.choose.return_value = "🔑 Enter Access Token"
with patch('fastanime.cli.interactive.menus.auth._handle_token_input') as mock_token:
mock_token.return_value = ControlFlow.CONTINUE
result = _handle_login(mock_context, auth_manager, feedback, True)
# Should call token input handler
mock_token.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_handle_login_back_selection(self, mock_context):
"""Test handling login with back selection."""
from fastanime.cli.interactive.menus.auth import _handle_login
auth_manager = Mock()
feedback = Mock()
# Mock selector to choose back
mock_context.selector.choose.return_value = "↩️ Back"
result = _handle_login(mock_context, auth_manager, feedback, True)
# Should return CONTINUE (stay in auth menu)
assert result == ControlFlow.CONTINUE
def test_handle_logout_success(self, mock_context):
"""Test successful logout."""
from fastanime.cli.interactive.menus.auth import _handle_logout
auth_manager = Mock()
feedback = Mock()
# Mock successful logout
auth_manager.logout.return_value = True
feedback.confirm.return_value = True
result = _handle_logout(mock_context, auth_manager, feedback, True)
# Should logout and reload context
auth_manager.logout.assert_called_once()
assert result == ControlFlow.RELOAD_CONFIG
def test_handle_logout_cancelled(self, mock_context):
"""Test cancelled logout."""
from fastanime.cli.interactive.menus.auth import _handle_logout
auth_manager = Mock()
feedback = Mock()
# Mock cancelled logout
feedback.confirm.return_value = False
result = _handle_logout(mock_context, auth_manager, feedback, True)
# Should not logout and continue
auth_manager.logout.assert_not_called()
assert result == ControlFlow.CONTINUE
def test_handle_logout_failure(self, mock_context):
"""Test failed logout."""
from fastanime.cli.interactive.menus.auth import _handle_logout
auth_manager = Mock()
feedback = Mock()
# Mock failed logout
auth_manager.logout.return_value = False
feedback.confirm.return_value = True
result = _handle_logout(mock_context, auth_manager, feedback, True)
# Should try logout but continue on failure
auth_manager.logout.assert_called_once()
feedback.error.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_handle_oauth_flow_success(self, mock_context):
"""Test successful OAuth flow."""
from fastanime.cli.interactive.menus.auth import _handle_oauth_flow
auth_manager = Mock()
feedback = Mock()
# Mock successful OAuth
auth_manager.start_oauth_flow.return_value = ("auth_url", "device_code")
auth_manager.poll_for_token.return_value = True
with patch('fastanime.cli.interactive.menus.auth.webbrowser.open') as mock_browser:
result = _handle_oauth_flow(mock_context, auth_manager, feedback, True)
# Should open browser and reload config
mock_browser.assert_called_once()
auth_manager.start_oauth_flow.assert_called_once()
auth_manager.poll_for_token.assert_called_once()
assert result == ControlFlow.RELOAD_CONFIG
def test_handle_oauth_flow_failure(self, mock_context):
"""Test failed OAuth flow."""
from fastanime.cli.interactive.menus.auth import _handle_oauth_flow
auth_manager = Mock()
feedback = Mock()
# Mock failed OAuth
auth_manager.start_oauth_flow.return_value = ("auth_url", "device_code")
auth_manager.poll_for_token.return_value = False
with patch('fastanime.cli.interactive.menus.auth.webbrowser.open'):
result = _handle_oauth_flow(mock_context, auth_manager, feedback, True)
# Should continue on failure
feedback.error.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_handle_token_input_success(self, mock_context):
"""Test successful token input."""
from fastanime.cli.interactive.menus.auth import _handle_token_input
auth_manager = Mock()
feedback = Mock()
# Mock token input
mock_context.selector.ask.return_value = "valid_token"
auth_manager.save_token.return_value = True
result = _handle_token_input(mock_context, auth_manager, feedback, True)
# Should save token and reload config
auth_manager.save_token.assert_called_once_with("valid_token")
assert result == ControlFlow.RELOAD_CONFIG
def test_handle_token_input_empty(self, mock_context):
"""Test empty token input."""
from fastanime.cli.interactive.menus.auth import _handle_token_input
auth_manager = Mock()
feedback = Mock()
# Mock empty token input
mock_context.selector.ask.return_value = ""
result = _handle_token_input(mock_context, auth_manager, feedback, True)
# Should continue without saving
auth_manager.save_token.assert_not_called()
assert result == ControlFlow.CONTINUE
def test_handle_token_input_failure(self, mock_context):
"""Test failed token input."""
from fastanime.cli.interactive.menus.auth import _handle_token_input
auth_manager = Mock()
feedback = Mock()
# Mock token input with save failure
mock_context.selector.ask.return_value = "invalid_token"
auth_manager.save_token.return_value = False
result = _handle_token_input(mock_context, auth_manager, feedback, True)
# Should continue on save failure
auth_manager.save_token.assert_called_once_with("invalid_token")
feedback.error.assert_called_once()
assert result == ControlFlow.CONTINUE
def test_display_user_profile_details(self, mock_context):
"""Test displaying user profile details."""
from fastanime.cli.interactive.menus.auth import _display_user_profile_details
console = Mock()
user_profile = UserProfile(
id=12345,
name="TestUser",
avatar="https://example.com/avatar.jpg"
)
_display_user_profile_details(console, user_profile, True)
# Should print table with user details
console.print.assert_called()
def test_display_token_help(self, mock_context):
"""Test displaying token help information."""
from fastanime.cli.interactive.menus.auth import _display_token_help
console = Mock()
_display_token_help(console, True)
# Should print help information
console.print.assert_called()