sabnzbd/usenet-scripts/test_movetdarr.py

108 lines
4 KiB
Python

import json
import os
import subprocess
import tempfile
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
SCRIPT = Path(__file__).with_name("movetdarr.sh")
actions = []
status_polls = 0
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
global status_polls
data = json.loads(self.rfile.read(int(self.headers.get("Content-Length", "0"))))
actions.append(data)
action = data.get("action")
if action == "start":
result = {"ok": True, "jobId": "test-job-1234", "state": "registered"}
elif action == "moved":
result = {"ok": True, "jobId": "test-job-1234", "state": "processing"}
elif action == "status":
status_polls += 1
result = {"ok": True, "jobId": "test-job-1234", "state": "success" if status_polls >= 2 else "processing"}
else:
result = {"ok": True, "jobId": "test-job-1234", "state": "failed"}
body = json.dumps(result).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *_args):
pass
def run_case(root, category="serien4k", status="0", name="Show.S01E01", target_video=False):
src_root = root / "source"
dst_root = root / "dest"
source = src_root / category / name
source.mkdir(parents=True)
(source / "episode.mkv").write_bytes(b"video")
target = dst_root / category / name
if target_video:
target.mkdir(parents=True)
(target / "existing.mkv").write_bytes(b"existing")
env = os.environ | {
"SOURCE_ROOT": str(src_root),
"DEST_ROOT": str(dst_root),
"LOGFILE": str(root / "postprocess.log"),
"HANDOFF_URL": f"http://127.0.0.1:{server.server_port}/media/handoff",
"POLL_SECONDS": "0",
"MAX_POLLS": "3",
}
result = subprocess.run(
["bash", str(SCRIPT), str(source), name + ".nzb", name, "", category, "", status],
env=env,
text=True,
capture_output=True,
timeout=20,
)
return result, source, target
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
result, source, target = run_case(Path(td))
assert result.returncode == 0, (result.stdout, result.stderr)
assert not source.exists() and (target / "episode.mkv").exists()
assert [a["action"] for a in actions] == ["start", "moved", "status", "status"]
assert actions[0]["expectedFiles"] == "1"
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
result, source, _ = run_case(Path(td), category="other")
assert result.returncode == 1 and source.exists() and actions == []
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
result, source, target = run_case(Path(td), target_video=True)
assert result.returncode == 2 and source.exists() and (target / "existing.mkv").exists()
assert [a["action"] for a in actions] == ["start", "fail"]
with tempfile.TemporaryDirectory() as td:
actions.clear()
status_polls = 0
root = Path(td)
stale = root / "dest" / "video" / "Movie.2026"
stale.mkdir(parents=True)
(stale / "leftover.nfo").write_text("stale")
result, source, target = run_case(root, category="video", name="Movie.2026")
assert result.returncode == 0 and not source.exists()
assert not (target / "leftover.nfo").exists() and (target / "episode.mkv").exists()
print("PASS: success lease, category fail-closed, no-overwrite, stale-directory cleanup")
finally:
server.shutdown()
server.server_close()