torrent-audit: make more robust

This commit is contained in:
2026-05-04 02:27:52 -04:00
parent ce42ccdcc0
commit 09175cd0dc
2 changed files with 128 additions and 21 deletions

View File

@@ -53,6 +53,10 @@ let
SINGLE8_NEW = "A" * 38 + "0D" # movieId=8, newer import keeper (not in qBit)
QUEUED_MOV = "A" * 38 + "0E" # in Radarr queue, not in history
INPROGRESS_MOV = "A" * 38 + "0F" # movieId=10, older import, currently re-downloading
KEEPER_GONE_OLD = "A" * 38 + "11" # movieId=11, older import, keeper incomplete in qBit
KEEPER_GONE_NEW = "A" * 38 + "12" # movieId=11, newer import (keeper), incomplete in qBit
INPROGRESS_MOV_NEW = "A" * 38 + "10" # movieId=10, newer import (not in qBit)
# TV
UNMANAGED_TV = "B" * 38 + "01"
@@ -65,7 +69,9 @@ let
REMOVED_TV_NEW = "B" * 38 + "08" # episodeId=400, newer import (not in qBit)
INPROGRESS_TV = "B" * 38 + "09" # episodeId=500, older import, currently re-downloading
INPROGRESS_TV_NEW = "B" * 38 + "0A" # episodeId=500, newer import (not in qBit)
INPROGRESS_MOV_NEW = "A" * 38 + "10" # movieId=10, newer import (not in qBit)
KEEPER_GONE_TV = "B" * 38 + "0B" # episodeId=600, older import, keeper incomplete in qBit
KEEPER_GONE_TV_NEW = "B" * 38 + "0C" # episodeId=600, newer import (active), incomplete in qBit
def make_torrent(h, name, size, added_on, state="uploading", progress=1.0):
return {
@@ -92,6 +98,10 @@ let
# In-progress re-download: hash matches an old import, but data is
# not yet on disk. Must NOT be flagged as abandoned (regression).
make_torrent(INPROGRESS_MOV, "InProgress.Movie.2024", 8_000_000_000, 1704067209, state="downloading", progress=0.05),
# Keeper-incomplete regression: old torrent is complete, keeper is
# incomplete in qBittorrent. Must be REVIEW, not SAFE.
make_torrent(KEEPER_GONE_OLD, "KeeperGone.Movie.2024", 2_000_000_000, 1704067210),
make_torrent(KEEPER_GONE_NEW, "KeeperGone.Movie.2024.Upgr", 3_000_000_000, 1704067211, state="downloading", progress=0.10),
],
"tvshows": [
make_torrent(UNMANAGED_TV, "Unmanaged.Show.S01E01", 1_000_000_000, 1704067200),
@@ -101,6 +111,9 @@ let
make_torrent(SEASON_PACK, "Season.Pack.S02", 5_000_000_000, 1704067204),
make_torrent(REMOVED_TV, "Removed.Show.S01E01", 900_000_000, 1704067205),
make_torrent(INPROGRESS_TV, "InProgress.Show.S01E01", 1_500_000_000, 1704067209, state="downloading", progress=0.05),
# Keeper-incomplete regression for TV
make_torrent(KEEPER_GONE_TV, "KeeperGone.Show.S01E01", 500_000_000, 1704067210),
make_torrent(KEEPER_GONE_TV_NEW, "KeeperGone.Show.S01E01.Upgr", 800_000_000, 1704067211, state="downloading", progress=0.10),
],
}
@@ -127,6 +140,10 @@ let
# In-progress re-download regression case for movies
{"movieId": 10, "downloadId": INPROGRESS_MOV, "eventType": "downloadFolderImported", "date": "2024-01-01T00:00:00Z"},
{"movieId": 10, "downloadId": INPROGRESS_MOV_NEW,"eventType": "downloadFolderImported", "date": "2024-06-01T00:00:00Z"},
# Keeper-incomplete regression: old import + newer import (keeper) that
# is incomplete in qBittorrent. The old torrent must be REVIEW.
{"movieId": 11, "downloadId": KEEPER_GONE_OLD, "eventType": "downloadFolderImported", "date": "2024-01-01T00:00:00Z"},
{"movieId": 11, "downloadId": KEEPER_GONE_NEW, "eventType": "downloadFolderImported", "date": "2024-06-01T00:00:00Z"},
]
RADARR_MOVIES = [
@@ -139,6 +156,7 @@ let
{"id": 7, "hasFile": True, "movieFile": {"size": 4_000_000_000, "quality": {"quality": {"name": "Bluray-1080p"}}}},
{"id": 8, "hasFile": True, "movieFile": {"size": 5_000_000_000, "quality": {"quality": {"name": "Remux-1080p"}}}},
{"id": 10, "hasFile": True, "movieFile": {"size": 8_000_000_000, "quality": {"quality": {"name": "Remux-2160p"}}}},
{"id": 11, "hasFile": True, "movieFile": {"size": 3_000_000_000, "quality": {"quality": {"name": "Remux-1080p"}}}},
]
# Sonarr mock data
@@ -164,6 +182,9 @@ let
# In-progress re-download regression case for TV
{"episodeId": 500, "seriesId": 1, "downloadId": INPROGRESS_TV, "eventType": "downloadFolderImported", "date": "2024-01-01T00:00:00Z"},
{"episodeId": 500, "seriesId": 1, "downloadId": INPROGRESS_TV_NEW,"eventType": "downloadFolderImported", "date": "2024-06-01T00:00:00Z"},
# Keeper-incomplete regression for TV
{"episodeId": 600, "seriesId": 1, "downloadId": KEEPER_GONE_TV, "eventType": "downloadFolderImported", "date": "2024-01-01T00:00:00Z"},
{"episodeId": 600, "seriesId": 1, "downloadId": KEEPER_GONE_TV_NEW,"eventType": "downloadFolderImported", "date": "2024-06-01T00:00:00Z"},
]
SONARR_HISTORY_ALL = SONARR_HISTORY_PAGE1 + SONARR_HISTORY_PAGE2
@@ -335,14 +356,14 @@ pkgs.testers.runNixOSTest {
with subtest("Detects unmanaged movie torrent"):
assert "Unmanaged.Movie.2024" in unmanaged_section, \
"Should detect unmanaged movie"
assert "1 unmanaged / 10 total" in unmanaged_section, \
"Should show 1 unmanaged movie out of 10"
assert "1 unmanaged / 12 total" in unmanaged_section, \
"Should show 1 unmanaged movie out of 12"
with subtest("Detects unmanaged TV torrent"):
assert "Unmanaged.Show.S01E01" in unmanaged_section, \
"Should detect unmanaged TV show"
assert "1 unmanaged / 7 total" in unmanaged_section, \
"Should show 1 unmanaged TV show out of 7"
assert "1 unmanaged / 9 total" in unmanaged_section, \
"Should show 1 unmanaged TV show out of 9"
with subtest("Empty category shows zero counts"):
assert "0 unmanaged / 0 total" in unmanaged_section, \
@@ -434,15 +455,42 @@ pkgs.testers.runNixOSTest {
break
with subtest("Correct abandoned counts per category"):
assert "movies (3 abandoned)" in abandoned_section, \
"Should show 3 abandoned movies"
assert "tvshows (2 abandoned)" in abandoned_section, \
"Should show 2 abandoned TV shows"
assert "movies (4 abandoned)" in abandoned_section, \
"Should show 4 abandoned movies"
assert "tvshows (3 abandoned)" in abandoned_section, \
"Should show 3 abandoned TV shows"
with subtest("Correct summary totals"):
assert "ABANDONED: 5 total (2 safe to delete)" in output, \
"Summary should show 5 total abandoned, 2 safe to delete"
assert "ABANDONED: 7 total (2 safe to delete)" in output, \
"Summary should show 7 total abandoned, 2 safe to delete"
assert "SAFE TO RECLAIM: 3.4 GiB" in output, \
"Should report 3.4 GiB reclaimable (2.8 GiB movie + 0.7 GiB TV)"
with subtest("Keeper-incomplete movie triggers REVIEW"):
assert "KeeperGone.Movie.2024" in abandoned_section, \
"Should detect abandoned torrent with incomplete keeper"
assert_note_near(abandoned_section, "KeeperGone.Movie.2024", "keeper torrent incomplete")
for line in abandoned_section.splitlines():
if "KeeperGone.Movie.2024" in line:
assert "REVIEW" in line, f"Keeper-incomplete movie should be REVIEW, got: {line}"
break
with subtest("Keeper-incomplete TV triggers REVIEW"):
assert "KeeperGone.Show.S01E01" in abandoned_section, \
"Should detect abandoned TV torrent with incomplete keeper"
assert_note_near(abandoned_section, "KeeperGone.Show.S01E01", "replacement torrent incomplete")
for line in abandoned_section.splitlines():
if "KeeperGone.Show.S01E01" in line:
assert "REVIEW" in line, f"Keeper-incomplete TV should be REVIEW, got: {line}"
break
with subtest("Keeper-incomplete does not affect existing SAFE verdicts"):
for line in abandoned_section.splitlines():
if "Old.Movie.Quality.2024" in line:
assert "SAFE" in line, f"Old movie should still be SAFE, got: {line}"
break
for line in abandoned_section.splitlines():
if "Old.Show.S01E01" in line:
assert "SAFE" in line, f"Old TV should still be SAFE, got: {line}"
break
'';
}