import subprocess import tempfile import unittest from pathlib import Path import release_manager class VersionTests(unittest.TestCase): def test_bumps_each_semver_component(self): self.assertEqual(release_manager.bump_semver("0.1.2", "patch"), "0.1.3") self.assertEqual(release_manager.bump_semver("0.1.2", "minor"), "0.2.0") self.assertEqual(release_manager.bump_semver("0.1.2", "major"), "1.0.0") def test_android_code_is_monotonic(self): self.assertEqual(release_manager.semantic_version_code("0.1.3"), 1003) self.assertEqual(release_manager.suggest_version_code("0.1.3", 5000), 5001) def test_rejects_non_release_semver(self): with self.assertRaises(ValueError): release_manager.parse_semver("1.2") class SafetyTests(unittest.TestCase): def test_detects_outputs_and_secrets(self): paths = [ "src/game/example.ts", "dist-android/assets/app.js", "android/app/release.apk", ".env.production", "FINE.key.ts", "scripts/content/publish-content.mjs", "public/data/healer-man-loot.sqlite", ] self.assertEqual( release_manager.find_dangerous_staged_paths(paths), [ "dist-android/assets/app.js", "android/app/release.apk", ".env.production", ], ) def test_truenas_commands_pin_expected_commit(self): block = release_manager.generate_truenas_deploy_commands("abc123") self.assertIn("EXPECTED_COMMIT='abc123'", block) self.assertIn("pull --ff-only", block) self.assertIn('test "$ACTUAL_COMMIT" = "$EXPECTED_COMMIT"', block) class ReleaseCandidateTests(unittest.TestCase): def test_checks_untracked_files_without_changing_the_real_index(self): with tempfile.TemporaryDirectory() as temp_dir: project_root = Path(temp_dir) def git(*args): subprocess.run( ["git", *args], cwd=project_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, ) git("init", "--quiet") (project_root / "tracked.ts").write_text("export const tracked = true;\n") git("add", "tracked.ts") git( "-c", "user.name=Healer Man Tests", "-c", "user.email=tests@example.invalid", "commit", "--quiet", "-m", "Initial commit", ) untracked = project_root / "untracked.ts" untracked.write_text("export const untracked = true;\n\n") with self.assertRaisesRegex( release_manager.ReleaseError, "untracked.ts.*new blank line at EOF" ): release_manager.validate_release_candidates(project_root) untracked.write_text("export const untracked = true;\n") self.assertEqual( release_manager.validate_release_candidates(project_root), ["untracked.ts"], ) self.assertEqual( subprocess.run( ["git", "diff", "--cached", "--name-only"], cwd=project_root, text=True, stdout=subprocess.PIPE, check=True, ).stdout, "", ) if __name__ == "__main__": unittest.main()