109 lines
4 KiB
Python
109 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",
|
|
"LEASE_LOCK": str(root / "lease.lock"),
|
|
"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()
|