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
303 lines
10 KiB
Python
303 lines
10 KiB
Python
"""Integration tests for the validator script execution."""
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest # pylint: disable=import-error
|
|
|
|
|
|
class TestValidatorIntegration:
|
|
"""Integration tests for running validator.py as a script."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test environment."""
|
|
# Clear INPUT_ environment variables
|
|
for key in list(os.environ.keys()):
|
|
if key.startswith("INPUT_"):
|
|
del os.environ[key]
|
|
|
|
# Create temporary output file
|
|
# noqa: SIM115 - Need persistent file for teardown, can't use context manager
|
|
self.temp_output = tempfile.NamedTemporaryFile(mode="w", delete=False) # noqa: SIM115
|
|
os.environ["GITHUB_OUTPUT"] = self.temp_output.name
|
|
self.temp_output.close()
|
|
|
|
# Get validator script path
|
|
self.validator_path = Path(__file__).parent.parent / "validator.py"
|
|
|
|
def teardown_method(self):
|
|
"""Clean up after each test."""
|
|
if Path(self.temp_output.name).exists():
|
|
os.unlink(self.temp_output.name)
|
|
|
|
def run_validator(self, env_vars=None):
|
|
"""Run the validator script with given environment variables."""
|
|
env = os.environ.copy()
|
|
if env_vars:
|
|
env.update(env_vars)
|
|
|
|
result = subprocess.run(
|
|
[sys.executable, str(self.validator_path)],
|
|
check=False,
|
|
env=env,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
return result
|
|
|
|
def test_validator_script_success(self):
|
|
"""Test validator script execution with valid inputs."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "github-release",
|
|
"INPUT_VERSION": "1.2.3",
|
|
"INPUT_CHANGELOG": "Release notes",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 0
|
|
assert "All input validation checks passed" in result.stderr
|
|
|
|
def test_validator_script_failure(self):
|
|
"""Test validator script execution with invalid inputs."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "github-release",
|
|
"INPUT_VERSION": "invalid-version",
|
|
"INPUT_CHANGELOG": "Release notes",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
assert "Input validation failed" in result.stderr
|
|
|
|
def test_validator_script_missing_required(self):
|
|
"""Test validator script with missing required inputs."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "github-release",
|
|
# Missing required INPUT_VERSION
|
|
"INPUT_CHANGELOG": "Release notes",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
assert "Required input 'version' is missing" in result.stderr
|
|
|
|
def test_validator_script_calver_validation(self):
|
|
"""Test validator script with CalVer version."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "github-release",
|
|
"INPUT_VERSION": "2024.3.1",
|
|
"INPUT_CHANGELOG": "Release notes",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 0
|
|
assert "All input validation checks passed" in result.stderr
|
|
|
|
def test_validator_script_invalid_calver(self):
|
|
"""Test validator script with invalid CalVer version."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "github-release",
|
|
"INPUT_VERSION": "2024.13.1", # Invalid month
|
|
"INPUT_CHANGELOG": "Release notes",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
assert "Invalid CalVer format" in result.stderr
|
|
|
|
def test_validator_script_docker_build(self):
|
|
"""Test validator script with docker-build action."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "docker-build",
|
|
"INPUT_CONTEXT": ".", # Required by custom validator
|
|
"INPUT_IMAGE_NAME": "myapp",
|
|
"INPUT_TAG": "v1.0.0",
|
|
"INPUT_DOCKERFILE": "Dockerfile",
|
|
"INPUT_ARCHITECTURES": "linux/amd64,linux/arm64",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 0
|
|
assert "All input validation checks passed" in result.stderr
|
|
|
|
def test_validator_script_csharp_publish(self):
|
|
"""Test validator script with csharp-publish action."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "csharp-publish",
|
|
"INPUT_TOKEN": "github_pat_" + "a" * 71,
|
|
"INPUT_NAMESPACE": "test-namespace",
|
|
"INPUT_DOTNET_VERSION": "8.0.0",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 0
|
|
assert "All input validation checks passed" in result.stderr
|
|
|
|
def test_validator_script_invalid_token(self):
|
|
"""Test validator script with invalid GitHub token."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "csharp-publish",
|
|
"INPUT_TOKEN": "invalid-token",
|
|
"INPUT_NAMESPACE": "test-namespace",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
assert "token format" in result.stderr.lower()
|
|
|
|
def test_validator_script_security_injection(self):
|
|
"""Test validator script detects security injection attempts."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "eslint-fix",
|
|
"INPUT_TOKEN": "github_pat_" + "a" * 82,
|
|
"INPUT_USERNAME": "user; rm -rf /", # Command injection attempt
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
assert "Command injection patterns not allowed" in result.stderr
|
|
|
|
def test_validator_script_numeric_range(self):
|
|
"""Test validator script with numeric range validation."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "docker-build",
|
|
"INPUT_CONTEXT": ".", # Required by custom validator
|
|
"INPUT_IMAGE_NAME": "myapp",
|
|
"INPUT_TAG": "latest",
|
|
"INPUT_PARALLEL_BUILDS": "5", # Should be valid (0-16 range)
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 0
|
|
|
|
def test_validator_script_numeric_range_invalid(self):
|
|
"""Test validator script with invalid numeric range."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "docker-build",
|
|
"INPUT_IMAGE_NAME": "myapp",
|
|
"INPUT_TAG": "latest",
|
|
"INPUT_PARALLEL_BUILDS": "20", # Should be invalid (exceeds 16)
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
|
|
def test_validator_script_boolean_validation(self):
|
|
"""Test validator script with boolean validation."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "docker-build",
|
|
"INPUT_CONTEXT": ".", # Required by custom validator
|
|
"INPUT_IMAGE_NAME": "myapp",
|
|
"INPUT_TAG": "latest",
|
|
"INPUT_DRY_RUN": "true",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 0
|
|
|
|
def test_validator_script_boolean_invalid(self):
|
|
"""Test validator script with invalid boolean."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "docker-build",
|
|
"INPUT_IMAGE_NAME": "myapp",
|
|
"INPUT_TAG": "latest",
|
|
"INPUT_DRY_RUN": "maybe", # Invalid boolean
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
|
|
def test_validator_script_no_action_type(self):
|
|
"""Test validator script without action type."""
|
|
env_vars = {
|
|
# No INPUT_ACTION_TYPE
|
|
"INPUT_VERSION": "1.2.3",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
# Should still run but with empty action type
|
|
assert result.returncode in [0, 1] # Depends on validation logic
|
|
|
|
def test_validator_script_output_file_creation(self):
|
|
"""Test that validator script creates GitHub output file."""
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "github-release",
|
|
"INPUT_VERSION": "1.2.3",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
# Check that validator ran successfully
|
|
assert result.returncode == 0
|
|
|
|
# Check that output file was written to
|
|
assert Path(self.temp_output.name).exists()
|
|
|
|
with Path(self.temp_output.name).open() as f:
|
|
content = f.read()
|
|
assert "status=" in content
|
|
|
|
def test_validator_script_error_handling(self):
|
|
"""Test validator script handles exceptions gracefully."""
|
|
# Test with invalid GITHUB_OUTPUT path to trigger exception
|
|
env_vars = {
|
|
"INPUT_ACTION_TYPE": "github-release",
|
|
"INPUT_VERSION": "1.2.3",
|
|
"GITHUB_OUTPUT": "/invalid/path/that/does/not/exist",
|
|
}
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
assert result.returncode == 1
|
|
assert "Validation script error" in result.stderr
|
|
|
|
@pytest.mark.parametrize(
|
|
"action_type,inputs,expected_success",
|
|
[
|
|
("github-release", {"version": "1.2.3"}, True),
|
|
("github-release", {"version": "2024.3.1"}, True),
|
|
("github-release", {"version": "invalid"}, False),
|
|
("docker-build", {"context": ".", "image-name": "app", "tag": "latest"}, True),
|
|
(
|
|
"docker-build",
|
|
{"context": ".", "image-name": "App", "tag": "latest"},
|
|
False,
|
|
), # Uppercase not allowed
|
|
("csharp-publish", {"token": "github_pat_" + "a" * 71, "namespace": "test"}, True),
|
|
("csharp-publish", {"token": "invalid", "namespace": "test"}, False),
|
|
],
|
|
)
|
|
def test_validator_script_parametrized(self, action_type, inputs, expected_success):
|
|
"""Parametrized test for various action types and inputs."""
|
|
env_vars = {"INPUT_ACTION_TYPE": action_type}
|
|
|
|
# Convert inputs to environment variables
|
|
for key, value in inputs.items():
|
|
env_key = f"INPUT_{key.upper().replace('-', '_')}"
|
|
env_vars[env_key] = value
|
|
|
|
result = self.run_validator(env_vars)
|
|
|
|
if expected_success:
|
|
assert result.returncode == 0, f"Expected success for {action_type} with {inputs}"
|
|
else:
|
|
assert result.returncode == 1, f"Expected failure for {action_type} with {inputs}"
|