Pytest suite with a tiny_client fixture, an ephemeral Ed25519 keypair fixture, and a threaded HTTPServer helper. Exercises cli dispatch, inspect (including excluded-path handling), build-manifest and sign against the real m2dev-client scripts, diff-remote via a local server, and the full release publish composite against a local rsync target.
84 lines
2.7 KiB
Python
84 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent / "fixtures"))
|
|
from http_server import serve_dir # noqa: E402
|
|
|
|
from metin_release.commands import publish # noqa: E402
|
|
from metin_release.workspace import Context # noqa: E402
|
|
|
|
|
|
MAKE_MANIFEST = Path("/home/jann/metin/repos/m2dev-client/scripts/make-manifest.py")
|
|
SIGN_MANIFEST = Path("/home/jann/metin/repos/m2dev-client/scripts/sign-manifest.py")
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not (MAKE_MANIFEST.is_file() and SIGN_MANIFEST.is_file()),
|
|
reason="reference scripts not available",
|
|
)
|
|
def test_publish_end_to_end_local(tiny_client: Path, tmp_path: Path, test_keypair):
|
|
key_path, pub_hex = test_keypair
|
|
out_dir = tmp_path / "release"
|
|
rsync_target_dir = tmp_path / "remote"
|
|
rsync_target_dir.mkdir()
|
|
rsync_target = str(rsync_target_dir) + "/"
|
|
|
|
ctx = Context(make_manifest_script=MAKE_MANIFEST, sign_manifest_script=SIGN_MANIFEST)
|
|
|
|
# We need verify-public to hit the rsync target as an HTTP server.
|
|
# Start HTTP server pointing at rsync target BEFORE publish runs verify-public.
|
|
with serve_dir(rsync_target_dir) as base_url:
|
|
args = argparse.Namespace(
|
|
source=tiny_client,
|
|
version="2026.04.14-1",
|
|
out=out_dir,
|
|
key=key_path,
|
|
rsync_target=rsync_target,
|
|
base_url=base_url,
|
|
public_key=pub_hex,
|
|
previous=None,
|
|
notes=None,
|
|
launcher="Metin2Launcher.exe",
|
|
created_at="2026-04-14T00:00:00Z",
|
|
sample_blobs=2,
|
|
yes=True,
|
|
force=False,
|
|
dry_run_upload=False,
|
|
)
|
|
result = publish.run(ctx, args)
|
|
|
|
assert result.ok is True
|
|
assert result.status == "published"
|
|
stages = result.data["stages"]
|
|
names = [s["name"] for s in stages]
|
|
assert names == [
|
|
"build-manifest",
|
|
"sign",
|
|
"stage-blobs",
|
|
"upload-blobs",
|
|
"promote",
|
|
"verify-public",
|
|
]
|
|
for s in stages:
|
|
assert "error" not in s
|
|
|
|
# Verify that the rsync target now contains manifest + blobs.
|
|
assert (rsync_target_dir / "manifest.json").is_file()
|
|
assert (rsync_target_dir / "manifest.json.sig").is_file()
|
|
assert (rsync_target_dir / "files").is_dir()
|
|
blobs = list((rsync_target_dir / "files").rglob("*"))
|
|
blob_files = [b for b in blobs if b.is_file()]
|
|
assert len(blob_files) >= 3
|
|
|
|
# Verify sample-blobs count reflected
|
|
verify_stats = result.data["stats"]["verify"]
|
|
assert verify_stats["signature_valid"] is True
|
|
assert verify_stats["sampled_blob_count"] == 2
|
|
assert verify_stats["sampled_blob_failures"] == []
|