from copy import deepcopy
from unittest.mock import MagicMock, patch
from lib.api.trakt.trakt_cache import default_activities
from lib.services.trakt_sync import TraktSyncService
def test_get_changed_buckets_detects_expected_activity_groups():
service = TraktSyncService(api=MagicMock(), monitor=MagicMock())
previous = default_activities()
latest = deepcopy(previous)
latest["movies"]["watched_at"] = "2026-03-13T18:00:00.000Z"
latest["episodes"]["paused_at"] = "2026-03-13T18:01:00.000Z"
latest["lists"]["updated_at"] = "2026-03-13T18:02:00.000Z"
changes = service._get_changed_buckets(previous, latest)
assert "watched_movies" in changes
assert "progress_episodes" in changes
assert "lists" in changes
def test_sync_activities_applies_changed_buckets():
api = MagicMock()
api.sync.get_last_activities.return_value = {"movies": {"watched_at": "new"}}
service = TraktSyncService(api=api, monitor=MagicMock())
with patch(
"lib.services.trakt_sync.reset_activity",
return_value={"movies": {"watched_at": "old"}},
), patch.object(
service, "_get_changed_buckets", return_value={"watched_movies", "lists"}
) as changed_buckets, patch.object(service, "_apply_activity_changes") as apply_changes:
result = service.sync_activities()
changed_buckets.assert_called_once()
apply_changes.assert_called_once_with({"watched_movies", "lists"})
assert result == {"watched_movies", "lists"}
def test_sync_activities_logs_changed_buckets():
api = MagicMock()
api.sync.get_last_activities.return_value = {"movies": {"watched_at": "new"}}
service = TraktSyncService(api=api, monitor=MagicMock())
with patch(
"lib.services.trakt_sync.reset_activity",
return_value={"movies": {"watched_at": "old"}},
), patch.object(
service, "_get_changed_buckets", return_value={"watched_movies", "lists"}
), patch.object(service, "_apply_activity_changes"), patch(
"lib.services.trakt_sync.kodilog"
) as kodilog:
service.sync_activities()
assert any(
"changed buckets = lists, watched_movies" in str(call.args[0])
for call in kodilog.call_args_list
)
def test_invalidate_cached_buckets_targets_expected_caches():
service = TraktSyncService(api=MagicMock(), monitor=MagicMock())
with patch("lib.services.trakt_sync.lists_cache.delete_prefix") as delete_prefix, patch(
"lib.services.trakt_sync.clear_trakt_favorites"
) as clear_favorites, patch(
"lib.services.trakt_sync.clear_trakt_list_data"
) as clear_list_data, patch(
"lib.services.trakt_sync.clear_trakt_list_contents_data"
) as clear_list_contents:
service.invalidate_cached_buckets(
{"collection_movies", "favorites_movies", "recommendations_shows", "lists"}
)
delete_prefix.assert_any_call("trakt_movies_collection_")
delete_prefix.assert_any_call("trakt_recommendations_shows")
clear_favorites.assert_called_once_with()
clear_list_data.assert_any_call("my_lists")
clear_list_data.assert_any_call("liked_lists")
clear_list_contents.assert_any_call("my_lists")
clear_list_contents.assert_any_call("liked_lists")
def test_sync_watched_movies_replaces_cache_data():
api = MagicMock()
api.movies.get_watched_movies.return_value = [
{
"movie": {"ids": {"tmdb": 10}, "title": "Movie"},
"last_watched_at": "2026-03-13T18:03:00.000Z",
}
]
service = TraktSyncService(api=api, monitor=MagicMock())
with patch("lib.services.trakt_sync.trakt_watched_cache.set_bulk_movie_watched") as set_bulk:
service.sync_watched_movies()
set_bulk.assert_called_once_with(
[("movie", "10", None, None, "2026-03-13T18:03:00.000Z", "Movie")]
)