test_q3_release_desktop_e2e.py
python
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
13 hours ago
| 1 | """End-to-end tests for Q3-release desktop installers (§QR.13).""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | import json |
| 6 | from pathlib import Path |
| 7 | |
| 8 | import pytest |
| 9 | |
| 10 | from tests.fixtures.desktop_release import ( |
| 11 | GIT_SHA_FIXTURE, |
| 12 | PUBLIC_KEY, |
| 13 | RUNBOOK, |
| 14 | sample_signed_artifacts, |
| 15 | write_artifact_files, |
| 16 | ) |
| 17 | from tests.support import KIT_ROOT |
| 18 | from tools.desktop_release.checksums import parse_sha256sums |
| 19 | from tools.desktop_release.constants import MANIFEST_FILENAME_TEMPLATE, SHA256SUMS_FILENAME |
| 20 | from tools.desktop_release.finalize import ArtifactInput, FinalizeError, finalize_release_artifacts |
| 21 | from tools.desktop_release.manifest import validate_manifest |
| 22 | |
| 23 | |
| 24 | FULL_SECRETS = { |
| 25 | "APPLE_CERTIFICATE": True, |
| 26 | "APPLE_CERTIFICATE_PASSWORD": True, |
| 27 | "APPLE_ID": True, |
| 28 | "APPLE_TEAM_ID": True, |
| 29 | "APPLE_APP_SPECIFIC_PASSWORD": True, |
| 30 | "APPLE_SIGNING_IDENTITY": True, |
| 31 | "WINDOWS_CERTIFICATE": True, |
| 32 | "WINDOWS_CERTIFICATE_PASSWORD": True, |
| 33 | "LINUX_SIGNING_KEY": True, |
| 34 | } |
| 35 | |
| 36 | |
| 37 | def test_finalize_round_trip(tmp_path: Path) -> None: |
| 38 | arts = sample_signed_artifacts() |
| 39 | paths = write_artifact_files(tmp_path / "bins", arts) |
| 40 | inputs = [ |
| 41 | ArtifactInput( |
| 42 | platform=a["platform"], |
| 43 | path=p, |
| 44 | signing_status="signed", |
| 45 | signing_method=a["signing"]["method"], |
| 46 | arch=a.get("arch"), |
| 47 | ) |
| 48 | for a, p in zip(arts, paths, strict=True) |
| 49 | ] |
| 50 | out = tmp_path / "dist" |
| 51 | manifest = finalize_release_artifacts( |
| 52 | version="0.1.0", |
| 53 | git_sha=GIT_SHA_FIXTURE, |
| 54 | artifacts=inputs, |
| 55 | output_dir=out, |
| 56 | publish=True, |
| 57 | secrets_present=FULL_SECRETS, |
| 58 | ) |
| 59 | validate_manifest(manifest) |
| 60 | manifest_path = out / MANIFEST_FILENAME_TEMPLATE.format(version="0.1.0") |
| 61 | assert manifest_path.is_file() |
| 62 | sums_path = out / SHA256SUMS_FILENAME |
| 63 | parsed = parse_sha256sums(sums_path.read_text(encoding="utf-8")) |
| 64 | for entry in manifest["artifacts"]: |
| 65 | assert parsed[entry["filename"]] == entry["sha256"] |
| 66 | # Round-trip JSON |
| 67 | reload = json.loads(manifest_path.read_text(encoding="utf-8")) |
| 68 | validate_manifest(reload) |
| 69 | |
| 70 | |
| 71 | def test_finalize_refuses_missing_apple_secret(tmp_path: Path) -> None: |
| 72 | arts = sample_signed_artifacts()[:1] |
| 73 | paths = write_artifact_files(tmp_path / "bins", arts) |
| 74 | secrets = dict(FULL_SECRETS) |
| 75 | secrets["APPLE_CERTIFICATE"] = False |
| 76 | secrets["APPLE_API_KEY"] = False |
| 77 | with pytest.raises(FinalizeError, match="Apple"): |
| 78 | finalize_release_artifacts( |
| 79 | version="0.1.0", |
| 80 | git_sha=GIT_SHA_FIXTURE, |
| 81 | artifacts=[ |
| 82 | ArtifactInput( |
| 83 | platform="macos", |
| 84 | path=paths[0], |
| 85 | signing_status="signed", |
| 86 | signing_method="developer_id_notarized", |
| 87 | ) |
| 88 | ], |
| 89 | output_dir=tmp_path / "dist", |
| 90 | publish=True, |
| 91 | secrets_present=secrets, |
| 92 | ) |
| 93 | |
| 94 | |
| 95 | def test_runbook_honesty_and_python_prerequisite() -> None: |
| 96 | text = RUNBOOK.read_text(encoding="utf-8") |
| 97 | assert "Python 3.11+" in text |
| 98 | assert "Signed installers" in text or "signed installers" in text.lower() |
| 99 | assert "detached" in text.lower() |
| 100 | assert "AppImage" in text |
| 101 | # Win/Linux still unavailable as primary CTAs; Mac signed .dmg may already be live. |
| 102 | assert ( |
| 103 | "Not available" in text |
| 104 | or "not available" in text.lower() |
| 105 | or "unavailable" in text.lower() |
| 106 | or "until a GitHub Release" in text |
| 107 | ) |
| 108 | assert PUBLIC_KEY.is_file() |
| 109 | |
| 110 | |
| 111 | def test_track_q_api_allowlist_untouched() -> None: |
| 112 | """Q3-release must not mutate Track Q closed api/* surfaces.""" |
| 113 | server = KIT_ROOT / "tools" / "app" / "server.py" |
| 114 | assert server.is_file() |
| 115 | text = server.read_text(encoding="utf-8") |
| 116 | assert '("/api/health")' in text or "/api/health" in text |
| 117 | assert "/api/status" in text |
| 118 | # desktop_release must not import or rewrite tools.app |
| 119 | release_pkg = KIT_ROOT / "tools" / "desktop_release" |
| 120 | for path in release_pkg.rglob("*.py"): |
| 121 | src = path.read_text(encoding="utf-8") |
| 122 | assert "tools.app" not in src |
| 123 | assert "from tools.app" not in src |
| 124 | assert "import tools.app" not in src |
| 125 | |
| 126 | |
| 127 | def test_bundle_refuses_env_outside_allowlist(tmp_path: Path) -> None: |
| 128 | """Fresh destination after a dry closed-allowlist copy omits .env.""" |
| 129 | dest = tmp_path / "kit" |
| 130 | dest.mkdir() |
| 131 | # Simulate closed allowlist: only copy VERSION (never .env). |
| 132 | (KIT_ROOT / "VERSION").read_text(encoding="utf-8") |
| 133 | (dest / "VERSION").write_text("0.1.0\n", encoding="utf-8") |
| 134 | planted = tmp_path / ".env" |
| 135 | planted.write_text("SECRET=1\n", encoding="utf-8") |
| 136 | assert not (dest / ".env").exists() |
| 137 | assert "SECRET" not in "\n".join(p.read_text(encoding="utf-8") for p in dest.rglob("*") if p.is_file()) |
File History
1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
13 hours ago