Files

130 lines
4.8 KiB
Python

"""Unit tests for actions/resolve-version/action.yml."""
import os
from pathlib import Path
import re
import subprocess
import tempfile
import unittest
import yaml
ROOT = Path(__file__).resolve().parents[1]
ACTION = ROOT / 'actions/resolve-version/action.yml'
class ResolveVersionTests(unittest.TestCase):
def setUp(self):
self.data = yaml.safe_load(ACTION.read_text())
self.step = self.data['runs']['steps'][0]
def _run_action(self, input_version="", version_env="", app_title="Test App", env_sh=None):
with tempfile.TemporaryDirectory() as tmpdir:
work = Path(tmpdir)
if env_sh:
pkg = work / "packaging"
pkg.mkdir()
(pkg / "env.sh").write_text(env_sh)
output_file = work / "output"
summary_file = work / "summary"
env = {
**os.environ,
'INPUT_VERSION': input_version,
'VERSION': version_env,
'APP_TITLE': app_title,
'GITHUB_OUTPUT': str(output_file),
'GITHUB_STEP_SUMMARY': str(summary_file),
'GITHUB_SHA': 'abcdef1234567890',
}
res = subprocess.run(
['bash', '-euo', 'pipefail', '-c', self.step['run']],
cwd=work,
env=env,
capture_output=True,
text=True,
)
outputs = {}
if output_file.exists():
for line in output_file.read_text().splitlines():
if '=' in line:
k, v = line.split('=', 1)
outputs[k] = v
summary = summary_file.read_text() if summary_file.exists() else ""
return res, outputs, summary
def test_valid_versions(self):
cases = [
("1.2.3", "1.2.3", "false"),
("v1.2.3", "1.2.3", "false"),
("0.1.0-alpha.1", "0.1.0-alpha.1", "true"),
("2.0.0-rc-1", "2.0.0-rc-1", "true"),
("10.20.30", "10.20.30", "false"),
]
for input_ver, expected_ver, expected_pre in cases:
with self.subTest(input_ver=input_ver):
res, outputs, summary = self._run_action(input_version=input_ver)
self.assertEqual(res.returncode, 0, f"{res.stderr}\n{res.stdout}")
self.assertEqual(outputs.get('version'), expected_ver)
self.assertEqual(outputs.get('prerelease'), expected_pre)
self.assertRegex(outputs.get('date', ''), r'^[0-9]{4}-[0-9]{2}-[0-9]{2}$')
self.assertIn(f"v{expected_ver}", summary)
def test_app_title_crlf_stripped_from_summary(self):
res, outputs, summary = self._run_action(
input_version="1.2.3",
app_title="My\r\nApp\nTitle",
)
self.assertEqual(res.returncode, 0, f"{res.stderr}\n{res.stdout}")
self.assertNotIn("\r", summary)
self.assertEqual(summary.count("\n"), 1)
self.assertIn("My App Title", summary)
def test_fallback_to_version_env_and_env_sh(self):
res, outputs, _ = self._run_action(version_env="3.4.5")
self.assertEqual(res.returncode, 0)
self.assertEqual(outputs.get('version'), "3.4.5")
res, outputs, _ = self._run_action(env_sh='VERSION="4.5.6"\n')
self.assertEqual(res.returncode, 0)
self.assertEqual(outputs.get('version'), "4.5.6")
def test_newlines_and_cr_rejected(self):
bad_versions = [
"1.2.3\ndate=2026-01-01",
"1.2.3\r\nversion=fake",
"\n1.2.3",
"1.2.3\n",
"1.2.3\r",
"1.2.3\r\ndate=2026-01-01",
]
for bad_ver in bad_versions:
with self.subTest(bad_ver=repr(bad_ver)):
res, outputs, _ = self._run_action(input_version=bad_ver)
self.assertNotEqual(res.returncode, 0)
self.assertIn("must not contain carriage returns or newlines", res.stdout + res.stderr)
self.assertEqual(outputs, {})
def test_invalid_semver_rejected(self):
invalid_versions = [
"1.2",
"1.2.3.4",
"01.2.3",
"1.02.3",
"foo",
"1.2.3; touch injected",
"$(touch injected)",
"`touch injected`",
"1.2.3-",
"1.2.3+",
]
for bad_ver in invalid_versions:
with self.subTest(bad_ver=bad_ver):
res, outputs, _ = self._run_action(input_version=bad_ver)
self.assertNotEqual(res.returncode, 0)
self.assertIn("is not a version like 1.2.3 or 1.2.3-rc.1", res.stdout + res.stderr)
self.assertEqual(outputs, {})
if __name__ == '__main__':
unittest.main()