mirror of
https://github.com/trycua/computer.git
synced 2026-01-02 03:20:22 -06:00
* Add pyproject.toml version verification script and tests
Adds get_pyproject_version.py script to verify that pyproject.toml
versions match expected versions during git tag releases. Includes
comprehensive pytest test suite with best practices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Revert "Add pyproject.toml version verification script and tests"
This reverts commit 1d40e692cc.
* Add pyproject.toml version verification script and tests
Adds get_pyproject_version.py script to verify that pyproject.toml
versions match expected versions during git tag releases. Includes
comprehensive pytest test suite with best practices.
Updates the GitHub Actions workflow to use the verification script,
ensuring version consistency before publishing packages. Also removes
the old version-setting step as pyproject.toml is now the source of
truth for versions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* f
* add test for validation script to gha
---------
Co-authored-by: Your Name <you@example.com>
Co-authored-by: Claude <noreply@anthropic.com>
341 lines
12 KiB
Python
341 lines
12 KiB
Python
"""
|
|
Comprehensive tests for get_pyproject_version.py script using unittest.
|
|
|
|
This test suite covers:
|
|
- Version matching validation
|
|
- Error handling for missing versions
|
|
- Invalid input handling
|
|
- File not found scenarios
|
|
- Malformed TOML handling
|
|
"""
|
|
|
|
import sys
|
|
import unittest
|
|
import tempfile
|
|
from pathlib import Path
|
|
from io import StringIO
|
|
from unittest.mock import patch
|
|
|
|
# Add parent directory to path to import the module
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
# Import after path is modified
|
|
import get_pyproject_version
|
|
|
|
|
|
class TestGetPyprojectVersion(unittest.TestCase):
|
|
"""Test suite for get_pyproject_version.py functionality."""
|
|
|
|
def setUp(self):
|
|
"""Reset sys.argv before each test."""
|
|
self.original_argv = sys.argv.copy()
|
|
|
|
def tearDown(self):
|
|
"""Restore sys.argv after each test."""
|
|
sys.argv = self.original_argv
|
|
|
|
def create_pyproject_toml(self, version: str) -> Path:
|
|
"""Helper to create a temporary pyproject.toml file with a given version."""
|
|
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.toml', delete=False)
|
|
temp_file.write(f"""
|
|
[project]
|
|
name = "test-project"
|
|
version = "{version}"
|
|
description = "A test project"
|
|
""")
|
|
temp_file.close()
|
|
return Path(temp_file.name)
|
|
|
|
def create_pyproject_toml_no_version(self) -> Path:
|
|
"""Helper to create a pyproject.toml without a version field."""
|
|
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.toml', delete=False)
|
|
temp_file.write("""
|
|
[project]
|
|
name = "test-project"
|
|
description = "A test project without version"
|
|
""")
|
|
temp_file.close()
|
|
return Path(temp_file.name)
|
|
|
|
def create_pyproject_toml_no_project(self) -> Path:
|
|
"""Helper to create a pyproject.toml without a project section."""
|
|
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.toml', delete=False)
|
|
temp_file.write("""
|
|
[tool.poetry]
|
|
name = "test-project"
|
|
version = "1.0.0"
|
|
""")
|
|
temp_file.close()
|
|
return Path(temp_file.name)
|
|
|
|
def create_malformed_toml(self) -> Path:
|
|
"""Helper to create a malformed TOML file."""
|
|
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.toml', delete=False)
|
|
temp_file.write("""
|
|
[project
|
|
name = "test-project
|
|
version = "1.0.0"
|
|
""")
|
|
temp_file.close()
|
|
return Path(temp_file.name)
|
|
|
|
# Test: Successful version match
|
|
def test_matching_versions(self):
|
|
"""Test that matching versions result in success."""
|
|
pyproject_file = self.create_pyproject_toml("1.2.3")
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.2.3']
|
|
|
|
# Capture stdout
|
|
captured_output = StringIO()
|
|
with patch('sys.stdout', captured_output):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 0)
|
|
self.assertIn("✅ Version consistency check passed: 1.2.3", captured_output.getvalue())
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: Version mismatch
|
|
def test_version_mismatch(self):
|
|
"""Test that mismatched versions result in failure with appropriate error message."""
|
|
pyproject_file = self.create_pyproject_toml("1.2.3")
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.2.4']
|
|
|
|
# Capture stderr
|
|
captured_error = StringIO()
|
|
with patch('sys.stderr', captured_error):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
error_output = captured_error.getvalue()
|
|
self.assertIn("❌ Version mismatch detected!", error_output)
|
|
self.assertIn("pyproject.toml version: 1.2.3", error_output)
|
|
self.assertIn("Expected version: 1.2.4", error_output)
|
|
self.assertIn("Please update pyproject.toml to version 1.2.4", error_output)
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: Missing version in pyproject.toml
|
|
def test_missing_version_field(self):
|
|
"""Test handling of pyproject.toml without a version field."""
|
|
pyproject_file = self.create_pyproject_toml_no_version()
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.0.0']
|
|
|
|
captured_error = StringIO()
|
|
with patch('sys.stderr', captured_error):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
self.assertIn("❌ ERROR: No version found in pyproject.toml", captured_error.getvalue())
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: Missing project section
|
|
def test_missing_project_section(self):
|
|
"""Test handling of pyproject.toml without a project section."""
|
|
pyproject_file = self.create_pyproject_toml_no_project()
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.0.0']
|
|
|
|
captured_error = StringIO()
|
|
with patch('sys.stderr', captured_error):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
self.assertIn("❌ ERROR: No version found in pyproject.toml", captured_error.getvalue())
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: File not found
|
|
def test_file_not_found(self):
|
|
"""Test handling of non-existent pyproject.toml file."""
|
|
sys.argv = ['get_pyproject_version.py', '/nonexistent/pyproject.toml', '1.0.0']
|
|
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
|
|
# Test: Malformed TOML
|
|
def test_malformed_toml(self):
|
|
"""Test handling of malformed TOML file."""
|
|
pyproject_file = self.create_malformed_toml()
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.0.0']
|
|
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: Incorrect number of arguments - too few
|
|
def test_too_few_arguments(self):
|
|
"""Test that providing too few arguments results in usage error."""
|
|
sys.argv = ['get_pyproject_version.py', 'pyproject.toml']
|
|
|
|
captured_error = StringIO()
|
|
with patch('sys.stderr', captured_error):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
self.assertIn("Usage: python get_pyproject_version.py <pyproject_path> <expected_version>",
|
|
captured_error.getvalue())
|
|
|
|
# Test: Incorrect number of arguments - too many
|
|
def test_too_many_arguments(self):
|
|
"""Test that providing too many arguments results in usage error."""
|
|
sys.argv = ['get_pyproject_version.py', 'pyproject.toml', '1.0.0', 'extra']
|
|
|
|
captured_error = StringIO()
|
|
with patch('sys.stderr', captured_error):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
self.assertIn("Usage: python get_pyproject_version.py <pyproject_path> <expected_version>",
|
|
captured_error.getvalue())
|
|
|
|
# Test: No arguments
|
|
def test_no_arguments(self):
|
|
"""Test that providing no arguments results in usage error."""
|
|
sys.argv = ['get_pyproject_version.py']
|
|
|
|
captured_error = StringIO()
|
|
with patch('sys.stderr', captured_error):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
self.assertIn("Usage: python get_pyproject_version.py <pyproject_path> <expected_version>",
|
|
captured_error.getvalue())
|
|
|
|
# Test: Version with pre-release tags
|
|
def test_version_with_prerelease_tags(self):
|
|
"""Test matching versions with pre-release tags like alpha, beta, rc."""
|
|
pyproject_file = self.create_pyproject_toml("1.2.3-rc.1")
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.2.3-rc.1']
|
|
|
|
captured_output = StringIO()
|
|
with patch('sys.stdout', captured_output):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 0)
|
|
self.assertIn("✅ Version consistency check passed: 1.2.3-rc.1", captured_output.getvalue())
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: Version with build metadata
|
|
def test_version_with_build_metadata(self):
|
|
"""Test matching versions with build metadata."""
|
|
pyproject_file = self.create_pyproject_toml("1.2.3+build.123")
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.2.3+build.123']
|
|
|
|
captured_output = StringIO()
|
|
with patch('sys.stdout', captured_output):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 0)
|
|
self.assertIn("✅ Version consistency check passed: 1.2.3+build.123", captured_output.getvalue())
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: Various semantic version formats
|
|
def test_semantic_version_0_0_1(self):
|
|
"""Test semantic version 0.0.1."""
|
|
self._test_version_format("0.0.1")
|
|
|
|
def test_semantic_version_1_0_0(self):
|
|
"""Test semantic version 1.0.0."""
|
|
self._test_version_format("1.0.0")
|
|
|
|
def test_semantic_version_10_20_30(self):
|
|
"""Test semantic version 10.20.30."""
|
|
self._test_version_format("10.20.30")
|
|
|
|
def test_semantic_version_alpha(self):
|
|
"""Test semantic version with alpha tag."""
|
|
self._test_version_format("1.2.3-alpha")
|
|
|
|
def test_semantic_version_beta(self):
|
|
"""Test semantic version with beta tag."""
|
|
self._test_version_format("1.2.3-beta.1")
|
|
|
|
def test_semantic_version_rc_with_build(self):
|
|
"""Test semantic version with rc and build metadata."""
|
|
self._test_version_format("1.2.3-rc.1+build.456")
|
|
|
|
def _test_version_format(self, version: str):
|
|
"""Helper method to test various semantic version formats."""
|
|
pyproject_file = self.create_pyproject_toml(version)
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), version]
|
|
|
|
captured_output = StringIO()
|
|
with patch('sys.stdout', captured_output):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 0)
|
|
self.assertIn(f"✅ Version consistency check passed: {version}", captured_output.getvalue())
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
# Test: Empty version string
|
|
def test_empty_version_string(self):
|
|
"""Test handling of empty version string."""
|
|
pyproject_file = self.create_pyproject_toml("")
|
|
|
|
try:
|
|
sys.argv = ['get_pyproject_version.py', str(pyproject_file), '1.0.0']
|
|
|
|
captured_error = StringIO()
|
|
with patch('sys.stderr', captured_error):
|
|
with self.assertRaises(SystemExit) as cm:
|
|
get_pyproject_version.main()
|
|
|
|
self.assertEqual(cm.exception.code, 1)
|
|
# Empty string is falsy, so it should trigger error
|
|
self.assertIn("❌", captured_error.getvalue())
|
|
finally:
|
|
pyproject_file.unlink()
|
|
|
|
|
|
class TestSuiteInfo(unittest.TestCase):
|
|
"""Test suite metadata."""
|
|
|
|
def test_suite_info(self):
|
|
"""Display test suite information."""
|
|
print("\n" + "="*70)
|
|
print("Test Suite: get_pyproject_version.py")
|
|
print("Framework: unittest (Python built-in)")
|
|
print("TOML Library: tomllib (Python 3.11+ built-in)")
|
|
print("="*70)
|
|
self.assertTrue(True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Run tests with verbose output
|
|
unittest.main(verbosity=2)
|