Harden queue identity and Jellyfin move checks

This commit is contained in:
Dymas
2026-05-25 23:51:16 +02:00
parent 8fb32a59ba
commit 1ceb773f99
8 changed files with 124 additions and 17 deletions
+63 -1
View File
@@ -1279,6 +1279,37 @@ class WatchlistCompletionTests(unittest.TestCase):
self.assertEqual(prepared["show_id"], "show-42")
self.assertEqual(prepared["title"], "Queue Show Season 2")
def test_prepare_download_job_resolves_unique_match_without_result_index(self):
job = {"query": "Queue Show", "mode": "sub", "title": "Queue Show", "show_id": ""}
with mock.patch.object(
APP,
"search_anime",
return_value=[
{"id": "show-42", "title": "Queue Show"},
{"id": "show-99", "title": "Different Show"},
],
):
prepared = APP.prepare_download_job(dict(job))
self.assertEqual(prepared["show_id"], "show-42")
self.assertEqual(prepared["title"], "Queue Show")
def test_prepare_download_job_skips_ambiguous_match_without_result_index(self):
job = {"query": "Queue Show", "mode": "sub", "title": "Queue Show", "show_id": ""}
with mock.patch.object(
APP,
"search_anime",
return_value=[
{"id": "show-42", "title": "Queue Show"},
{"id": "show-99", "title": "Queue Show (2024)"},
],
):
prepared = APP.prepare_download_job(dict(job))
self.assertEqual(prepared["show_id"], "")
def test_command_for_job_omits_result_switch_when_result_index_missing(self):
job = APP.app_support.build_job(
{
@@ -1448,11 +1479,13 @@ class JellyfinSyncTests(unittest.TestCase):
self.assertEqual(send_webhook.call_args.args[2]["reason"], "target_missing")
def test_trigger_jellyfin_sync_for_item_skips_already_moved_notification(self):
self.seed_watchlist_item()
self.seed_watchlist_item(expected_count=1, dub_count=1, downloaded_dub_episodes_json=APP.encode_episode_values(["1"]))
item = APP.WATCHLIST.get("show-jelly-1")
with tempfile.TemporaryDirectory() as temp_root:
jellyfin_tv = Path(temp_root) / "jellyfin-tv" / "Jelly Show"
jellyfin_tv.mkdir(parents=True, exist_ok=True)
(jellyfin_tv / "Season 01").mkdir(parents=True, exist_ok=True)
(jellyfin_tv / "Season 01" / "Jelly Show - S01E01.mp4").write_bytes(b"episode-data")
with mock.patch.object(APP, "send_discord_webhook_event") as send_webhook:
result = APP.trigger_jellyfin_sync_for_item(
item,
@@ -1471,6 +1504,31 @@ class JellyfinSyncTests(unittest.TestCase):
self.assertEqual(result["reason"], "already_moved")
send_webhook.assert_not_called()
def test_trigger_jellyfin_sync_for_item_flags_incomplete_target_when_source_missing(self):
self.seed_watchlist_item(expected_count=12, dub_count=12)
item = APP.WATCHLIST.get("show-jelly-1")
with tempfile.TemporaryDirectory() as temp_root:
jellyfin_tv = Path(temp_root) / "jellyfin-tv" / "Jelly Show"
jellyfin_tv.mkdir(parents=True, exist_ok=True)
with mock.patch.object(APP, "send_discord_webhook_event") as send_webhook:
result = APP.trigger_jellyfin_sync_for_item(
item,
automatic=False,
config={
"download_dir": str(Path(temp_root) / "downloads"),
"jellyfin_sync_enabled": True,
"jellyfin_tv_dir": str(Path(temp_root) / "jellyfin-tv"),
"jellyfin_movie_dir": "",
"discord_webhook_url": "https://discord.example/webhook",
"discord_webhook_events": ["jellyfin_sync_failed"],
},
)
self.assertFalse(result["moved"])
self.assertEqual(result["reason"], "target_incomplete")
send_webhook.assert_called_once()
self.assertEqual(send_webhook.call_args.args[1], "jellyfin_sync_failed")
def test_trigger_jellyfin_sync_for_item_skips_incomplete_series(self):
self.seed_watchlist_item(
expected_count=12,
@@ -2568,6 +2626,10 @@ class TemplateHelperTests(unittest.TestCase):
def test_search_page_sends_watchlist_auto_download_series(self):
self.assertIn('auto_download_series: $("seasonInput").value || "1"', APP.INDEX_HTML)
def test_search_page_queues_selected_title_without_result_index(self):
self.assertIn('query: state.selected.title,', APP.INDEX_HTML)
self.assertNotIn('index: state.selected.index,', APP.INDEX_HTML)
def test_pages_share_serial_polling_helper(self):
self.assertIn("function startSerialPoll(callback, intervalMs)", APP.QUEUE_HTML)
self.assertIn("function startSerialPoll(callback, intervalMs)", APP.WATCHLIST_HTML)