106 lines
4.8 KiB
Python
106 lines
4.8 KiB
Python
"""Exercise the real nightly decision script without GitHub or repository mutations."""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / "nightly.sh"
|
|
|
|
|
|
def release(tag, published, *, draft=False, prerelease=False):
|
|
return {"tag_name": tag, "published_at": published, "draft": draft,
|
|
"prerelease": prerelease, "target_commitish": "main"}
|
|
|
|
|
|
class NightlyDecision(unittest.TestCase):
|
|
def run_decision(self, pages, refs=None, *, event="schedule", sha="current", api_fail=False):
|
|
self.assertIsNotNone(shutil.which("jq"), "install jq to run packaging checks")
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
output = root / "output"
|
|
summary = root / "summary"
|
|
calls = root / "calls"
|
|
output.touch()
|
|
summary.touch()
|
|
for command, body in {
|
|
"gh": '''import json, os, sys
|
|
args = sys.argv[1:]
|
|
with open(os.environ["CALLS"], "a") as log: log.write(json.dumps(args) + "\\n")
|
|
if args != ["api", "--paginate", "--slurp", "repos/test/project/releases?per_page=100"]:
|
|
sys.exit("unexpected gh invocation (slurp and jq must not be combined)")
|
|
if os.environ["API_FAIL"] == "1": sys.exit("mock API failure")
|
|
print(os.environ["PAGES"])
|
|
''',
|
|
"git": '''import json, os, sys
|
|
args = sys.argv[1:]
|
|
with open(os.environ["CALLS"], "a") as log: log.write(json.dumps(args) + "\\n")
|
|
refs = json.loads(os.environ["REFS"])
|
|
if len(args) != 3 or args[:2] != ["rev-parse", "--verify"] or args[2] not in refs:
|
|
sys.exit("missing tag")
|
|
print(refs[args[2]])
|
|
''',
|
|
}.items():
|
|
path = root / command
|
|
path.write_text("#!/usr/bin/env python3\n" + body, encoding="utf-8")
|
|
path.chmod(0o755)
|
|
env = {"PATH": str(root) + os.pathsep + os.environ["PATH"],
|
|
"GITHUB_EVENT_NAME": event, "GITHUB_REPOSITORY": "test/project",
|
|
"GITHUB_SHA": sha, "GITHUB_OUTPUT": str(output),
|
|
"GITHUB_STEP_SUMMARY": str(summary), "CALLS": str(calls),
|
|
"PAGES": json.dumps(pages), "REFS": json.dumps(refs or {}),
|
|
"API_FAIL": "1" if api_fail else "0"}
|
|
result = subprocess.run(["bash", str(SCRIPT)], env=env, capture_output=True, text=True, check=False)
|
|
return result, output.read_text(), summary.read_text(), calls.read_text() if calls.exists() else ""
|
|
|
|
def test_newest_published_prerelease_across_pages_skips_exact_tag_commit(self):
|
|
pages = [[release("v1", "2026-01-01T00:00:00Z")],
|
|
[release("v2-nightly", "2026-01-03T00:00:00Z", prerelease=True),
|
|
release("v3-draft", "2026-01-04T00:00:00Z", draft=True)]]
|
|
result, output, summary, calls = self.run_decision(pages, {"refs/tags/v2-nightly^{commit}": "current"})
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual(output, "build=false\n")
|
|
self.assertIn("v2-nightly", summary)
|
|
self.assertIn("refs/tags/v2-nightly^{commit}", calls)
|
|
self.assertNotIn("v3-draft^{commit}", calls)
|
|
|
|
def test_changed_commit_builds(self):
|
|
result, output, _, _ = self.run_decision([[release("v1", "2026-01-01T00:00:00Z")]],
|
|
{"refs/tags/v1^{commit}": "previous"})
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual(output, "build=true\n")
|
|
|
|
def test_no_published_release_builds(self):
|
|
for pages in [[], [[]], [[release("draft", None, draft=True)]]]:
|
|
with self.subTest(pages=pages):
|
|
result, output, _, calls = self.run_decision(pages)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual(output, "build=true\n")
|
|
self.assertNotIn("rev-parse", calls)
|
|
|
|
def test_non_schedule_does_not_query_github(self):
|
|
for event in ["push", "workflow_dispatch"]:
|
|
with self.subTest(event=event):
|
|
result, output, _, calls = self.run_decision([], event=event, api_fail=True)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual(output, "build=true\n")
|
|
self.assertEqual(calls, "")
|
|
|
|
def test_api_failure_does_not_report_build_or_skip(self):
|
|
result, output, _, _ = self.run_decision([], api_fail=True)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertEqual(output, "")
|
|
|
|
def test_missing_tag_fails_instead_of_using_moving_target_commitish(self):
|
|
result, output, _, _ = self.run_decision([[release("missing", "2026-01-01T00:00:00Z")]])
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertEqual(output, "")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|