84 lines
4.0 KiB
Python
Executable File
84 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Actual CLI contract checks; --live adds bounded public embed/root probes only."""
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
def require(condition, message):
|
|
if not condition:
|
|
raise AssertionError(message)
|
|
|
|
|
|
def snapshot(*roots):
|
|
return {
|
|
str(path): path.read_bytes() if path.is_file() else None
|
|
for root in roots
|
|
for path in root.rglob("*")
|
|
}
|
|
|
|
|
|
def main():
|
|
executable = Path(sys.argv[1]).resolve(strict=True)
|
|
live = "--live" in sys.argv[2:]
|
|
watch = "https://zokoanime.video/stream/ani/1/1/sub"
|
|
secret = "private-fixture-token"
|
|
with tempfile.TemporaryDirectory(prefix="terebi-source-check-") as temporary:
|
|
root = Path(temporary)
|
|
home, config = root / "home", root / "config"
|
|
home.mkdir()
|
|
(config / "terebi").mkdir(parents=True)
|
|
# A launch would parse this malformed configuration or reject this
|
|
# executable outside its bundle. A source check must do neither.
|
|
(config / "terebi" / "config.toml").write_text("not valid TOML = [\n")
|
|
cli = root / "terebi"
|
|
shutil.copy2(executable, cli)
|
|
environment = dict(os.environ, HOME=str(home), CFFIXED_USER_HOME=str(home), XDG_CONFIG_HOME=str(config))
|
|
before = snapshot(home, config)
|
|
|
|
def invoke(arguments):
|
|
try:
|
|
result = subprocess.run([str(cli), *arguments], env=environment, capture_output=True, timeout=20)
|
|
except subprocess.TimeoutExpired:
|
|
raise AssertionError("source-check CLI exceeded total deadline") from None
|
|
require(len(result.stdout) <= 128 and len(result.stderr) <= 1024, "bounded CLI output")
|
|
require(secret.encode() not in result.stdout + result.stderr, "CLI output redacts private input")
|
|
require(b"https://" not in result.stdout, "no source URLs in JSON")
|
|
require(snapshot(home, config) == before, "source checking must not write configuration or resume/progress state")
|
|
return result
|
|
|
|
for arguments in [
|
|
["--check-source"], ["--check-source", watch + "?token=" + secret],
|
|
["--check-source", "https://" + secret + "@zokoanime.video/stream/ani/1/1/sub"],
|
|
["--new-instance", "--check-source", watch], ["--check-source", watch, "--no-config"],
|
|
["--check-source", watch, "--progress-file", str(root / secret)],
|
|
["--check-source", "https://127.0.0.1/" + secret],
|
|
]:
|
|
result = invoke(arguments)
|
|
require(result.returncode != 0 and not result.stdout, "invalid syntax/unsupported URL must fail without JSON or launch")
|
|
require(not (root / secret).exists(), "invalid source check must not create progress file")
|
|
if live:
|
|
for media_id, episode, expected in [
|
|
(1, 1, "available"), (1, 27, "unavailable"),
|
|
(97940, 1, "available"), (97940, 170, "available"),
|
|
(195604, 1, "available"),
|
|
]:
|
|
result = invoke(["--check-source", f"https://zokoanime.video/stream/ani/{media_id}/{episode}/sub"])
|
|
require(result.returncode == 0, "completed provider checks exit zero")
|
|
require(result.stdout.endswith(b"\n") and result.stdout.count(b"\n") == 1, "exactly one JSON line")
|
|
row = json.loads(result.stdout)
|
|
require(row == {"version": 1, "provider": "zokoanime", "track": "sub", "status": expected},
|
|
f"live AniList {media_id} episode {episode} expected {expected}; result is redacted")
|
|
require(not result.stderr, "successful source checks have no error logs")
|
|
print("PASS: actual CLI resolves exact MAL mappings for Cowboy Bebop and both Black Clover entries; explicit missing remains unavailable; no state writes")
|
|
else:
|
|
print("PASS: actual CLI rejects hostile URLs/flags privately, without configuration reads, state writes or bundle launch")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|