from unittest.mock import MagicMock, patch
from lib.api.trakt.trakt import TraktLists
from lib.api.trakt.trakt_utils import (
add_trakt_favorites_context_menu,
add_trakt_watchlist_context_menu,
)
from lib.clients.trakt.trakt import TraktPresentation
def test_add_to_favorites_posts_expected_payload():
trakt_lists = TraktLists()
with patch.object(trakt_lists, "call_trakt", return_value={}) as call_trakt, patch(
"lib.api.trakt.trakt.clear_trakt_favorites"
) as clear_favorites:
trakt_lists.add_to_favorites("movies", {"tmdb": 11, "imdb": "tt11"})
call_trakt.assert_called_once_with(
"sync/favorites",
data={"movies": [{"ids": {"tmdb": 11, "imdb": "tt11"}}]},
with_auth=True,
pagination=False,
)
clear_favorites.assert_called_once_with()
def test_remove_from_favorites_posts_expected_payload():
trakt_lists = TraktLists()
with patch.object(trakt_lists, "call_trakt", return_value={}) as call_trakt, patch(
"lib.api.trakt.trakt.clear_trakt_favorites"
):
trakt_lists.remove_from_favorites("shows", {"tmdb": 22, "tvdb": 33})
call_trakt.assert_called_once_with(
"sync/favorites/remove",
data={"shows": [{"ids": {"tmdb": 22, "tvdb": 33}}]},
with_auth=True,
pagination=False,
)
def test_watchlist_context_menu_can_hide_add_action():
menu = add_trakt_watchlist_context_menu(
"movies", {"tmdb_id": 1}, include_add=False, include_remove=True
)
labels = [label for label, _cmd in menu]
assert labels == ["Remove from Trakt Watchlist"]
def test_favorites_context_menu_can_hide_remove_action():
menu = add_trakt_favorites_context_menu(
"shows", {"tmdb_id": 1}, include_add=True, include_remove=False
)
labels = [label for label, _cmd in menu]
assert labels == ["Add to Trakt Favorites"]
def test_show_favorites_uses_state_aware_context_flags():
fake_item = MagicMock()
with patch("lib.clients.trakt.trakt.make_list_item", return_value=fake_item), patch(
"lib.clients.trakt.trakt.tmdb_get", return_value={}
), patch("lib.clients.trakt.trakt.set_media_infoTag"), patch(
"lib.clients.trakt.trakt.BaseTraktClient._add_media_directory_item"
) as add_item:
TraktPresentation.show_favorites(
{"title": "Demo", "media_ids": {"tmdb": 1, "imdb": "tt1"}},
"movies",
)
from lib.clients.trakt.trakt import BaseTraktClient
assert add_item.call_args.kwargs["context_flags"] == BaseTraktClient._exclusive_context_flags(
"favorites"
)
def test_exclusive_context_flags_only_enable_requested_remove_action():
from lib.clients.trakt.trakt import BaseTraktClient
flags = BaseTraktClient._exclusive_context_flags("watchlist")
assert flags["watchlist_remove"] is True
assert flags["watchlist_add"] is False
assert flags["favorites_remove"] is False
assert flags["collection_remove"] is False
assert flags["custom_list_remove"] is False
def test_add_to_collection_clears_collection_caches_for_movies():
trakt_lists = TraktLists()
with patch.object(trakt_lists, "call_trakt", return_value={}) as call_trakt, patch(
"lib.api.trakt.trakt.lists_cache.delete_prefix"
) as delete_prefix, patch(
"lib.api.trakt.trakt.clear_trakt_collection_watchlist_data"
) as clear_collection:
trakt_lists.add_to_collection("movies", {"tmdb": 44})
call_trakt.assert_called_once()
delete_prefix.assert_called_once_with("trakt_movies_collection_")
clear_collection.assert_called_once_with("collection", "movies")
def test_remove_from_collection_uses_all_known_ids_for_shows():
trakt_lists = TraktLists()
with patch.object(trakt_lists, "call_trakt", return_value={}) as call_trakt, patch(
"lib.api.trakt.trakt.lists_cache.delete_prefix"
), patch("lib.api.trakt.trakt.clear_trakt_collection_watchlist_data"):
trakt_lists.remove_from_collection("shows", {"tmdb": "44", "tvdb": "55", "imdb": "tt66"})
call_trakt.assert_called_once_with(
"sync/collection/remove",
data={"shows": [{"ids": {"tmdb": 44, "tvdb": 55, "imdb": "tt66"}}]},
with_auth=True,
pagination=False,
)
def test_build_show_collection_payload_uses_show_level_ids():
with patch(
"lib.clients.trakt.trakt.TraktPresentation._resolve_media_ids",
return_value={"tmdb_id": "99", "tvdb_id": "77", "imdb_id": "tt99"},
), patch("lib.clients.trakt.trakt.TraktAPI") as trakt_api, patch(
"lib.clients.trakt.trakt.tmdb_get",
return_value={"name": "Demo Show", "first_air_date": "2024-01-01"},
):
trakt_api.return_value.lists.get_trakt_object_by_tmdb.return_value = {
"title": "Demo Show",
"year": 2024,
"ids": {"trakt": 123, "slug": "demo-show"},
}
from lib.clients.trakt.trakt import TraktClient
payload = TraktClient._build_show_collection_payload({"tmdb_id": "99"})
assert payload == {
"shows": [
{
"title": "Demo Show",
"year": 2024,
"ids": {
"tmdb": 99,
"tvdb": 77,
"imdb": "tt99",
"trakt": 123,
"slug": "demo-show",
},
}
]
}
def test_build_show_collection_episode_payload_includes_aired_episodes_only():
tv_details = {"number_of_seasons": 1}
season_details = {
"episodes": [
{"episode_number": 1, "air_date": "2024-01-01"},
{"episode_number": 2, "air_date": "2999-01-01"},
]
}
with patch("lib.clients.trakt.trakt.tmdb_get", side_effect=[tv_details, season_details]), patch(
"lib.clients.trakt.trakt.TraktPresentation._resolve_media_ids",
return_value={"tmdb_id": "99", "tvdb_id": "77", "imdb_id": "tt99"},
), patch("lib.clients.trakt.trakt.TraktAPI") as trakt_api:
trakt_api.return_value.lists.get_trakt_object_by_tmdb.return_value = {
"title": "Demo Show",
"year": 2024,
"ids": {"trakt": 123, "slug": "demo-show"},
}
from lib.clients.trakt.trakt import TraktClient
payload = TraktClient._build_show_collection_episode_payload({"tmdb_id": "99"})
assert payload == {
"shows": [
{
"title": "Demo Show",
"year": 2024,
"ids": {
"tmdb": 99,
"tvdb": 77,
"imdb": "tt99",
"trakt": 123,
"slug": "demo-show",
},
"seasons": [{"number": 1, "episodes": [{"number": 1}]}],
}
]
}
def test_build_show_collection_season_payload_uses_season_numbers_only():
episode_payload = {
"shows": [
{
"ids": {"tmdb": 99, "tvdb": 77},
"title": "Demo Show",
"year": 2024,
"seasons": [
{"number": 1, "episodes": [{"number": 1}]},
{"number": 2, "episodes": [{"number": 1}]},
],
}
]
}
with patch(
"lib.clients.trakt.trakt.TraktClient._build_show_collection_episode_payload",
return_value=episode_payload,
):
from lib.clients.trakt.trakt import TraktClient
payload = TraktClient._build_show_collection_season_payload({"tmdb_id": "99"})
assert payload == {
"shows": [
{
"ids": {"tmdb": 99, "tvdb": 77},
"title": "Demo Show",
"year": 2024,
"seasons": [{"number": 1}, {"number": 2}],
}
]
}
def test_normalize_trakt_media_type_prefers_show_when_tvdb_present():
from lib.clients.trakt.trakt import TraktClient
result = TraktClient._normalize_trakt_media_type("movies", {"tmdb": 44, "tvdb": 55})
assert result == "shows"
def test_normalize_trakt_media_type_uses_tmdb_details_for_tv_ids():
with patch("lib.clients.trakt.trakt.tmdb_get", side_effect=[{"name": "TV Show"}, None]):
from lib.clients.trakt.trakt import TraktClient
result = TraktClient._normalize_trakt_media_type("movies", {"tmdb": 999})
assert result == "shows"
def test_build_collection_remove_payload_uses_existing_collection_entry():
trakt_lists = TraktLists()
with patch.object(
trakt_lists,
"get_collection_items",
return_value=[
{
"show": {
"title": "Demo Show",
"year": 2024,
"ids": {"tmdb": 99, "tvdb": 77, "imdb": "tt99", "trakt": 123},
},
"seasons": [{"number": 1, "episodes": [{"number": 1}, {"number": 2}]}],
}
],
):
payload = trakt_lists.build_collection_remove_payload("shows", {"tmdb": 99, "tvdb": 77})
assert payload == {
"shows": [
{
"title": "Demo Show",
"year": 2024,
"ids": {"tmdb": 99, "tvdb": 77, "imdb": "tt99", "trakt": 123},
"seasons": [{"number": 1, "episodes": [{"number": 1}, {"number": 2}]}],
}
]
}
def test_result_has_collection_change_for_show_add_when_episodes_added():
from lib.clients.trakt.trakt import TraktClient
result = TraktClient._result_has_collection_change(
{"added": {"movies": 0, "episodes": 3}}, "add"
)
assert result is True