mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-30 22:50:45 -08:00
377 lines
17 KiB
Python
377 lines
17 KiB
Python
"""
|
|
Tests for the main menu functionality.
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock, patch, MagicMock
|
|
|
|
from fastanime.cli.interactive.menus.main import main
|
|
from fastanime.cli.interactive.state import ControlFlow, State, MediaApiState
|
|
from fastanime.libs.api.types import MediaSearchResult
|
|
|
|
|
|
class TestMainMenu:
|
|
"""Test cases for the main menu."""
|
|
|
|
def test_main_menu_displays_options(self, mock_context, empty_state):
|
|
"""Test that the main menu displays all expected options."""
|
|
# Setup selector to return None (exit)
|
|
mock_context.selector.choose.return_value = None
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should return EXIT when no choice is made
|
|
assert result == ControlFlow.EXIT
|
|
|
|
# Verify selector was called with expected options
|
|
mock_context.selector.choose.assert_called_once()
|
|
call_args = mock_context.selector.choose.call_args
|
|
choices = call_args[1]['choices']
|
|
|
|
# Check that key options are present
|
|
expected_options = [
|
|
"Trending", "Popular", "Favourites", "Top Scored",
|
|
"Upcoming", "Recently Updated", "Random", "Search",
|
|
"Watching", "Planned", "Completed", "Paused", "Dropped", "Rewatching",
|
|
"Local Watch History", "Authentication", "Session Management",
|
|
"Edit Config", "Exit"
|
|
]
|
|
|
|
for option in expected_options:
|
|
assert any(option in choice for choice in choices)
|
|
|
|
def test_main_menu_trending_selection(self, mock_context, empty_state):
|
|
"""Test selecting trending anime from main menu."""
|
|
# Setup selector to return trending choice
|
|
trending_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Trending" in choice)
|
|
mock_context.selector.choose.return_value = trending_choice
|
|
|
|
# Mock successful API call
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
mock_context.media_api.search_media.return_value = mock_search_result
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should transition to RESULTS state
|
|
assert isinstance(result, State)
|
|
assert result.menu_name == "RESULTS"
|
|
assert result.media_api.search_results == mock_search_result
|
|
|
|
def test_main_menu_search_selection(self, mock_context, empty_state):
|
|
"""Test selecting search from main menu."""
|
|
search_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Search" in choice)
|
|
mock_context.selector.choose.return_value = search_choice
|
|
mock_context.selector.ask.return_value = "test query"
|
|
|
|
# Mock successful API call
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should transition to RESULTS state
|
|
assert isinstance(result, State)
|
|
assert result.menu_name == "RESULTS"
|
|
assert result.media_api.search_results == mock_search_result
|
|
|
|
def test_main_menu_search_empty_query(self, mock_context, empty_state):
|
|
"""Test search with empty query returns to menu."""
|
|
search_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Search" in choice)
|
|
mock_context.selector.choose.return_value = search_choice
|
|
mock_context.selector.ask.return_value = "" # Empty query
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should return CONTINUE when search query is empty
|
|
assert result == ControlFlow.CONTINUE
|
|
|
|
def test_main_menu_user_list_authenticated(self, mock_context, empty_state):
|
|
"""Test accessing user list when authenticated."""
|
|
watching_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Watching" in choice)
|
|
mock_context.selector.choose.return_value = watching_choice
|
|
|
|
# Ensure user is authenticated
|
|
mock_context.media_api.is_authenticated.return_value = True
|
|
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should transition to RESULTS state
|
|
assert isinstance(result, State)
|
|
assert result.menu_name == "RESULTS"
|
|
|
|
def test_main_menu_user_list_not_authenticated(self, mock_context, empty_state):
|
|
"""Test accessing user list when not authenticated."""
|
|
watching_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Watching" in choice)
|
|
mock_context.selector.choose.return_value = watching_choice
|
|
|
|
# User not authenticated
|
|
mock_context.media_api.is_authenticated.return_value = False
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.check_authentication_required') as mock_auth:
|
|
mock_auth.return_value = False # Authentication check fails
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should return CONTINUE when authentication is required but not provided
|
|
assert result == ControlFlow.CONTINUE
|
|
|
|
def test_main_menu_exit_selection(self, mock_context, empty_state):
|
|
"""Test selecting exit from main menu."""
|
|
exit_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Exit" in choice)
|
|
mock_context.selector.choose.return_value = exit_choice
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
assert result == ControlFlow.EXIT
|
|
|
|
def test_main_menu_config_edit_selection(self, mock_context, empty_state):
|
|
"""Test selecting config edit from main menu."""
|
|
config_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Edit Config" in choice)
|
|
mock_context.selector.choose.return_value = config_choice
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
assert result == ControlFlow.RELOAD_CONFIG
|
|
|
|
def test_main_menu_session_management_selection(self, mock_context, empty_state):
|
|
"""Test selecting session management from main menu."""
|
|
session_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Session Management" in choice)
|
|
mock_context.selector.choose.return_value = session_choice
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
assert isinstance(result, State)
|
|
assert result.menu_name == "SESSION_MANAGEMENT"
|
|
|
|
def test_main_menu_auth_selection(self, mock_context, empty_state):
|
|
"""Test selecting authentication from main menu."""
|
|
auth_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Authentication" in choice)
|
|
mock_context.selector.choose.return_value = auth_choice
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
assert isinstance(result, State)
|
|
assert result.menu_name == "AUTH"
|
|
|
|
def test_main_menu_watch_history_selection(self, mock_context, empty_state):
|
|
"""Test selecting local watch history from main menu."""
|
|
history_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Local Watch History" in choice)
|
|
mock_context.selector.choose.return_value = history_choice
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
assert isinstance(result, State)
|
|
assert result.menu_name == "WATCH_HISTORY"
|
|
|
|
def test_main_menu_api_failure(self, mock_context, empty_state):
|
|
"""Test handling API failures in main menu."""
|
|
trending_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Trending" in choice)
|
|
mock_context.selector.choose.return_value = trending_choice
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (False, None) # API failure
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should return CONTINUE on API failure
|
|
assert result == ControlFlow.CONTINUE
|
|
|
|
def test_main_menu_random_selection(self, mock_context, empty_state):
|
|
"""Test selecting random anime from main menu."""
|
|
random_choice = next(choice for choice in self._get_menu_choices(mock_context)
|
|
if "Random" in choice)
|
|
mock_context.selector.choose.return_value = random_choice
|
|
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
result = main(mock_context, empty_state)
|
|
|
|
# Should transition to RESULTS state
|
|
assert isinstance(result, State)
|
|
assert result.menu_name == "RESULTS"
|
|
assert result.media_api.search_results == mock_search_result
|
|
|
|
def test_main_menu_icons_enabled(self, mock_context, empty_state):
|
|
"""Test main menu with icons enabled."""
|
|
mock_context.config.general.icons = True
|
|
|
|
# Just ensure menu doesn't crash with icons enabled
|
|
mock_context.selector.choose.return_value = None
|
|
|
|
result = main(mock_context, empty_state)
|
|
assert result == ControlFlow.EXIT
|
|
|
|
def test_main_menu_icons_disabled(self, mock_context, empty_state):
|
|
"""Test main menu with icons disabled."""
|
|
mock_context.config.general.icons = False
|
|
|
|
# Just ensure menu doesn't crash with icons disabled
|
|
mock_context.selector.choose.return_value = None
|
|
|
|
result = main(mock_context, empty_state)
|
|
assert result == ControlFlow.EXIT
|
|
|
|
def _get_menu_choices(self, mock_context):
|
|
"""Helper to get the menu choices from a mock call."""
|
|
# Temporarily call the menu to get choices
|
|
mock_context.selector.choose.return_value = None
|
|
main(mock_context, State(menu_name="TEST"))
|
|
|
|
# Extract choices from the call
|
|
call_args = mock_context.selector.choose.call_args
|
|
return call_args[1]['choices']
|
|
|
|
|
|
class TestMainMenuHelperFunctions:
|
|
"""Test the helper functions in main menu."""
|
|
|
|
def test_create_media_list_action_success(self, mock_context):
|
|
"""Test creating a media list action that succeeds."""
|
|
from fastanime.cli.interactive.menus.main import _create_media_list_action
|
|
|
|
action = _create_media_list_action(mock_context, "TRENDING_DESC")
|
|
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
menu_name, result, api_params, user_list_params = action()
|
|
|
|
assert menu_name == "RESULTS"
|
|
assert result == mock_search_result
|
|
assert api_params is not None
|
|
assert user_list_params is None
|
|
|
|
def test_create_media_list_action_failure(self, mock_context):
|
|
"""Test creating a media list action that fails."""
|
|
from fastanime.cli.interactive.menus.main import _create_media_list_action
|
|
|
|
action = _create_media_list_action(mock_context, "TRENDING_DESC")
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (False, None)
|
|
|
|
menu_name, result, api_params, user_list_params = action()
|
|
|
|
assert menu_name == "CONTINUE"
|
|
assert result is None
|
|
assert api_params is None
|
|
assert user_list_params is None
|
|
|
|
def test_create_user_list_action_authenticated(self, mock_context):
|
|
"""Test creating a user list action when authenticated."""
|
|
from fastanime.cli.interactive.menus.main import _create_user_list_action
|
|
|
|
action = _create_user_list_action(mock_context, "CURRENT")
|
|
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.check_authentication_required') as mock_auth:
|
|
mock_auth.return_value = True
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
menu_name, result, api_params, user_list_params = action()
|
|
|
|
assert menu_name == "RESULTS"
|
|
assert result == mock_search_result
|
|
assert api_params is None
|
|
assert user_list_params is not None
|
|
|
|
def test_create_user_list_action_not_authenticated(self, mock_context):
|
|
"""Test creating a user list action when not authenticated."""
|
|
from fastanime.cli.interactive.menus.main import _create_user_list_action
|
|
|
|
action = _create_user_list_action(mock_context, "CURRENT")
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.check_authentication_required') as mock_auth:
|
|
mock_auth.return_value = False
|
|
|
|
menu_name, result, api_params, user_list_params = action()
|
|
|
|
assert menu_name == "CONTINUE"
|
|
assert result is None
|
|
assert api_params is None
|
|
assert user_list_params is None
|
|
|
|
def test_create_search_media_list_with_query(self, mock_context):
|
|
"""Test creating a search media list action with a query."""
|
|
from fastanime.cli.interactive.menus.main import _create_search_media_list
|
|
|
|
action = _create_search_media_list(mock_context)
|
|
|
|
mock_context.selector.ask.return_value = "test query"
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
menu_name, result, api_params, user_list_params = action()
|
|
|
|
assert menu_name == "RESULTS"
|
|
assert result == mock_search_result
|
|
assert api_params is not None
|
|
assert user_list_params is None
|
|
|
|
def test_create_search_media_list_no_query(self, mock_context):
|
|
"""Test creating a search media list action without a query."""
|
|
from fastanime.cli.interactive.menus.main import _create_search_media_list
|
|
|
|
action = _create_search_media_list(mock_context)
|
|
|
|
mock_context.selector.ask.return_value = "" # Empty query
|
|
|
|
menu_name, result, api_params, user_list_params = action()
|
|
|
|
assert menu_name == "CONTINUE"
|
|
assert result is None
|
|
assert api_params is None
|
|
assert user_list_params is None
|
|
|
|
def test_create_random_media_list(self, mock_context):
|
|
"""Test creating a random media list action."""
|
|
from fastanime.cli.interactive.menus.main import _create_random_media_list
|
|
|
|
action = _create_random_media_list(mock_context)
|
|
|
|
mock_search_result = MediaSearchResult(media=[], page_info=Mock())
|
|
|
|
with patch('fastanime.cli.interactive.menus.main.execute_with_feedback') as mock_execute:
|
|
mock_execute.return_value = (True, mock_search_result)
|
|
|
|
menu_name, result, api_params, user_list_params = action()
|
|
|
|
assert menu_name == "RESULTS"
|
|
assert result == mock_search_result
|
|
assert api_params is not None
|
|
assert user_list_params is None
|
|
# Check that random IDs were used
|
|
assert api_params.id_in is not None
|
|
assert len(api_params.id_in) == 50
|