mirror of
https://github.com/ivuorinen/actions.git
synced 2026-01-26 11:34:00 +00:00
* docs: update documentation * feat: validate-inputs has it's own pyproject * security: mask DOCKERHUB_PASSWORD * chore: add tokens, checkout, recrete docs, integration tests * fix: add `statuses: write` permission to pr-lint
330 lines
13 KiB
Python
330 lines
13 KiB
Python
"""Tests for the test generation system."""
|
|
# pylint: disable=protected-access # Testing private methods is intentional
|
|
|
|
import importlib.util
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import yaml # pylint: disable=import-error
|
|
|
|
# Import the test generator
|
|
scripts_path = Path(__file__).parent.parent / "scripts"
|
|
sys.path.insert(0, str(scripts_path))
|
|
|
|
spec = importlib.util.spec_from_file_location("generate_tests", scripts_path / "generate-tests.py")
|
|
if spec is None or spec.loader is None:
|
|
sys.exit("Failed to load generate-tests module")
|
|
|
|
generate_tests = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(generate_tests)
|
|
# Import as GeneratorClass to avoid pytest collection warning
|
|
GeneratorClass = generate_tests.TestGenerator
|
|
|
|
|
|
class TestTestGenerator:
|
|
"""Test cases for the test generation system."""
|
|
|
|
def setup_method(self): # pylint: disable=attribute-defined-outside-init
|
|
"""Set up test fixtures."""
|
|
self.temp_dir = Path(tempfile.mkdtemp())
|
|
self.generator = GeneratorClass(self.temp_dir)
|
|
|
|
def teardown_method(self):
|
|
"""Clean up test fixtures."""
|
|
import shutil # pylint: disable=import-outside-toplevel
|
|
|
|
if self.temp_dir.exists():
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
def test_generator_initialization(self):
|
|
"""Test that generator initializes correctly."""
|
|
assert self.generator.project_root == self.temp_dir
|
|
assert self.generator.validate_inputs_dir == self.temp_dir / "validate-inputs"
|
|
assert self.generator.tests_dir == self.temp_dir / "_tests"
|
|
assert self.generator.generated_count == 0
|
|
assert self.generator.skipped_count == 0
|
|
|
|
def test_skip_existing_shellspec_test(self):
|
|
"""Test that existing ShellSpec tests are not overwritten."""
|
|
# Create action directory with action.yml
|
|
action_dir = self.temp_dir / "test-action"
|
|
action_dir.mkdir(parents=True)
|
|
|
|
action_yml = action_dir / "action.yml"
|
|
action_yml.write_text(
|
|
yaml.dump(
|
|
{
|
|
"name": "Test Action",
|
|
"description": "Test action for testing",
|
|
"inputs": {"test-input": {"required": True}},
|
|
},
|
|
),
|
|
)
|
|
|
|
# Create existing test file
|
|
test_file = self.temp_dir / "_tests" / "unit" / "test-action" / "validation.spec.sh"
|
|
test_file.parent.mkdir(parents=True, exist_ok=True)
|
|
test_file.write_text("# Existing test")
|
|
|
|
# Run generator
|
|
self.generator.generate_action_tests()
|
|
|
|
# Verify test was not overwritten
|
|
assert test_file.read_text() == "# Existing test"
|
|
assert self.generator.skipped_count == 1
|
|
assert self.generator.generated_count == 0
|
|
|
|
def test_generate_new_shellspec_test(self):
|
|
"""Test generation of new ShellSpec test."""
|
|
# Create action directory with action.yml
|
|
action_dir = self.temp_dir / "test-action"
|
|
action_dir.mkdir(parents=True)
|
|
|
|
action_yml = action_dir / "action.yml"
|
|
action_yml.write_text(
|
|
yaml.dump(
|
|
{
|
|
"name": "Test Action",
|
|
"description": "Test action for testing",
|
|
"inputs": {
|
|
"token": {"required": True, "description": "GitHub token"},
|
|
"version": {"required": False, "default": "1.0.0"},
|
|
},
|
|
},
|
|
),
|
|
)
|
|
|
|
# Run generator
|
|
self.generator.generate_action_tests()
|
|
|
|
# Verify test was created
|
|
test_file = self.temp_dir / "_tests" / "unit" / "test-action" / "validation.spec.sh"
|
|
assert test_file.exists()
|
|
assert test_file.stat().st_mode & 0o111 # Check executable
|
|
|
|
content = test_file.read_text()
|
|
assert "Test Action Input Validation" in content
|
|
assert "should fail when required inputs are missing" in content
|
|
assert "should fail without token" in content
|
|
assert "should pass with all valid inputs" in content
|
|
|
|
assert self.generator.generated_count == 1
|
|
assert self.generator.skipped_count == 0
|
|
|
|
def test_skip_existing_pytest_test(self):
|
|
"""Test that existing pytest tests are not overwritten."""
|
|
# Create validators directory
|
|
validators_dir = self.temp_dir / "validate-inputs" / "validators"
|
|
validators_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Create validator file
|
|
validator_file = validators_dir / "test_validator.py"
|
|
validator_file.write_text("class TestValidator: pass")
|
|
|
|
# Create existing test file
|
|
test_file = self.temp_dir / "validate-inputs" / "tests" / "test_test_validator.py"
|
|
test_file.parent.mkdir(parents=True, exist_ok=True)
|
|
test_file.write_text("# Existing test")
|
|
|
|
# Run generator
|
|
self.generator.generate_validator_tests()
|
|
|
|
# Verify test was not overwritten
|
|
assert test_file.read_text() == "# Existing test"
|
|
assert self.generator.skipped_count == 1
|
|
|
|
def test_generate_new_pytest_test(self):
|
|
"""Test generation of new pytest test."""
|
|
# Create validators directory
|
|
validators_dir = self.temp_dir / "validate-inputs" / "validators"
|
|
validators_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Create validator file
|
|
validator_file = validators_dir / "example_validator.py"
|
|
validator_file.write_text("class ExampleValidator: pass")
|
|
|
|
# Ensure tests directory exists
|
|
tests_dir = self.temp_dir / "validate-inputs" / "tests"
|
|
tests_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Run generator
|
|
self.generator.generate_validator_tests()
|
|
|
|
# Verify test was created
|
|
test_file = tests_dir / "test_example_validator.py"
|
|
assert test_file.exists()
|
|
|
|
content = test_file.read_text()
|
|
assert "Tests for example_validator validator" in content
|
|
assert "from validators.example_validator import ExampleValidator" in content
|
|
assert "class TestExampleValidator:" in content
|
|
assert "def test_validate_inputs(self):" in content
|
|
|
|
def test_generate_custom_validator_test(self):
|
|
"""Test generation of custom validator test."""
|
|
# Create action with custom validator
|
|
action_dir = self.temp_dir / "docker-build"
|
|
action_dir.mkdir(parents=True)
|
|
|
|
custom_validator = action_dir / "CustomValidator.py"
|
|
custom_validator.write_text("class CustomValidator: pass")
|
|
|
|
# Ensure tests directory exists
|
|
tests_dir = self.temp_dir / "validate-inputs" / "tests"
|
|
tests_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Run generator
|
|
self.generator.generate_custom_validator_tests()
|
|
|
|
# Verify test was created
|
|
test_file = tests_dir / "test_docker-build_custom.py"
|
|
assert test_file.exists()
|
|
|
|
content = test_file.read_text()
|
|
assert "Tests for docker-build custom validator" in content
|
|
assert "from CustomValidator import CustomValidator" in content
|
|
assert "test_docker_specific_validation" in content # Docker-specific test
|
|
|
|
def test_get_example_value_patterns(self):
|
|
"""Test example value generation for different input patterns."""
|
|
# Token patterns
|
|
assert (
|
|
self.generator._get_example_value("github-token", {}) == "${{ secrets.GITHUB_TOKEN }}"
|
|
)
|
|
assert self.generator._get_example_value("npm-token", {}) == "${{ secrets.GITHUB_TOKEN }}"
|
|
|
|
# Version patterns
|
|
assert self.generator._get_example_value("version", {}) == "1.2.3"
|
|
assert self.generator._get_example_value("node-version", {}) == "1.2.3"
|
|
|
|
# Path patterns
|
|
assert self.generator._get_example_value("file-path", {}) == "./path/to/file"
|
|
assert self.generator._get_example_value("directory", {}) == "./path/to/file"
|
|
|
|
# URL patterns
|
|
assert self.generator._get_example_value("webhook-url", {}) == "https://example.com"
|
|
assert self.generator._get_example_value("endpoint", {}) == "https://example.com"
|
|
|
|
# Boolean patterns
|
|
assert self.generator._get_example_value("dry-run", {}) == "false"
|
|
assert self.generator._get_example_value("debug", {}) == "false"
|
|
assert self.generator._get_example_value("push", {}) == "true"
|
|
|
|
# Default from config
|
|
assert self.generator._get_example_value("anything", {"default": "custom"}) == "custom"
|
|
|
|
# Fallback
|
|
assert self.generator._get_example_value("unknown-input", {}) == "test-value"
|
|
|
|
def test_generate_input_test_cases(self):
|
|
"""Test generation of input-specific test cases."""
|
|
# Boolean input
|
|
cases = self.generator._generate_input_test_cases("dry-run", {})
|
|
assert len(cases) == 1
|
|
assert "should accept boolean values" in cases[0]
|
|
assert "should reject invalid boolean" in cases[0]
|
|
|
|
# Version input
|
|
cases = self.generator._generate_input_test_cases("version", {})
|
|
assert len(cases) == 1
|
|
assert "should accept valid version" in cases[0]
|
|
assert "should accept version with v prefix" in cases[0]
|
|
|
|
# Token input
|
|
cases = self.generator._generate_input_test_cases("github-token", {})
|
|
assert len(cases) == 1
|
|
assert "should accept GitHub token" in cases[0]
|
|
assert "should accept classic PAT" in cases[0]
|
|
|
|
# Path input
|
|
cases = self.generator._generate_input_test_cases("config-file", {})
|
|
assert len(cases) == 1
|
|
assert "should accept valid path" in cases[0]
|
|
assert "should reject path traversal" in cases[0]
|
|
|
|
# No specific pattern
|
|
cases = self.generator._generate_input_test_cases("custom-input", {})
|
|
assert len(cases) == 0
|
|
|
|
def test_generate_pytest_content_by_type(self):
|
|
"""Test that different validator types get appropriate test methods."""
|
|
# Version validator
|
|
content = self.generator._generate_pytest_content("version_validator")
|
|
assert "test_valid_semantic_version" in content
|
|
assert "test_valid_calver" in content
|
|
|
|
# Token validator
|
|
content = self.generator._generate_pytest_content("token_validator")
|
|
assert "test_valid_github_token" in content
|
|
assert "test_other_token_types" in content
|
|
|
|
# Boolean validator
|
|
content = self.generator._generate_pytest_content("boolean_validator")
|
|
assert "test_valid_boolean_values" in content
|
|
assert "test_invalid_boolean_values" in content
|
|
|
|
# Docker validator
|
|
content = self.generator._generate_pytest_content("docker_validator")
|
|
assert "test_valid_image_names" in content
|
|
assert "test_valid_platforms" in content
|
|
|
|
# Generic validator
|
|
content = self.generator._generate_pytest_content("unknown_validator")
|
|
assert "test_validate_inputs" in content
|
|
assert "TODO: Add specific test cases" in content
|
|
|
|
def test_skip_special_directories(self):
|
|
"""Test that special directories are skipped."""
|
|
# Create special directories that should be skipped
|
|
dot_dir = self.temp_dir / ".hidden"
|
|
dot_dir.mkdir()
|
|
(dot_dir / "action.yml").write_text("name: Hidden")
|
|
|
|
underscore_dir = self.temp_dir / "_internal"
|
|
underscore_dir.mkdir()
|
|
(underscore_dir / "action.yml").write_text("name: Internal")
|
|
|
|
validate_dir = self.temp_dir / "validate-inputs"
|
|
validate_dir.mkdir()
|
|
(validate_dir / "action.yml").write_text("name: Validate")
|
|
|
|
# Run generator
|
|
self.generator.generate_action_tests()
|
|
|
|
# Verify no tests were created for special directories
|
|
assert not (self.temp_dir / "_tests" / "unit" / ".hidden").exists()
|
|
assert not (self.temp_dir / "_tests" / "unit" / "_internal").exists()
|
|
assert not (self.temp_dir / "_tests" / "unit" / "validate-inputs").exists()
|
|
|
|
assert self.generator.generated_count == 0
|
|
|
|
def test_full_generation_workflow(self):
|
|
"""Test the complete generation workflow."""
|
|
# Setup test environment
|
|
self._setup_test_environment()
|
|
|
|
# Run full generation
|
|
self.generator.generate_all_tests()
|
|
|
|
# Verify counts
|
|
assert self.generator.generated_count > 0
|
|
assert self.generator.skipped_count >= 0
|
|
|
|
# Verify some files were created
|
|
shellspec_test = self.temp_dir / "_tests" / "unit" / "test-action" / "validation.spec.sh"
|
|
assert shellspec_test.exists()
|
|
|
|
def _setup_test_environment(self):
|
|
"""Set up a minimal test environment."""
|
|
# Create an action
|
|
action_dir = self.temp_dir / "test-action"
|
|
action_dir.mkdir(parents=True)
|
|
(action_dir / "action.yml").write_text(
|
|
yaml.dump({"name": "Test", "inputs": {"test": {"required": True}}}),
|
|
)
|
|
|
|
# Create validate-inputs structure
|
|
(self.temp_dir / "validate-inputs" / "validators").mkdir(parents=True, exist_ok=True)
|
|
(self.temp_dir / "validate-inputs" / "tests").mkdir(parents=True, exist_ok=True)
|