Files
YouTubeDL/tests/component-update.py
DepressedCat 5f2aad00f4
Check extension and helper / check (push) Canceled after 0s
Publish YouTubeDL 1.4.5 and website on Gitea
2026-10-10 23:53:17 +03:00

85 lines
4.7 KiB
Python

"""Real staged publication, stale-update rejection and live PyPI wheels, no registry changes."""
import hashlib
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch
PROJECT = Path(__file__).resolve().parent.parent
def inside(packages=False):
from host.bundle import ROOT, installation_root
from host.updates import update
from host.media import capabilities
application = installation_root()
marker = application / "installation.json"
before = json.loads(marker.read_text(encoding="utf-8-sig"))["current"]
if packages:
import io
import time
from host.main import NativeHost
host = NativeHost(io.BytesIO())
try:
response = host.start_update([{"id": "packages", "version": "latest-compatible"}])
deadline = time.monotonic() + 500
while host.jobs[response["jobId"]]["status"] == "running" and time.monotonic() < deadline: time.sleep(0.1)
job = host.jobs[response["jobId"]]
assert job["status"] == "complete", job
result = job["result"]
finally: host.close()
after = Path(json.loads(marker.read_text(encoding="utf-8-sig"))["current"])
versions = json.loads((after / "bundle.json").read_text(encoding="utf-8"))["pythonPackages"]
assert result["restartRequired"] and "yt-dlp" in {key.lower() for key in versions}
print(json.dumps({"livePyPI": "passed", "nativeWorkerUpdate": "passed", "packages": versions}))
return
archive = PROJECT / ".build-cache/node.zip"
data = archive.read_bytes()
candidate = {"id": "node", "version": "v24.21.0", "url": "https://nodejs.org/dist/v24.21.0/node-v24.21.0-win-x64.zip", "sha256": hashlib.sha256(data).hexdigest()}
class Response:
headers = {"Content-Length": str(len(data))}
def __enter__(self): return self
def __exit__(self, *args): return False
def iter_content(self, count):
for offset in range(0, len(data), count): yield data[offset:offset + count]
dll = ROOT / "runtime/python/python313.dll"
with dll.open("rb") as opened, patch("host.updates.get", return_value=Response()):
result = update(lambda *args: None, lambda _: None, [candidate])
after = json.loads(marker.read_text(encoding="utf-8-sig"))["current"]
assert after != before and result["restartRequired"] and dll.is_file() and opened.read(2) == b"MZ"
try: update(lambda *args: None, lambda _: None, [candidate])
except ValueError as error: assert "другой установкой" in str(error)
else: raise AssertionError("A stale helper must not replace the newer runtime")
try: update(lambda *args: None, lambda _: None, [{**candidate, "sha256": "0" * 64}])
except ValueError as error: assert "Контрольная сумма" in str(error)
else: raise AssertionError("Modified downloads must be rejected")
assert json.loads(marker.read_text(encoding="utf-8-sig"))["current"] == after
assert not list((application / "releases").glob(".ytdl-update-*")) == [], "The published release must remain"
print(json.dumps({"stagedActivation": "passed", "openOldDLL": "preserved", "staleUpdate": "rejected", "badHash": "rejected"}))
def outer():
artifacts = PROJECT / "test-artifacts"; artifacts.mkdir(exist_ok=True)
with tempfile.TemporaryDirectory(prefix="update-test-", dir=artifacts) as temporary:
root = Path(temporary) / "Helper"
process = subprocess.run([str(PROJECT / "dist/YouTubeDL-Helper-Setup.exe"), "--silent", "--no-register", "--target-root", str(root)], capture_output=True, text=True, encoding="utf-8", timeout=180)
assert process.returncode == 0, process.stdout + process.stderr
records = []
for action in ("--inside", "--inside-packages"):
marker = json.loads((root / "installation.json").read_text(encoding="utf-8-sig"))
python = Path(marker["current"]) / "runtime/python/python.exe"
result = subprocess.run([str(python), "-I", "-X", "utf8", "-B", str(Path(__file__).resolve()), action], capture_output=True, text=True, encoding="utf-8", timeout=600)
assert result.returncode == 0, result.stdout[-1000:] + result.stderr[-4000:]
records.append(json.loads(result.stdout.splitlines()[-1]))
(artifacts / "component-update.json").write_text(json.dumps(records, indent=2), encoding="utf-8")
print("Component updates passed: live PyPI downloads, verified stage activation, retained old DLL, bad hash rejection and stale-update rejection.")
if __name__ == "__main__":
if "--inside" in sys.argv: inside()
elif "--inside-packages" in sys.argv: inside(True)
else: outer()