mirror of
https://github.com/ivuorinen/actions.git
synced 2026-02-13 05:47:10 +00:00
feat: fixes, tweaks, new actions, linting (#186)
* feat: fixes, tweaks, new actions, linting * fix: improve docker publish loops and dotnet parsing (#193) * fix: harden action scripts and version checks (#191) * refactor: major repository restructuring and security enhancements Add comprehensive development infrastructure: - Add Makefile with automated documentation generation, formatting, and linting tasks - Add TODO.md tracking self-containment progress and repository improvements - Add .nvmrc for consistent Node.js version management - Create python-version-detect-v2 action for enhanced Python detection Enhance all GitHub Actions with standardized patterns: - Add consistent token handling across 27 actions using standardized input patterns - Implement bash error handling (set -euo pipefail) in all shell steps - Add comprehensive input validation for path traversal and command injection protection - Standardize checkout token authentication to prevent rate limiting - Remove relative action dependencies to ensure external usability Rewrite security workflow for PR-focused analysis: - Transform security-suite.yml to PR-only security analysis workflow - Remove scheduled runs, repository issue management, and Slack notifications - Implement smart comment generation showing only sections with content - Add GitHub Actions permission diff analysis and new action detection - Integrate OWASP, Semgrep, and TruffleHog for comprehensive PR security scanning Improve version detection and dependency management: - Simplify version detection actions to use inline logic instead of shared utilities - Fix Makefile version detection fallback to properly return 'main' when version not found - Update all external action references to use SHA-pinned versions - Remove deprecated run.sh in favor of Makefile automation Update documentation and project standards: - Enhance CLAUDE.md with self-containment requirements and linting standards - Update README.md with improved action descriptions and usage examples - Standardize code formatting with updated .editorconfig and .prettierrc.yml - Improve GitHub templates for issues and security reporting This refactoring ensures all 40 actions are fully self-contained and can be used independently when referenced as ivuorinen/actions/action-name@main, addressing the critical requirement for external usability while maintaining comprehensive security analysis and development automation. * feat: add automated action catalog generation system - Create generate_listing.cjs script for comprehensive action catalog - Add package.json with development tooling and npm scripts - Implement automated README.md catalog section with --update flag - Generate markdown reference-style links for all 40 actions - Add categorized tables with features, language support matrices - Replace static reference links with auto-generated dynamic links - Enable complete automation of action documentation maintenance * feat: enhance actions with improved documentation and functionality - Add comprehensive README files for 12 actions with usage examples - Implement new utility actions (go-version-detect, dotnet-version-detect) - Enhance node-setup with extensive configuration options - Improve error handling and validation across all actions - Update package.json scripts for better development workflow - Expand TODO.md with detailed roadmap and improvement plans - Standardize action structure with consistent inputs/outputs * feat: add comprehensive output handling across all actions - Add standardized outputs to 15 actions that previously had none - Implement consistent snake_case naming convention for all outputs - Add build status and test results outputs to build actions - Add files changed and status outputs to lint/fix actions - Add test execution metrics to php-tests action - Add stale/closed counts to stale action - Add release URLs and IDs to github-release action - Update documentation with output specifications - Mark comprehensive output handling task as complete in TODO.md * feat: implement shared cache strategy across all actions - Add caching to 10 actions that previously had none (Node.js, .NET, Python, Go) - Standardize 4 existing actions to use common-cache instead of direct actions/cache - Implement consistent cache-hit optimization to skip installations when cache available - Add language-specific cache configurations with appropriate key files - Create unified caching approach using ivuorinen/actions/common-cache@main - Fix YAML syntax error in php-composer action paths parameter - Update TODO.md to mark shared cache strategy as complete * feat: implement comprehensive retry logic for network operations - Create new common-retry action for standardized retry patterns with configurable strategies - Add retry logic to 9 actions missing network retry capabilities - Implement exponential backoff, custom timeouts, and flexible error handling - Add max-retries input parameter to all network-dependent actions (Node.js, .NET, Python, Go) - Standardize existing retry implementations to use common-retry utility - Update action catalog to include new common-retry action (41 total actions) - Update documentation with retry configuration examples and parameters - Mark retry logic implementation as complete in TODO.md roadmap * feat: enhance Node.js support with Corepack and Bun - Add Corepack support for automatic package manager version management - Add Bun package manager support across all Node.js actions - Improve Yarn Berry/PnP support with .yarnrc.yml detection - Add Node.js feature detection (ESM, TypeScript, frameworks) - Update package manager detection priority and lockfile support - Enhance caching with package-manager-specific keys - Update eslint, prettier, and biome actions for multi-package-manager support * fix: resolve critical runtime issues across multiple actions - Fix token validation by removing ineffective literal string comparisons - Add missing @microsoft/eslint-formatter-sarif dependency for SARIF output - Fix Bash variable syntax errors in username and changelog length checks - Update Dockerfile version regex to handle tags with suffixes (e.g., -alpine) - Simplify version selection logic with single grep command - Fix command execution in retry action with proper bash -c wrapper - Correct step output references using .outcome instead of .outputs.outcome - Add missing step IDs for version detection actions - Include go.mod in cache key files for accurate invalidation - Require minor version in all version regex patterns - Improve Bun installation security by verifying script before execution - Replace bc with sort -V for portable PHP version comparison - Remove non-existent pre-commit output references These fixes ensure proper runtime behavior, improved security, and better cross-platform compatibility across all affected actions. * fix: resolve critical runtime and security issues across actions - Fix biome-fix files_changed calculation using git diff instead of git status delta - Fix compress-images output description and add absolute path validation - Remove csharp-publish token default and fix token fallback in push commands - Add @microsoft/eslint-formatter-sarif to all package managers in eslint-check - Fix eslint-check command syntax by using variable assignment - Improve node-setup Bun installation security and remove invalid frozen-lockfile flag - Fix pre-commit token validation by removing ineffective literal comparison - Fix prettier-fix token comparison and expand regex for all GitHub token types - Add version-file-parser regex validation safety and fix csproj wildcard handling These fixes address security vulnerabilities, runtime errors, and functional issues to ensure reliable operation across all affected GitHub Actions. * feat: enhance Docker actions with advanced multi-architecture support Major enhancement to Docker build and publish actions with comprehensive multi-architecture capabilities and enterprise-grade features. Added features: - Advanced buildx configuration (version control, cache modes, build contexts) - Auto-detect platforms for dynamic architecture discovery - Performance optimizations with enhanced caching strategies - Security scanning with Trivy and image signing with Cosign - SBOM generation in multiple formats with validation - Verbose logging and dry-run modes for debugging - Platform-specific build args and fallback mechanisms Enhanced all Docker actions: - docker-build: Core buildx features and multi-arch support - docker-publish-gh: GitHub Packages with security features - docker-publish-hub: Docker Hub with scanning and signing - docker-publish: Orchestrator with unified configuration Updated documentation across all modified actions. * fix: resolve documentation generation placeholder issue Fixed Makefile and package.json to properly replace placeholder tokens in generated documentation, ensuring all README files show correct repository paths instead of ***PROJECT***@***VERSION***. * chore: simplify github token validation * chore(lint): optional yamlfmt, config and fixes * feat: use relative `uses` names * feat: comprehensive testing infrastructure and Python validation system - Migrate from tests/ to _tests/ directory structure with ShellSpec framework - Add comprehensive validation system with Python-based input validation - Implement dual testing approach (ShellSpec + pytest) for complete coverage - Add modern Python tooling (uv, ruff, pytest-cov) and dependencies - Create centralized validation rules with automatic generation system - Update project configuration and build system for new architecture - Enhance documentation to reflect current testing capabilities This establishes a robust foundation for action validation and testing with extensive coverage across all GitHub Actions in the repository. * chore: remove Dockerfile for now * chore: code review fixes * feat: comprehensive GitHub Actions restructuring and tooling improvements This commit represents a major restructuring of the GitHub Actions monorepo with improved tooling, testing infrastructure, and comprehensive PR #186 review implementation. ## Major Changes ### 🔧 Development Tooling & Configuration - **Shellcheck integration**: Exclude shellspec test files from linting - Updated .pre-commit-config.yaml to exclude _tests/*.sh from shellcheck/shfmt - Modified Makefile shellcheck pattern to skip shellspec files - Updated CLAUDE.md documentation with proper exclusion syntax - **Testing infrastructure**: Enhanced Python validation framework - Fixed nested if statements and boolean parameter issues in validation.py - Improved code quality with explicit keyword arguments - All pre-commit hooks now passing ### 🏗️ Project Structure & Documentation - **Added Serena AI integration** with comprehensive project memories: - Project overview, structure, and technical stack documentation - Code style conventions and completion requirements - Comprehensive PR #186 review analysis and implementation tracking - **Enhanced configuration**: Updated .gitignore, .yamlfmt.yml, pyproject.toml - **Improved testing**: Added integration workflows and enhanced test specs ### 🚀 GitHub Actions Improvements (30+ actions updated) - **Centralized validation**: Updated 41 validation rule files - **Enhanced actions**: Improvements across all action categories: - Setup actions (node-setup, version detectors) - Utility actions (version-file-parser, version-validator) - Linting actions (biome, eslint, terraform-lint-fix major refactor) - Build/publish actions (docker-build, npm-publish, csharp-*) - Repository management actions ### 📝 Documentation Updates - **README consistency**: Updated version references across action READMEs - **Enhanced documentation**: Improved action descriptions and usage examples - **CLAUDE.md**: Updated with current tooling and best practices ## Technical Improvements - **Security enhancements**: Input validation and sanitization improvements - **Performance optimizations**: Streamlined action logic and dependencies - **Cross-platform compatibility**: Better Windows/macOS/Linux support - **Error handling**: Improved error reporting and user feedback ## Files Changed - 100 files changed - 13 new Serena memory files documenting project state - 41 validation rules updated for consistency - 30+ GitHub Actions and READMEs improved - Core tooling configuration enhanced * feat: comprehensive GitHub Actions improvements and PR review fixes Major Infrastructure Improvements: - Add comprehensive testing framework with 17+ ShellSpec validation tests - Implement Docker-based testing tools with automated test runner - Add CodeRabbit configuration for automated code reviews - Restructure documentation and memory management system - Update validation rules for 25+ actions with enhanced input validation - Modernize CI/CD workflows and testing infrastructure Critical PR Review Fixes (All Issues Resolved): - Fix double caching in node-setup (eliminate redundant cache operations) - Optimize shell pipeline in version-file-parser (single awk vs complex pipeline) - Fix GitHub expression interpolation in prettier-check cache keys - Resolve terraform command order issue (validation after setup) - Add missing flake8-sarif dependency for Python SARIF output - Fix environment variable scope in pr-lint (export to GITHUB_ENV) Performance & Reliability: - Eliminate duplicate cache operations saving CI time - Improve shell script efficiency with optimized parsing - Fix command execution dependencies preventing runtime failures - Ensure proper dependency installation for all linting tools - Resolve workflow conditional logic issues Security & Quality: - All input validation rules updated with latest security patterns - Cross-platform compatibility improvements maintained - Comprehensive error handling and retry logic preserved - Modern development tooling and best practices adopted This commit addresses 100% of actionable feedback from PR review analysis, implements comprehensive testing infrastructure, and maintains high code quality standards across all 41 GitHub Actions. * feat: enhance expression handling and version parsing - Fix node-setup force-version expression logic for proper empty string handling - Improve version-file-parser with secure regex validation and enhanced Python detection - Add CodeRabbit configuration for CalVer versioning and README review guidance * feat(validate-inputs): implement modular validation system - Add modular validator architecture with specialized validators - Implement base validator classes for different input types - Add validators: boolean, docker, file, network, numeric, security, token, version - Add convention mapper for automatic input validation - Add comprehensive documentation for the validation system - Implement PCRE regex support and injection protection * feat(validate-inputs): add validation rules for all actions - Add YAML validation rules for 42 GitHub Actions - Auto-generated rules with convention mappings - Include metadata for validation coverage and quality indicators - Mark rules as auto-generated to prevent manual edits * test(validate-inputs): add comprehensive test suite for validators - Add unit tests for all validator modules - Add integration tests for the validation system - Add fixtures for version test data - Test coverage for boolean, docker, file, network, numeric, security, token, and version validators - Add tests for convention mapper and registry * feat(tools): add validation scripts and utilities - Add update-validators.py script for auto-generating rules - Add benchmark-validator.py for performance testing - Add debug-validator.py for troubleshooting - Add generate-tests.py for test generation - Add check-rules-not-manually-edited.sh for CI validation - Add fix-local-action-refs.py tool for fixing action references * feat(actions): add CustomValidator.py files for specialized validation - Add custom validators for actions requiring special validation logic - Implement validators for docker, go, node, npm, php, python, terraform actions - Add specialized validation for compress-images, common-cache, common-file-check - Implement version detection validators with language-specific logic - Add validation for build arguments, architectures, and version formats * test: update ShellSpec test framework for Python validation - Update all validation.spec.sh files to use Python validator - Add shared validation_core.py for common test utilities - Remove obsolete bash validation helpers - Update test output expectations for Python validator format - Add codeql-analysis test suite - Refactor framework utilities for Python integration - Remove deprecated test files * feat(actions): update action.yml files to use validate-inputs - Replace inline bash validation with validate-inputs action - Standardize validation across all 42 actions - Add new codeql-analysis action - Update action metadata and branding - Add validation step as first step in composite actions - Maintain backward compatibility with existing inputs/outputs * ci: update GitHub workflows for enhanced security and testing - Add new codeql-new.yml workflow - Update security scanning workflows - Enhance dependency review configuration - Update test-actions workflow for new validation system - Improve workflow permissions and security settings - Update action versions to latest SHA-pinned releases * build: update build configuration and dependencies - Update Makefile with new validation targets - Add Python dependencies in pyproject.toml - Update npm dependencies and scripts - Enhance Docker testing tools configuration - Add targets for validator updates and local ref fixes - Configure uv for Python package management * chore: update linting and documentation configuration - Update EditorConfig settings for consistent formatting - Enhance pre-commit hooks configuration - Update prettier and yamllint ignore patterns - Update gitleaks security scanning rules - Update CodeRabbit review configuration - Update CLAUDE.md with latest project standards and rules * docs: update Serena memory files and project metadata - Remove obsolete PR-186 memory files - Update project overview with current architecture - Update project structure documentation - Add quality standards and communication guidelines - Add modular validator architecture documentation - Add shellspec testing framework documentation - Update project.yml with latest configuration * feat: moved rules.yml to same folder as action, fixes * fix(validators): correct token patterns and fix validator bugs - Fix GitHub classic PAT pattern: ghp_ + 36 chars = 40 total - Fix GitHub fine-grained PAT pattern: github_pat_ + 71 chars = 82 total - Initialize result variable in convention_mapper to prevent UnboundLocalError - Fix empty URL validation in network validator to return error - Add GitHub expression check to docker architectures validator - Update docker-build CustomValidator parallel-builds max to 16 * test(validators): fix test fixtures and expectations - Fix token lengths in test data: github_pat 71 chars, ghp/gho 36 chars - Update integration tests with correct token lengths - Fix file validator test to expect absolute paths rejected for security - Rename TestGenerator import to avoid pytest collection warning - Update custom validator tests with correct input names - Change docker-build tests: platforms->architectures, tags->tag - Update docker-publish tests to match new registry enum validation * test(shellspec): fix token lengths in test helpers and specs - Fix default token lengths in spec_helper.sh to use correct 40-char format - Update csharp-publish default tokens in 4 locations - Update codeql-analysis default tokens in 2 locations - Fix codeql-analysis test tokens to correct lengths (40 and 82 chars) - Fix npm-publish fine-grained token test to use 82-char format * feat(actions): add permissions documentation and environment variable usage - Add permissions comments to all action.yml files documenting required GitHub permissions - Convert direct input usage to environment variables in shell steps for security - Add validation steps with proper error handling - Update input descriptions and add security notes where applicable - Ensure all actions follow consistent patterns for input validation * chore(workflows): update GitHub Actions workflow versions - Update workflow action versions to latest - Improve workflow consistency and maintainability * docs(security): add comprehensive security policy - Document security features and best practices - Add vulnerability reporting process - Include audit history and security testing information * docs(memory): add GitHub workflow reference documentation - Add GitHub Actions workflow commands reference - Add GitHub workflow expressions guide - Add secure workflow usage patterns and best practices * chore: token optimization, code style conventions * chore: cr fixes * fix: trivy reported Dockerfile problems * fix(security): more security fixes * chore: dockerfile and make targets for publishing * fix(ci): add creds to test-actions workflow * fix: security fix and checkout step to codeql-new * chore: test fixes * fix(security): codeql detected issues * chore: code review fixes, ReDos protection * style: apply MegaLinter fixes * fix(ci): missing packages read permission * fix(ci): add missing working directory setting * chore: linting, add validation-regex to use regex_pattern * chore: code review fixes * chore(deps): update actions * fix(security): codeql fixes * chore(cr): apply cr comments * chore: improve POSIX compatibility * chore(cr): apply cr comments * fix: codeql warning in Dockerfile, build failures * chore(cr): apply cr comments * fix: docker-testing-tools/Dockerfile * chore(cr): apply cr comments * fix(docker): update testing-tools image for GitHub Actions compatibility * chore(cr): apply cr comments * feat: add more tests, fix issues * chore: fix codeql issues, update actions * chore(cr): apply cr comments * fix: integration tests * chore: deduplication and fixes * style: apply MegaLinter fixes * chore(cr): apply cr comments * feat: dry-run mode for generate-tests * fix(ci): kcov installation * chore(cr): apply cr comments * chore(cr): apply cr comments * chore(cr): apply cr comments * chore(cr): apply cr comments, simplify action testing, use uv * fix: run-tests.sh action counting * chore(cr): apply cr comments * chore(cr): apply cr comments
This commit is contained in:
150
_tests/unit/ansible-lint-fix/validation.spec.sh
Executable file
150
_tests/unit/ansible-lint-fix/validation.spec.sh
Executable file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for ansible-lint-fix action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "ansible-lint-fix action"
|
||||
ACTION_DIR="ansible-lint-fix"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts all GitHub token formats"
|
||||
When call validate_input_python "ansible-lint-fix" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts organization token"
|
||||
When call validate_input_python "ansible-lint-fix" "token" "gho_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts user token"
|
||||
When call validate_input_python "ansible-lint-fix" "token" "ghu_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts server token"
|
||||
When call validate_input_python "ansible-lint-fix" "token" "ghs_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts refresh token"
|
||||
When call validate_input_python "ansible-lint-fix" "token" "ghr_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "ansible-lint-fix" "email" "test@example.com"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid email without @"
|
||||
When call validate_input_python "ansible-lint-fix" "email" "testexample.com"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects invalid email without domain"
|
||||
When call validate_input_python "ansible-lint-fix" "email" "test@"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "ansible-lint-fix" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects semicolon injection"
|
||||
When call validate_input_python "ansible-lint-fix" "username" "user;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects ampersand injection"
|
||||
When call validate_input_python "ansible-lint-fix" "username" "user&&malicious"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects pipe injection"
|
||||
When call validate_input_python "ansible-lint-fix" "username" "user|dangerous"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects overly long username"
|
||||
When call validate_input_python "ansible-lint-fix" "username" "this-username-is-definitely-too-long-for-github-maximum-length-limit"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "ansible-lint-fix" "max-retries" "5"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "ansible-lint-fix" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects negative retries"
|
||||
When call validate_input_python "ansible-lint-fix" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects retries above limit"
|
||||
When call validate_input_python "ansible-lint-fix" "max-retries" "15"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects non-numeric retries"
|
||||
When call validate_input_python "ansible-lint-fix" "max-retries" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Ansible Lint and Fix"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "files_changed"
|
||||
The output should include "lint_status"
|
||||
The output should include "sarif_path"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects command injection in token"
|
||||
When call validate_input_python "ansible-lint-fix" "token" "ghp_123;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects command injection in email"
|
||||
When call validate_input_python "ansible-lint-fix" "email" "user@domain.com;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates all inputs for injection patterns"
|
||||
# Username injection testing already covered above
|
||||
When call validate_input_python "ansible-lint-fix" "max-retries" "3;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "token" "ghp_123456789012345678901234567890123456" "username" "github-actions" "email" "test@example.com" "max-retries" "3"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: ansible-lint-fix"
|
||||
The stderr should include "Output test passed for: ansible-lint-fix"
|
||||
End
|
||||
End
|
||||
End
|
||||
149
_tests/unit/biome-check/validation.spec.sh
Executable file
149
_tests/unit/biome-check/validation.spec.sh
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for biome-check action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "biome-check action"
|
||||
ACTION_DIR="biome-check"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts personal access token"
|
||||
When call validate_input_python "biome-check" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts organization token"
|
||||
When call validate_input_python "biome-check" "token" "gho_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts user token"
|
||||
When call validate_input_python "biome-check" "token" "ghu_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts server token"
|
||||
When call validate_input_python "biome-check" "token" "ghs_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts refresh token"
|
||||
When call validate_input_python "biome-check" "token" "ghr_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "biome-check" "email" "test@example.com"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid email without @"
|
||||
When call validate_input_python "biome-check" "email" "testexample.com"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects invalid email without domain"
|
||||
When call validate_input_python "biome-check" "email" "test@"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "biome-check" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects semicolon injection"
|
||||
When call validate_input_python "biome-check" "username" "user;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects ampersand injection"
|
||||
When call validate_input_python "biome-check" "username" "user&&malicious"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects pipe injection"
|
||||
When call validate_input_python "biome-check" "username" "user|dangerous"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects overly long username"
|
||||
When call validate_input_python "biome-check" "username" "this-username-is-definitely-too-long-for-github-maximum-length-limit"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "biome-check" "max-retries" "5"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "biome-check" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects negative retries"
|
||||
When call validate_input_python "biome-check" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects retries above limit"
|
||||
When call validate_input_python "biome-check" "max-retries" "15"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects non-numeric retries"
|
||||
When call validate_input_python "biome-check" "max-retries" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Biome Check"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "check_status"
|
||||
The output should include "errors_count"
|
||||
The output should include "warnings_count"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects command injection in token"
|
||||
When call validate_input_python "biome-check" "token" "ghp_123;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects command injection in email"
|
||||
When call validate_input_python "biome-check" "email" "user@domain.com;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates all inputs for injection patterns"
|
||||
When call validate_input_python "biome-check" "max-retries" "3;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "token" "ghp_123456789012345678901234567890123456" "username" "github-actions" "email" "test@example.com" "max-retries" "3"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: biome-check"
|
||||
The stderr should include "Output test passed for: biome-check"
|
||||
End
|
||||
End
|
||||
End
|
||||
148
_tests/unit/biome-fix/validation.spec.sh
Executable file
148
_tests/unit/biome-fix/validation.spec.sh
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for biome-fix action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "biome-fix action"
|
||||
ACTION_DIR="biome-fix"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts personal access token"
|
||||
When call validate_input_python "biome-fix" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts organization token"
|
||||
When call validate_input_python "biome-fix" "token" "gho_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts user token"
|
||||
When call validate_input_python "biome-fix" "token" "ghu_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts server token"
|
||||
When call validate_input_python "biome-fix" "token" "ghs_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts refresh token"
|
||||
When call validate_input_python "biome-fix" "token" "ghr_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "biome-fix" "email" "test@example.com"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid email without @"
|
||||
When call validate_input_python "biome-fix" "email" "testexample.com"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects invalid email without domain"
|
||||
When call validate_input_python "biome-fix" "email" "test@"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "biome-fix" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects semicolon injection"
|
||||
When call validate_input_python "biome-fix" "username" "user;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects ampersand injection"
|
||||
When call validate_input_python "biome-fix" "username" "user&&malicious"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects pipe injection"
|
||||
When call validate_input_python "biome-fix" "username" "user|dangerous"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects overly long username"
|
||||
When call validate_input_python "biome-fix" "username" "this-username-is-definitely-too-long-for-github-maximum-length-limit"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "biome-fix" "max-retries" "5"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "biome-fix" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects negative retries"
|
||||
When call validate_input_python "biome-fix" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects retries above limit"
|
||||
When call validate_input_python "biome-fix" "max-retries" "15"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects non-numeric retries"
|
||||
When call validate_input_python "biome-fix" "max-retries" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Biome Fix"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "files_changed"
|
||||
The output should include "fix_status"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects command injection in token"
|
||||
When call validate_input_python "biome-fix" "token" "ghp_123;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects command injection in email"
|
||||
When call validate_input_python "biome-fix" "email" "user@domain.com;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates all inputs for injection patterns"
|
||||
When call validate_input_python "biome-fix" "max-retries" "3;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "token" "ghp_123456789012345678901234567890123456" "username" "github-actions" "email" "test@example.com" "max-retries" "3"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: biome-fix"
|
||||
The stderr should include "Output test passed for: biome-fix"
|
||||
End
|
||||
End
|
||||
End
|
||||
377
_tests/unit/codeql-analysis/validation.spec.sh
Executable file
377
_tests/unit/codeql-analysis/validation.spec.sh
Executable file
@@ -0,0 +1,377 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
Describe "codeql-analysis validation"
|
||||
Include "_tests/unit/spec_helper.sh"
|
||||
|
||||
Describe "language validation"
|
||||
It "validates javascript language"
|
||||
When call validate_input_python "codeql-analysis" "language" "javascript"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates typescript language"
|
||||
When call validate_input_python "codeql-analysis" "language" "typescript"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates python language"
|
||||
When call validate_input_python "codeql-analysis" "language" "python"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates java language"
|
||||
When call validate_input_python "codeql-analysis" "language" "java"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates csharp language"
|
||||
When call validate_input_python "codeql-analysis" "language" "csharp"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates cpp language"
|
||||
When call validate_input_python "codeql-analysis" "language" "cpp"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates c language"
|
||||
When call validate_input_python "codeql-analysis" "language" "c"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates go language"
|
||||
When call validate_input_python "codeql-analysis" "language" "go"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates ruby language"
|
||||
When call validate_input_python "codeql-analysis" "language" "ruby"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates swift language"
|
||||
When call validate_input_python "codeql-analysis" "language" "swift"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates kotlin language"
|
||||
When call validate_input_python "codeql-analysis" "language" "kotlin"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates actions language"
|
||||
When call validate_input_python "codeql-analysis" "language" "actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates case insensitive languages"
|
||||
When call validate_input_python "codeql-analysis" "language" "JavaScript"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid language"
|
||||
When call validate_input_python "codeql-analysis" "language" "invalid-lang"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty language"
|
||||
When call validate_input_python "codeql-analysis" "language" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects unsupported language"
|
||||
When call validate_input_python "codeql-analysis" "language" "rust"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Describe "queries validation"
|
||||
It "validates security-extended queries"
|
||||
When call validate_input_python "codeql-analysis" "queries" "security-extended"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates security-and-quality queries"
|
||||
When call validate_input_python "codeql-analysis" "queries" "security-and-quality"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates code-scanning queries"
|
||||
When call validate_input_python "codeql-analysis" "queries" "code-scanning"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates default queries"
|
||||
When call validate_input_python "codeql-analysis" "queries" "default"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates case insensitive queries"
|
||||
When call validate_input_python "codeql-analysis" "queries" "Security-Extended"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates custom query file with .ql extension"
|
||||
When call validate_input_python "codeql-analysis" "queries" "custom-queries.ql"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates custom query suite with .qls extension"
|
||||
When call validate_input_python "codeql-analysis" "queries" "my-suite.qls"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates custom query file with path"
|
||||
When call validate_input_python "codeql-analysis" "queries" ".github/codeql/custom.ql"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid query suite"
|
||||
When call validate_input_python "codeql-analysis" "queries" "invalid-suite"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty queries"
|
||||
When call validate_input_python "codeql-analysis" "queries" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Describe "category validation"
|
||||
It "validates proper category format"
|
||||
When call validate_input_python "codeql-analysis" "category" "/language:javascript"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates custom category"
|
||||
When call validate_input_python "codeql-analysis" "category" "/custom/analysis"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates category with underscores"
|
||||
When call validate_input_python "codeql-analysis" "category" "/my_custom_category"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates category with hyphens"
|
||||
When call validate_input_python "codeql-analysis" "category" "/my-custom-category"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates category with colons"
|
||||
When call validate_input_python "codeql-analysis" "category" "/language:python:custom"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates empty category (optional)"
|
||||
When call validate_input_python "codeql-analysis" "category" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects category without leading slash"
|
||||
When call validate_input_python "codeql-analysis" "category" "language:javascript"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects category with invalid characters"
|
||||
When call validate_input_python "codeql-analysis" "category" "/language@javascript"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects category with spaces"
|
||||
When call validate_input_python "codeql-analysis" "category" "/language javascript"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Describe "config-file validation"
|
||||
It "validates valid config file path"
|
||||
When call validate_input_python "codeql-analysis" "config-file" ".github/codeql/config.yml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates relative config file path"
|
||||
When call validate_input_python "codeql-analysis" "config-file" "codeql-config.yml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates empty config file (optional)"
|
||||
When call validate_input_python "codeql-analysis" "config-file" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects absolute path"
|
||||
When call validate_input_python "codeql-analysis" "config-file" "/etc/config.yml"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "codeql-analysis" "config-file" "../config.yml"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Describe "checkout-ref validation"
|
||||
It "validates main branch"
|
||||
When call validate_input_python "codeql-analysis" "checkout-ref" "main"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates feature branch"
|
||||
When call validate_input_python "codeql-analysis" "checkout-ref" "feature/security-updates"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates commit SHA"
|
||||
When call validate_input_python "codeql-analysis" "checkout-ref" "abc123def456"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates tag"
|
||||
When call validate_input_python "codeql-analysis" "checkout-ref" "v1.2.3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates empty checkout-ref (optional)"
|
||||
When call validate_input_python "codeql-analysis" "checkout-ref" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Describe "token validation"
|
||||
It "validates classic GitHub token"
|
||||
When call validate_input_python "codeql-analysis" "token" "ghp_1234567890abcdef1234567890abcdef1234"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates fine-grained token"
|
||||
When call validate_input_python "codeql-analysis" "token" "github_pat_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates installation token"
|
||||
When call validate_input_python "codeql-analysis" "token" "ghs_1234567890abcdef1234567890abcdef1234"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "codeql-analysis" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty token"
|
||||
When call validate_input_python "codeql-analysis" "token" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Describe "working-directory validation"
|
||||
It "validates current directory"
|
||||
When call validate_input_python "codeql-analysis" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates relative directory"
|
||||
When call validate_input_python "codeql-analysis" "working-directory" "src"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates nested directory"
|
||||
When call validate_input_python "codeql-analysis" "working-directory" "backend/src"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects absolute path"
|
||||
When call validate_input_python "codeql-analysis" "working-directory" "/home/user/project"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "codeql-analysis" "working-directory" "../other-project"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Describe "upload-results validation"
|
||||
It "validates true value"
|
||||
When call validate_input_python "codeql-analysis" "upload-results" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates false value"
|
||||
When call validate_input_python "codeql-analysis" "upload-results" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects uppercase TRUE"
|
||||
When call validate_input_python "codeql-analysis" "upload-results" "TRUE"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects uppercase FALSE"
|
||||
When call validate_input_python "codeql-analysis" "upload-results" "FALSE"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid boolean"
|
||||
When call validate_input_python "codeql-analysis" "upload-results" "yes"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty value"
|
||||
When call validate_input_python "codeql-analysis" "upload-results" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Describe "complete action validation"
|
||||
It "validates all required inputs with minimal config"
|
||||
# Set up environment for the validation
|
||||
export INPUT_ACTION_TYPE="codeql-analysis"
|
||||
export INPUT_LANGUAGE="javascript"
|
||||
|
||||
When call uv run validate-inputs/validator.py
|
||||
The status should be success
|
||||
The stderr should include "All input validation checks passed"
|
||||
End
|
||||
|
||||
It "validates all inputs with full config"
|
||||
# Set up environment for the validation
|
||||
export INPUT_ACTION_TYPE="codeql-analysis"
|
||||
export INPUT_LANGUAGE="python"
|
||||
export INPUT_QUERIES="security-extended"
|
||||
export INPUT_CONFIG_FILE=".github/codeql/config.yml"
|
||||
export INPUT_CATEGORY="/custom/python-analysis"
|
||||
export INPUT_CHECKOUT_REF="main"
|
||||
export INPUT_TOKEN="ghp_1234567890abcdef1234567890abcdef1234"
|
||||
export INPUT_WORKING_DIRECTORY="backend"
|
||||
export INPUT_UPLOAD_RESULTS="true"
|
||||
|
||||
When call uv run validate-inputs/validator.py
|
||||
The status should be success
|
||||
The stderr should include "All input validation checks passed"
|
||||
End
|
||||
|
||||
It "fails validation with missing required language"
|
||||
# Set up environment for the validation
|
||||
export INPUT_ACTION_TYPE="codeql-analysis"
|
||||
unset INPUT_LANGUAGE
|
||||
|
||||
When call uv run validate-inputs/validator.py
|
||||
The status should be failure
|
||||
The stderr should include "Required input 'language' is missing"
|
||||
End
|
||||
|
||||
It "fails validation with invalid language and queries"
|
||||
# Set up environment for the validation
|
||||
export INPUT_ACTION_TYPE="codeql-analysis"
|
||||
export INPUT_LANGUAGE="invalid-lang"
|
||||
export INPUT_QUERIES="invalid-suite"
|
||||
|
||||
When call uv run validate-inputs/validator.py
|
||||
The status should be failure
|
||||
The stderr should include "Unsupported CodeQL language"
|
||||
The stderr should include "Invalid CodeQL query suite"
|
||||
End
|
||||
End
|
||||
End
|
||||
168
_tests/unit/common-cache/validation.spec.sh
Executable file
168
_tests/unit/common-cache/validation.spec.sh
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for common-cache action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "common-cache action"
|
||||
ACTION_DIR="common-cache"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating cache type input"
|
||||
It "accepts npm cache type"
|
||||
When call validate_input_python "common-cache" "type" "npm"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts composer cache type"
|
||||
When call validate_input_python "common-cache" "type" "composer"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts go cache type"
|
||||
When call validate_input_python "common-cache" "type" "go"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts pip cache type"
|
||||
When call validate_input_python "common-cache" "type" "pip"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts maven cache type"
|
||||
When call validate_input_python "common-cache" "type" "maven"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts gradle cache type"
|
||||
When call validate_input_python "common-cache" "type" "gradle"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects empty cache type"
|
||||
When call validate_input_python "common-cache" "type" ""
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects invalid cache type"
|
||||
Pending "TODO: Implement enum validation for cache type"
|
||||
When call validate_input_python "common-cache" "type" "invalid-type"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating paths input"
|
||||
It "accepts single path"
|
||||
When call validate_input_python "common-cache" "paths" "node_modules"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts multiple paths"
|
||||
When call validate_input_python "common-cache" "paths" "node_modules,dist,build"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects empty paths"
|
||||
When call validate_input_python "common-cache" "paths" ""
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "common-cache" "paths" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects command injection in paths"
|
||||
When call validate_input_python "common-cache" "paths" "node_modules;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating key-prefix input"
|
||||
It "accepts valid key prefix"
|
||||
When call validate_input_python "common-cache" "key-prefix" "v2-build"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects command injection in key-prefix"
|
||||
When call validate_input_python "common-cache" "key-prefix" "v2&&malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating key-files input"
|
||||
It "accepts single key file"
|
||||
When call validate_input_python "common-cache" "key-files" "package.json"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts multiple key files"
|
||||
When call validate_input_python "common-cache" "key-files" "package.json,package-lock.json,yarn.lock"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects path traversal in key-files"
|
||||
When call validate_input_python "common-cache" "key-files" "../../../sensitive.json"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating restore-keys input"
|
||||
It "accepts valid restore keys format"
|
||||
When call validate_input_python "common-cache" "restore-keys" "Linux-npm-,Linux-"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects malicious restore keys"
|
||||
When call validate_input_python "common-cache" "restore-keys" "Linux-npm-;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Common Cache"
|
||||
End
|
||||
|
||||
It "defines required inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "type"
|
||||
The output should include "paths"
|
||||
End
|
||||
|
||||
It "defines optional inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "key-prefix"
|
||||
The output should include "key-files"
|
||||
The output should include "restore-keys"
|
||||
The output should include "env-vars"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "cache-hit"
|
||||
The output should include "cache-key"
|
||||
The output should include "cache-paths"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects injection in all input types"
|
||||
When call validate_input_python "common-cache" "type" "npm;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates environment variable names safely"
|
||||
When call validate_input_python "common-cache" "env-vars" "NODE_ENV,CI"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects injection in environment variables"
|
||||
When call validate_input_python "common-cache" "env-vars" "NODE_ENV;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "type" "npm" "paths" "node_modules"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: common-cache"
|
||||
The stderr should include "Output test passed for: common-cache"
|
||||
End
|
||||
End
|
||||
End
|
||||
99
_tests/unit/common-file-check/validation.spec.sh
Executable file
99
_tests/unit/common-file-check/validation.spec.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for common-file-check action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "common-file-check action"
|
||||
ACTION_DIR="common-file-check"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating file-pattern input"
|
||||
It "accepts simple file pattern"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "package.json"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts glob pattern with wildcard"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "*.json"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts glob pattern with question mark"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "test?.js"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts nested path pattern"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "src/**/*.ts"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts pattern with braces"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "*.{js,ts}"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts pattern with brackets"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "[A-Z]*.txt"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects empty file pattern"
|
||||
When call validate_input_python "common-file-check" "file-pattern" ""
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects command injection"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "*.json;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Common File Check"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "file-pattern"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "found"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "validates glob patterns safely"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "**/*.{js,ts,json}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects injection in glob patterns"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "*.js&&malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects pipe injection in patterns"
|
||||
When call validate_input_python "common-file-check" "file-pattern" "*.js|dangerous"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "file-pattern" "*.json"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: common-file-check"
|
||||
The stderr should include "Output test passed for: common-file-check"
|
||||
End
|
||||
End
|
||||
End
|
||||
165
_tests/unit/common-retry/validation.spec.sh
Executable file
165
_tests/unit/common-retry/validation.spec.sh
Executable file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for common-retry action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "common-retry action"
|
||||
ACTION_DIR="common-retry"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts minimum value (1)"
|
||||
When call validate_input_python "common-retry" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts maximum value (10)"
|
||||
When call validate_input_python "common-retry" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects below minimum"
|
||||
When call validate_input_python "common-retry" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects above maximum"
|
||||
When call validate_input_python "common-retry" "max-retries" "11"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects non-numeric"
|
||||
When call validate_input_python "common-retry" "max-retries" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating retry-delay input"
|
||||
It "accepts minimum value (1)"
|
||||
When call validate_input_python "common-retry" "retry-delay" "1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts maximum value (300)"
|
||||
When call validate_input_python "common-retry" "retry-delay" "300"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects below minimum"
|
||||
When call validate_input_python "common-retry" "retry-delay" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects above maximum"
|
||||
When call validate_input_python "common-retry" "retry-delay" "301"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating backoff-strategy input"
|
||||
It "accepts linear strategy"
|
||||
When call validate_input_python "common-retry" "backoff-strategy" "linear"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts exponential strategy"
|
||||
When call validate_input_python "common-retry" "backoff-strategy" "exponential"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts fixed strategy"
|
||||
When call validate_input_python "common-retry" "backoff-strategy" "fixed"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid strategy"
|
||||
When call validate_input_python "common-retry" "backoff-strategy" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating timeout input"
|
||||
It "accepts minimum value (1)"
|
||||
When call validate_input_python "common-retry" "timeout" "1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts maximum value (3600)"
|
||||
When call validate_input_python "common-retry" "timeout" "3600"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects below minimum"
|
||||
When call validate_input_python "common-retry" "timeout" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects above maximum"
|
||||
When call validate_input_python "common-retry" "timeout" "3601"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating working-directory input"
|
||||
It "accepts current directory"
|
||||
When call validate_input_python "common-retry" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
It "accepts relative path"
|
||||
When call validate_input_python "common-retry" "working-directory" "src/app"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "common-retry" "working-directory" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating shell input"
|
||||
It "accepts bash shell"
|
||||
When call validate_input_python "common-retry" "shell" "bash"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts sh shell"
|
||||
When call validate_input_python "common-retry" "shell" "sh"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects zsh shell"
|
||||
When call validate_input_python "common-retry" "shell" "zsh"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Common Retry"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects command injection with semicolon"
|
||||
When call validate_input_python "common-retry" "command" "value; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects command injection with ampersand"
|
||||
When call validate_input_python "common-retry" "command" "value && malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts valid success codes"
|
||||
When call validate_input_python "common-retry" "success-codes" "0,1,2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects success codes with injection"
|
||||
When call validate_input_python "common-retry" "success-codes" "0;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts valid retry codes"
|
||||
When call validate_input_python "common-retry" "retry-codes" "1,126,127"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects retry codes with injection"
|
||||
When call validate_input_python "common-retry" "retry-codes" "1;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
End
|
||||
52
_tests/unit/compress-images/validation.spec.sh
Executable file
52
_tests/unit/compress-images/validation.spec.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for compress-images action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "compress-images action"
|
||||
ACTION_DIR="compress-images"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating inputs"
|
||||
It "accepts valid quality setting"
|
||||
# pick one of the defined quality inputs
|
||||
inputs="$(get_action_inputs "$ACTION_FILE")"
|
||||
QUALITY_INPUT=$(echo "$inputs" | grep -E '^(image-quality|png-quality)$' | head -n1)
|
||||
[ -z "$QUALITY_INPUT" ] && Skip "No quality input found in action.yml"
|
||||
When call validate_input_python "compress-images" "$QUALITY_INPUT" "80"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid quality"
|
||||
# pick one of the defined quality inputs
|
||||
inputs="$(get_action_inputs "$ACTION_FILE")"
|
||||
QUALITY_INPUT=$(echo "$inputs" | grep -E '^(image-quality|png-quality)$' | head -n1)
|
||||
[ -z "$QUALITY_INPUT" ] && Skip "No quality input found in action.yml"
|
||||
When call validate_input_python "compress-images" "$QUALITY_INPUT" "150"
|
||||
The status should be failure
|
||||
End
|
||||
It "accepts valid path pattern"
|
||||
# use the defined path-filter input
|
||||
PATH_INPUT="ignore-paths"
|
||||
When call validate_input_python "compress-images" "$PATH_INPUT" "assets/**/*.{jpg,png}"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in path"
|
||||
# use the defined path-filter input
|
||||
PATH_INPUT="ignore-paths"
|
||||
When call validate_input_python "compress-images" "$PATH_INPUT" "images;rm -rf /tmp"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*Compress*"
|
||||
End
|
||||
End
|
||||
End
|
||||
81
_tests/unit/csharp-build/validation.spec.sh
Executable file
81
_tests/unit/csharp-build/validation.spec.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for csharp-build action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "csharp-build action"
|
||||
ACTION_DIR="csharp-build"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating dotnet-version input"
|
||||
It "accepts valid dotnet version"
|
||||
When call validate_input_python "csharp-build" "dotnet-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts dotnet 6 LTS"
|
||||
When call validate_input_python "csharp-build" "dotnet-version" "6.0"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid version"
|
||||
When call validate_input_python "csharp-build" "dotnet-version" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid max-retries"
|
||||
When call validate_input_python "csharp-build" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts minimum retries"
|
||||
When call validate_input_python "csharp-build" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "csharp-build" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects non-numeric retries"
|
||||
When call validate_input_python "csharp-build" "max-retries" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*C#*"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "dotnet-version"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "build_status"
|
||||
The output should include "test_status"
|
||||
The output should include "dotnet_version"
|
||||
The output should include "artifacts_path"
|
||||
The output should include "test_results_path"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "dotnet-version" "8.0" "max-retries" "3"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: csharp-build"
|
||||
The stderr should include "Output test passed for: csharp-build"
|
||||
End
|
||||
End
|
||||
End
|
||||
36
_tests/unit/csharp-lint-check/validation.spec.sh
Executable file
36
_tests/unit/csharp-lint-check/validation.spec.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for csharp-lint-check action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "csharp-lint-check action"
|
||||
ACTION_DIR="csharp-lint-check"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating inputs"
|
||||
It "accepts valid dotnet version"
|
||||
When call validate_input_python "csharp-lint-check" "dotnet-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid dotnet version format"
|
||||
When call validate_input_python "csharp-lint-check" "dotnet-version" "8.0.100"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection"
|
||||
When call validate_input_python "csharp-lint-check" "dotnet-version" "8.0;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*C#*"
|
||||
End
|
||||
End
|
||||
End
|
||||
52
_tests/unit/csharp-publish/validation.spec.sh
Executable file
52
_tests/unit/csharp-publish/validation.spec.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for csharp-publish action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "csharp-publish action"
|
||||
ACTION_DIR="csharp-publish"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating inputs"
|
||||
It "accepts valid dotnet version"
|
||||
When call validate_input_python "csharp-publish" "dotnet-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid namespace"
|
||||
When call validate_input_python "csharp-publish" "namespace" "ivuorinen"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts namespace with hyphens in middle"
|
||||
When call validate_input_python "csharp-publish" "namespace" "my-org-name"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects namespace ending with hyphen"
|
||||
When call validate_input_python "csharp-publish" "namespace" "invalid-"
|
||||
The status should be failure
|
||||
End
|
||||
It "accepts valid GitHub token"
|
||||
When call validate_input_python "csharp-publish" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in namespace"
|
||||
When call validate_input_python "csharp-publish" "namespace" "invalid;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects injection in token"
|
||||
When call validate_input_python "csharp-publish" "token" "token;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*C#*"
|
||||
End
|
||||
End
|
||||
End
|
||||
218
_tests/unit/docker-build/validation.spec.sh
Executable file
218
_tests/unit/docker-build/validation.spec.sh
Executable file
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for docker-build action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "docker-build action"
|
||||
ACTION_DIR="docker-build"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating image-name input"
|
||||
It "accepts valid image name"
|
||||
When call validate_input_python "docker-build" "image-name" "myapp"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts image name with registry prefix"
|
||||
When call validate_input_python "docker-build" "image-name" "registry.example.com/myapp"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects command injection in image name"
|
||||
When call validate_input_python "docker-build" "image-name" "app; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating tag input"
|
||||
It "accepts valid tag format"
|
||||
When call validate_input_python "docker-build" "tag" "v1.0.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts semantic version tag"
|
||||
When call validate_input_python "docker-build" "tag" "1.2.3"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts latest tag"
|
||||
When call validate_input_python "docker-build" "tag" "latest"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid tag format"
|
||||
When call validate_input_python "docker-build" "tag" "invalid_tag!"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating architectures input"
|
||||
It "accepts valid architectures list"
|
||||
When call validate_input_python "docker-build" "architectures" "linux/amd64,linux/arm64"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts single architecture"
|
||||
When call validate_input_python "docker-build" "architectures" "linux/amd64"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts ARM variants"
|
||||
When call validate_input_python "docker-build" "architectures" "linux/arm/v7,linux/arm/v6"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating dockerfile input"
|
||||
It "accepts valid dockerfile path"
|
||||
When call validate_input_python "docker-build" "dockerfile" "Dockerfile"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts custom dockerfile path"
|
||||
When call validate_input_python "docker-build" "dockerfile" "docker/Dockerfile.prod"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects malicious dockerfile path"
|
||||
When call validate_input_python "docker-build" "dockerfile" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating context input"
|
||||
It "accepts valid build context"
|
||||
When call validate_input_python "docker-build" "context" "."
|
||||
The status should be success
|
||||
End
|
||||
It "accepts relative context path"
|
||||
When call validate_input_python "docker-build" "context" "src/app"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts path traversal in context (no validation in action)"
|
||||
When call validate_input_python "docker-build" "context" "../../../etc"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating build-args input"
|
||||
It "accepts valid build args format"
|
||||
When call validate_input_python "docker-build" "build-args" "NODE_ENV=production,VERSION=1.0.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts empty build args"
|
||||
When call validate_input_python "docker-build" "build-args" ""
|
||||
The status should be success
|
||||
End
|
||||
It "rejects malicious build args"
|
||||
When call validate_input_python "docker-build" "build-args" "ARG=\$(rm -rf /)"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating cache inputs"
|
||||
It "accepts valid cache mode"
|
||||
When call validate_input_python "docker-build" "cache-mode" "max"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts min cache mode"
|
||||
When call validate_input_python "docker-build" "cache-mode" "min"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts inline cache mode"
|
||||
When call validate_input_python "docker-build" "cache-mode" "inline"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid cache mode"
|
||||
When call validate_input_python "docker-build" "cache-mode" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
It "accepts valid cache-from format"
|
||||
When call validate_input_python "docker-build" "cache-from" "type=registry,ref=myapp:cache"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security features"
|
||||
It "accepts scan-image boolean"
|
||||
When call validate_input_python "docker-build" "scan-image" "true"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts sign-image boolean"
|
||||
When call validate_input_python "docker-build" "sign-image" "false"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid SBOM format"
|
||||
When call validate_input_python "docker-build" "sbom-format" "spdx-json"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts cyclonedx SBOM format"
|
||||
When call validate_input_python "docker-build" "sbom-format" "cyclonedx-json"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid SBOM format"
|
||||
When call validate_input_python "docker-build" "sbom-format" "invalid-format"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating performance options"
|
||||
It "accepts valid parallel builds number"
|
||||
When call validate_input_python "docker-build" "parallel-builds" "4"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts auto parallel builds"
|
||||
When call validate_input_python "docker-build" "parallel-builds" "0"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects negative parallel builds"
|
||||
When call validate_input_python "docker-build" "parallel-builds" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects non-numeric parallel builds"
|
||||
When call validate_input_python "docker-build" "parallel-builds" "not-a-number"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
When call get_action_name "$ACTION_FILE"
|
||||
The output should match pattern "*Docker*"
|
||||
End
|
||||
|
||||
It "defines all required inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "tag"
|
||||
End
|
||||
|
||||
It "defines all expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "image-digest"
|
||||
The output should include "metadata"
|
||||
The output should include "platforms"
|
||||
The output should include "build-time"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects injection in all Docker inputs"
|
||||
When call validate_input_python "docker-build" "tag" "v1.0.0;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates buildx version safely"
|
||||
When call validate_input_python "docker-build" "buildx-version" "0.12.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects malicious buildx version"
|
||||
When call validate_input_python "docker-build" "buildx-version" "0.12;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "tag" "v1.0.0" "dockerfile" "Dockerfile"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: docker-build"
|
||||
The stderr should include "Output test passed for: docker-build"
|
||||
End
|
||||
End
|
||||
End
|
||||
40
_tests/unit/docker-publish-gh/validation.spec.sh
Executable file
40
_tests/unit/docker-publish-gh/validation.spec.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for docker-publish-gh action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "docker-publish-gh action"
|
||||
ACTION_DIR="docker-publish-gh"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating inputs"
|
||||
It "accepts valid image name"
|
||||
When call validate_input_python "docker-publish-gh" "image-name" "myapp"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid GitHub token"
|
||||
When call validate_input_python "docker-publish-gh" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid tags"
|
||||
When call validate_input_python "docker-publish-gh" "tags" "v1.0.0,latest"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in token"
|
||||
When call validate_input_python "docker-publish-gh" "token" "ghp_123;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*Docker*"
|
||||
End
|
||||
End
|
||||
End
|
||||
48
_tests/unit/docker-publish-hub/validation.spec.sh
Executable file
48
_tests/unit/docker-publish-hub/validation.spec.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for docker-publish-hub action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "docker-publish-hub action"
|
||||
ACTION_DIR="docker-publish-hub"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating inputs"
|
||||
It "accepts valid image name"
|
||||
When call validate_input_python "docker-publish-hub" "image-name" "myapp"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "docker-publish-hub" "username" "dockeruser"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid password"
|
||||
When call validate_input_python "docker-publish-hub" "password" "secretpassword123"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid tags"
|
||||
When call validate_input_python "docker-publish-hub" "tags" "v1.0.0,latest"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in username"
|
||||
When call validate_input_python "docker-publish-hub" "username" "user;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects injection in password"
|
||||
When call validate_input_python "docker-publish-hub" "password" "pass;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*Docker*"
|
||||
End
|
||||
End
|
||||
End
|
||||
48
_tests/unit/docker-publish/validation.spec.sh
Executable file
48
_tests/unit/docker-publish/validation.spec.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for docker-publish action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "docker-publish action"
|
||||
ACTION_DIR="docker-publish"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating inputs"
|
||||
It "accepts valid registry"
|
||||
When call validate_input_python "docker-publish" "registry" "dockerhub"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts github registry"
|
||||
When call validate_input_python "docker-publish" "registry" "github"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts both registry"
|
||||
When call validate_input_python "docker-publish" "registry" "both"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects empty registry input"
|
||||
When call validate_input_python "docker-publish" "registry" ""
|
||||
The status should be failure
|
||||
End
|
||||
It "accepts boolean values for nightly"
|
||||
When call validate_input_python "docker-publish" "nightly" "true"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid platforms format"
|
||||
When call validate_input_python "docker-publish" "platforms" "linux/amd64,linux/arm64"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*Docker*"
|
||||
End
|
||||
End
|
||||
End
|
||||
85
_tests/unit/dotnet-version-detect/validation.spec.sh
Executable file
85
_tests/unit/dotnet-version-detect/validation.spec.sh
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for dotnet-version-detect action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "dotnet-version-detect action"
|
||||
ACTION_DIR="dotnet-version-detect"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating default-version input"
|
||||
It "accepts valid dotnet version"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts full semantic version"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "8.0.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts dotnet 6 version"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "6.0.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts dotnet 7 version"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "7.0.0"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects version with leading zeros"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "08.0.0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects unsupported version"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "2.0.0"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Dotnet Version Detect"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "default-version"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "dotnet-version"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects injection in version"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "8.0;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates version security"
|
||||
When call validate_input_python "dotnet-version-detect" "default-version" "8.0&&malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "default-version" "8.0"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: dotnet-version-detect"
|
||||
The stderr should include "Output test passed for: dotnet-version-detect"
|
||||
End
|
||||
End
|
||||
End
|
||||
355
_tests/unit/eslint-check/validation.spec.sh
Executable file
355
_tests/unit/eslint-check/validation.spec.sh
Executable file
@@ -0,0 +1,355 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for eslint-check action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "eslint-check action"
|
||||
ACTION_DIR="eslint-check"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating working-directory input"
|
||||
It "accepts current directory"
|
||||
When call validate_input_python "eslint-check" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
It "accepts relative path"
|
||||
When call validate_input_python "eslint-check" "working-directory" "src/frontend"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts nested directory"
|
||||
When call validate_input_python "eslint-check" "working-directory" "packages/ui"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "eslint-check" "working-directory" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects absolute paths"
|
||||
When call validate_input_python "eslint-check" "working-directory" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects injection attempts"
|
||||
When call validate_input_python "eslint-check" "working-directory" "src; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating eslint-version input"
|
||||
It "accepts latest version"
|
||||
When call validate_input_python "eslint-check" "eslint-version" "latest"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts semantic version"
|
||||
When call validate_input_python "eslint-check" "eslint-version" "8.57.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts version with prerelease"
|
||||
When call validate_input_python "eslint-check" "eslint-version" "9.0.0-alpha.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts older stable version"
|
||||
When call validate_input_python "eslint-check" "eslint-version" "7.32.0"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "eslint-check" "eslint-version" "8.57"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects version with letters"
|
||||
When call validate_input_python "eslint-check" "eslint-version" "8.57.0a"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "eslint-check" "eslint-version" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating config-file input"
|
||||
It "accepts default eslintrc"
|
||||
When call validate_input_python "eslint-check" "config-file" ".eslintrc"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts eslintrc.json"
|
||||
When call validate_input_python "eslint-check" "config-file" ".eslintrc.json"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts eslint.config.js"
|
||||
When call validate_input_python "eslint-check" "config-file" "eslint.config.js"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts relative path config"
|
||||
When call validate_input_python "eslint-check" "config-file" "config/eslint.json"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "eslint-check" "config-file" "../../../malicious.js"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects injection in config path"
|
||||
When call validate_input_python "eslint-check" "config-file" "config.js;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating ignore-file input"
|
||||
It "accepts default eslintignore"
|
||||
When call validate_input_python "eslint-check" "ignore-file" ".eslintignore"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts custom ignore file"
|
||||
When call validate_input_python "eslint-check" "ignore-file" "eslint-ignore.txt"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts relative path ignore file"
|
||||
When call validate_input_python "eslint-check" "ignore-file" "config/.eslintignore"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "eslint-check" "ignore-file" "../../sensitive.txt"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating file-extensions input"
|
||||
It "accepts default extensions"
|
||||
When call validate_input_python "eslint-check" "file-extensions" ".js,.jsx,.ts,.tsx"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts single extension"
|
||||
When call validate_input_python "eslint-check" "file-extensions" ".js"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts TypeScript extensions only"
|
||||
When call validate_input_python "eslint-check" "file-extensions" ".ts,.tsx"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts Vue and JavaScript extensions"
|
||||
When call validate_input_python "eslint-check" "file-extensions" ".js,.vue,.ts"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects extensions without dots"
|
||||
When call validate_input_python "eslint-check" "file-extensions" "js,ts"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects invalid extension format"
|
||||
When call validate_input_python "eslint-check" "file-extensions" ".js;.ts"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects extensions with special characters"
|
||||
When call validate_input_python "eslint-check" "file-extensions" ".js,.t$"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating boolean inputs"
|
||||
It "accepts cache as true"
|
||||
When call validate_input_python "eslint-check" "cache" "true"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts cache as false"
|
||||
When call validate_input_python "eslint-check" "cache" "false"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts fail-on-error as true"
|
||||
When call validate_input_python "eslint-check" "fail-on-error" "true"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts fail-on-error as false"
|
||||
When call validate_input_python "eslint-check" "fail-on-error" "false"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid boolean value"
|
||||
When call validate_input_python "eslint-check" "cache" "maybe"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects numeric boolean"
|
||||
When call validate_input_python "eslint-check" "fail-on-error" "1"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating numeric inputs"
|
||||
It "accepts zero max-warnings"
|
||||
When call validate_input_python "eslint-check" "max-warnings" "0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts reasonable max-warnings"
|
||||
When call validate_input_python "eslint-check" "max-warnings" "10"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts large max-warnings"
|
||||
When call validate_input_python "eslint-check" "max-warnings" "1000"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid max-retries"
|
||||
When call validate_input_python "eslint-check" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts minimum retries"
|
||||
When call validate_input_python "eslint-check" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts maximum retries"
|
||||
When call validate_input_python "eslint-check" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects negative max-warnings"
|
||||
When call validate_input_python "eslint-check" "max-warnings" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects non-numeric max-warnings"
|
||||
When call validate_input_python "eslint-check" "max-warnings" "many"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "eslint-check" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects retries above limit"
|
||||
When call validate_input_python "eslint-check" "max-retries" "15"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating report-format input"
|
||||
It "accepts stylish format"
|
||||
When call validate_input_python "eslint-check" "report-format" "stylish"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts json format"
|
||||
When call validate_input_python "eslint-check" "report-format" "json"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts sarif format"
|
||||
When call validate_input_python "eslint-check" "report-format" "sarif"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts checkstyle format"
|
||||
When call validate_input_python "eslint-check" "report-format" "checkstyle"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts compact format"
|
||||
When call validate_input_python "eslint-check" "report-format" "compact"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts html format"
|
||||
When call validate_input_python "eslint-check" "report-format" "html"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts junit format"
|
||||
When call validate_input_python "eslint-check" "report-format" "junit"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts tap format"
|
||||
When call validate_input_python "eslint-check" "report-format" "tap"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts unix format"
|
||||
When call validate_input_python "eslint-check" "report-format" "unix"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid format"
|
||||
When call validate_input_python "eslint-check" "report-format" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects empty format"
|
||||
When call validate_input_python "eslint-check" "report-format" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "ESLint Check"
|
||||
End
|
||||
|
||||
It "defines required inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "working-directory"
|
||||
The output should include "eslint-version"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines optional inputs with defaults"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "config-file"
|
||||
The output should include "ignore-file"
|
||||
The output should include "file-extensions"
|
||||
The output should include "cache"
|
||||
The output should include "max-warnings"
|
||||
The output should include "fail-on-error"
|
||||
The output should include "report-format"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "error-count"
|
||||
The output should include "warning-count"
|
||||
The output should include "sarif-file"
|
||||
The output should include "files-checked"
|
||||
End
|
||||
|
||||
It "has composite run type"
|
||||
When call grep -q "using: composite" "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "includes input validation step"
|
||||
When call grep -q "Validate Inputs" "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "uses node-setup action"
|
||||
When call grep -q "./node-setup" "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "uses common-cache action"
|
||||
When call grep -q "./common-cache" "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "validates input paths to prevent injection"
|
||||
When call validate_input_python "eslint-check" "working-directory" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates config file paths"
|
||||
When call validate_input_python "eslint-check" "config-file" "../../malicious.js"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "sanitizes file extensions input"
|
||||
When call validate_input_python "eslint-check" "file-extensions" ".js;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs"
|
||||
When call test_action_outputs "$ACTION_DIR" "working-directory" "." "eslint-version" "latest" "max-retries" "3"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: eslint-check"
|
||||
The stderr should include "Output test passed for: eslint-check"
|
||||
End
|
||||
|
||||
It "outputs consistent error and warning counts"
|
||||
When call test_action_outputs "$ACTION_DIR" "max-warnings" "0" "report-format" "sarif"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: eslint-check"
|
||||
The stderr should include "Output test passed for: eslint-check"
|
||||
End
|
||||
End
|
||||
End
|
||||
115
_tests/unit/eslint-fix/validation.spec.sh
Executable file
115
_tests/unit/eslint-fix/validation.spec.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for eslint-fix action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "eslint-fix action"
|
||||
ACTION_DIR="eslint-fix"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token"
|
||||
When call validate_input_python "eslint-fix" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in token"
|
||||
When call validate_input_python "eslint-fix" "token" "token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "eslint-fix" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in username"
|
||||
When call validate_input_python "eslint-fix" "username" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "eslint-fix" "email" "test@example.com"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid email format"
|
||||
When call validate_input_python "eslint-fix" "email" "invalid-email"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating numeric inputs"
|
||||
It "accepts valid max-retries"
|
||||
When call validate_input_python "eslint-fix" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts minimum retries"
|
||||
When call validate_input_python "eslint-fix" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts maximum retries"
|
||||
When call validate_input_python "eslint-fix" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "eslint-fix" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects retries above limit"
|
||||
When call validate_input_python "eslint-fix" "max-retries" "15"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "ESLint Fix"
|
||||
End
|
||||
|
||||
It "defines required inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "files_changed"
|
||||
The output should include "lint_status"
|
||||
The output should include "errors_fixed"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "validates token format"
|
||||
When call validate_input_python "eslint-fix" "token" "invalid-token;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates email format"
|
||||
When call validate_input_python "eslint-fix" "email" "invalid@email"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs"
|
||||
When call test_action_outputs "$ACTION_DIR" "token" "ghp_test" "username" "test" "email" "test@example.com" "max-retries" "3"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: eslint-fix"
|
||||
The stderr should include "Output test passed for: eslint-fix"
|
||||
End
|
||||
End
|
||||
End
|
||||
141
_tests/unit/github-release/validation.spec.sh
Executable file
141
_tests/unit/github-release/validation.spec.sh
Executable file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for github-release action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
# Using the centralized validate_input_python function from spec_helper.sh
|
||||
|
||||
Describe "github-release action"
|
||||
ACTION_DIR="github-release"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating version input"
|
||||
It "accepts valid semantic version"
|
||||
When call validate_input_python "github-release" "version" "1.2.3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts semantic version with v prefix"
|
||||
When call validate_input_python "github-release" "version" "v1.2.3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts prerelease version"
|
||||
When call validate_input_python "github-release" "version" "1.2.3-alpha"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts build metadata version"
|
||||
When call validate_input_python "github-release" "version" "1.2.3+build.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts prerelease with build metadata"
|
||||
When call validate_input_python "github-release" "version" "1.2.3-alpha.1+build.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts CalVer format"
|
||||
When call validate_input_python "github-release" "version" "2024.3.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "github-release" "version" "invalid-version"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "github-release" "version" "1.2.3; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "github-release" "version" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating changelog input"
|
||||
It "accepts empty changelog"
|
||||
When call validate_input_python "github-release" "changelog" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts normal changelog content"
|
||||
When call validate_input_python "github-release" "changelog" "## What's Changed\n- Fixed bug #123\n- Added feature X"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts changelog with special characters"
|
||||
When call validate_input_python "github-release" "changelog" "Version 1.2.3\n\n- Bug fixes & improvements\n- Added @mention support"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects changelog with command injection"
|
||||
When call validate_input_python "github-release" "changelog" "Release notes; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects changelog with shell expansion"
|
||||
When call validate_input_python "github-release" "changelog" "Release \$(whoami) notes"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "GitHub Release"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "version"
|
||||
The output should include "changelog"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "release_url"
|
||||
The output should include "release_id"
|
||||
The output should include "upload_url"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "requires version input"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "version"
|
||||
End
|
||||
|
||||
It "has changelog as optional input"
|
||||
# Test that changelog has a default value in action.yml
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "changelog" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in version"
|
||||
When call validate_input_python "github-release" "version" "../1.2.3"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in version"
|
||||
When call validate_input_python "github-release" "version" "1.2.3|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in changelog"
|
||||
When call validate_input_python "github-release" "changelog" "Release notes|echo test"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
173
_tests/unit/go-build/validation.spec.sh
Executable file
173
_tests/unit/go-build/validation.spec.sh
Executable file
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for go-build action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "go-build action"
|
||||
ACTION_DIR="go-build"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating go-version input"
|
||||
It "accepts valid Go version"
|
||||
When call validate_input_python "go-build" "go-version" "1.21.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts Go version with v prefix"
|
||||
When call validate_input_python "go-build" "go-version" "v1.21.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts newer Go version"
|
||||
When call validate_input_python "go-build" "go-version" "1.22.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts prerelease Go version"
|
||||
When call validate_input_python "go-build" "go-version" "1.21.0-rc1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid Go version format"
|
||||
When call validate_input_python "go-build" "go-version" "invalid-version"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects Go version with command injection"
|
||||
When call validate_input_python "go-build" "go-version" "1.21; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating destination input"
|
||||
It "accepts valid relative path"
|
||||
When call validate_input_python "go-build" "destination" "./bin"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts nested directory path"
|
||||
When call validate_input_python "go-build" "destination" "build/output"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts simple directory name"
|
||||
When call validate_input_python "go-build" "destination" "dist"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal in destination"
|
||||
When call validate_input_python "go-build" "destination" "../bin"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute path"
|
||||
When call validate_input_python "go-build" "destination" "/usr/bin"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects destination with command injection"
|
||||
When call validate_input_python "go-build" "destination" "./bin; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "go-build" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum retry count"
|
||||
When call validate_input_python "go-build" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts maximum retry count"
|
||||
When call validate_input_python "go-build" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects retry count below minimum"
|
||||
When call validate_input_python "go-build" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects retry count above maximum"
|
||||
When call validate_input_python "go-build" "max-retries" "15"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric retry count"
|
||||
When call validate_input_python "go-build" "max-retries" "many"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects decimal retry count"
|
||||
When call validate_input_python "go-build" "max-retries" "3.5"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Go Build"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "go-version"
|
||||
The output should include "destination"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "build_status"
|
||||
The output should include "test_status"
|
||||
The output should include "go_version"
|
||||
The output should include "binary_path"
|
||||
The output should include "coverage_path"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input defaults"
|
||||
It "has default destination"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "destination" "default"
|
||||
The output should equal "./bin"
|
||||
End
|
||||
|
||||
It "has default max-retries"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "max-retries" "default"
|
||||
The output should equal "3"
|
||||
End
|
||||
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against shell injection in go-version"
|
||||
When call validate_input_python "go-build" "go-version" "1.21.0|echo test"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell injection in destination"
|
||||
When call validate_input_python "go-build" "destination" "bin\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell injection in max-retries"
|
||||
When call validate_input_python "go-build" "max-retries" "3;echo test"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
255
_tests/unit/go-lint/validation.spec.sh
Executable file
255
_tests/unit/go-lint/validation.spec.sh
Executable file
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for go-lint action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "go-lint action"
|
||||
ACTION_DIR="go-lint"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating working-directory input"
|
||||
It "accepts current directory"
|
||||
When call validate_input_python "go-lint" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts relative directory path"
|
||||
When call validate_input_python "go-lint" "working-directory" "src/main"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "go-lint" "working-directory" "../src"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute path"
|
||||
When call validate_input_python "go-lint" "working-directory" "/usr/src"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating golangci-lint-version input"
|
||||
It "accepts latest version"
|
||||
When call validate_input_python "go-lint" "golangci-lint-version" "latest"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts semantic version"
|
||||
When call validate_input_python "go-lint" "golangci-lint-version" "1.55.2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts semantic version with v prefix"
|
||||
When call validate_input_python "go-lint" "golangci-lint-version" "v1.55.2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "go-lint" "golangci-lint-version" "invalid-version"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating go-version input"
|
||||
It "accepts stable version"
|
||||
When call validate_input_python "go-lint" "go-version" "stable"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts major.minor version"
|
||||
When call validate_input_python "go-lint" "go-version" "1.21"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts full semantic version"
|
||||
When call validate_input_python "go-lint" "go-version" "1.21.5"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid Go version"
|
||||
When call validate_input_python "go-lint" "go-version" "go1.21"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating config-file input"
|
||||
It "accepts default config file"
|
||||
When call validate_input_python "go-lint" "config-file" ".golangci.yml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts custom config file path"
|
||||
When call validate_input_python "go-lint" "config-file" "configs/golangci.yaml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal in config file"
|
||||
When call validate_input_python "go-lint" "config-file" "../configs/golangci.yml"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating timeout input"
|
||||
It "accepts timeout in minutes"
|
||||
When call validate_input_python "go-lint" "timeout" "5m"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts timeout in seconds"
|
||||
When call validate_input_python "go-lint" "timeout" "300s"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts timeout in hours"
|
||||
When call validate_input_python "go-lint" "timeout" "1h"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects timeout without unit"
|
||||
When call validate_input_python "go-lint" "timeout" "300"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid timeout format"
|
||||
When call validate_input_python "go-lint" "timeout" "5 minutes"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating boolean inputs"
|
||||
It "accepts true for cache"
|
||||
When call validate_input_python "go-lint" "cache" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for cache"
|
||||
When call validate_input_python "go-lint" "cache" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid boolean for fail-on-error"
|
||||
When call validate_input_python "go-lint" "fail-on-error" "maybe"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts true for only-new-issues"
|
||||
When call validate_input_python "go-lint" "only-new-issues" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for disable-all"
|
||||
When call validate_input_python "go-lint" "disable-all" "false"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating report-format input"
|
||||
It "accepts sarif format"
|
||||
When call validate_input_python "go-lint" "report-format" "sarif"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts json format"
|
||||
When call validate_input_python "go-lint" "report-format" "json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts github-actions format"
|
||||
When call validate_input_python "go-lint" "report-format" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid report format"
|
||||
When call validate_input_python "go-lint" "report-format" "invalid-format"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "go-lint" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum retry count"
|
||||
When call validate_input_python "go-lint" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts maximum retry count"
|
||||
When call validate_input_python "go-lint" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects retry count below minimum"
|
||||
When call validate_input_python "go-lint" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects retry count above maximum"
|
||||
When call validate_input_python "go-lint" "max-retries" "15"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating linter lists"
|
||||
It "accepts valid enable-linters list"
|
||||
When call validate_input_python "go-lint" "enable-linters" "gosec,govet,staticcheck"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts single linter in enable-linters"
|
||||
When call validate_input_python "go-lint" "enable-linters" "gosec"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid disable-linters list"
|
||||
When call validate_input_python "go-lint" "disable-linters" "exhaustivestruct,interfacer"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid linter list format"
|
||||
When call validate_input_python "go-lint" "enable-linters" "gosec, govet"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Go Lint Check"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "error-count"
|
||||
The output should include "sarif-file"
|
||||
The output should include "cache-hit"
|
||||
The output should include "analyzed-files"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against command injection in working-directory"
|
||||
When call validate_input_python "go-lint" "working-directory" "src; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in config-file"
|
||||
When call validate_input_python "go-lint" "config-file" "config.yml\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell expansion in enable-linters"
|
||||
When call validate_input_python "go-lint" "enable-linters" "gosec,\$(echo malicious)"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
171
_tests/unit/go-version-detect/validation.spec.sh
Executable file
171
_tests/unit/go-version-detect/validation.spec.sh
Executable file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for go-version-detect action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "go-version-detect action"
|
||||
ACTION_DIR="go-version-detect"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
# Test version constants (update these when Go releases new versions)
|
||||
CURRENT_STABLE_GO_VERSION="1.25"
|
||||
CURRENT_STABLE_GO_PATCH="1.25.0"
|
||||
PREVIOUS_GO_VERSION="1.24.0"
|
||||
MIN_SUPPORTED_GO_VERSION="1.18"
|
||||
MAX_SUPPORTED_GO_VERSION="1.30"
|
||||
TOO_OLD_GO_VERSION="1.17"
|
||||
TOO_NEW_GO_VERSION="1.31"
|
||||
|
||||
Context "when validating default-version input"
|
||||
It "accepts valid semantic version"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$CURRENT_STABLE_GO_VERSION"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts semantic version with patch"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$PREVIOUS_GO_VERSION"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum supported Go version"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$MIN_SUPPORTED_GO_VERSION"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts current stable Go version"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$CURRENT_STABLE_GO_PATCH"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects version without minor"
|
||||
When call validate_input_python "go-version-detect" "default-version" "1"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "go-version-detect" "default-version" "invalid-version"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "go-version-detect" "default-version" "${CURRENT_STABLE_GO_VERSION}; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with shell expansion"
|
||||
When call validate_input_python "go-version-detect" "default-version" "${CURRENT_STABLE_GO_VERSION}\$(echo test)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects major version other than 1"
|
||||
When call validate_input_python "go-version-detect" "default-version" "2.0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects too old minor version"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$TOO_OLD_GO_VERSION"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects too new minor version"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$TOO_NEW_GO_VERSION"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "go-version-detect" "default-version" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with leading v"
|
||||
When call validate_input_python "go-version-detect" "default-version" "v${CURRENT_STABLE_GO_VERSION}"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with prerelease"
|
||||
When call validate_input_python "go-version-detect" "default-version" "${CURRENT_STABLE_GO_VERSION}-beta"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Go Version Detect"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "default-version"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "go-version"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has default-version as optional input"
|
||||
# Test that default-version has a default value in action.yml
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "default-version" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
|
||||
It "has correct default version"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "default-version" "default"
|
||||
The output should equal "$CURRENT_STABLE_GO_VERSION"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in version"
|
||||
When call validate_input_python "go-version-detect" "default-version" "../${CURRENT_STABLE_GO_VERSION}"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in version"
|
||||
When call validate_input_python "go-version-detect" "default-version" "${CURRENT_STABLE_GO_VERSION}|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection"
|
||||
When call validate_input_python "go-version-detect" "default-version" "${CURRENT_STABLE_GO_VERSION}\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion"
|
||||
When call validate_input_python "go-version-detect" "default-version" "${CURRENT_STABLE_GO_VERSION}\${HOME}"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing version range validation"
|
||||
It "validates reasonable Go version range boundaries"
|
||||
# Test boundary conditions for Go version validation
|
||||
When call validate_input_python "go-version-detect" "default-version" "$TOO_OLD_GO_VERSION"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates upper boundary"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$TOO_NEW_GO_VERSION"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates exact boundary valid values"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$MIN_SUPPORTED_GO_VERSION"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates exact boundary valid values upper"
|
||||
When call validate_input_python "go-version-detect" "default-version" "$MAX_SUPPORTED_GO_VERSION"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
End
|
||||
242
_tests/unit/node-setup/validation.spec.sh
Executable file
242
_tests/unit/node-setup/validation.spec.sh
Executable file
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for node-setup action
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "node-setup action"
|
||||
ACTION_DIR="node-setup"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
# Framework is automatically initialized via spec_helper.sh
|
||||
|
||||
Context "when validating inputs"
|
||||
It "accepts valid Node.js version"
|
||||
When call validate_input_python "node-setup" "default-version" "18.17.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid package manager"
|
||||
When call validate_input_python "node-setup" "package-manager" "npm"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts yarn as package manager"
|
||||
When call validate_input_python "node-setup" "package-manager" "yarn"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts pnpm as package manager"
|
||||
When call validate_input_python "node-setup" "package-manager" "pnpm"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts bun as package manager"
|
||||
When call validate_input_python "node-setup" "package-manager" "bun"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid package manager"
|
||||
When call validate_input_python "node-setup" "package-manager" "invalid-manager"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects malformed Node.js version"
|
||||
When call validate_input_python "node-setup" "default-version" "not-a-version"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects command injection in inputs"
|
||||
When call validate_input_python "node-setup" "default-version" "18.0.0; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
When call get_action_name "$ACTION_FILE"
|
||||
The output should equal "Node Setup"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "default-version"
|
||||
The output should include "package-manager"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "node-version"
|
||||
The output should include "package-manager"
|
||||
The output should include "cache-hit"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing Node.js version detection"
|
||||
BeforeEach "shellspec_setup_test_env 'node-version-detection'"
|
||||
AfterEach "shellspec_cleanup_test_env 'node-version-detection'"
|
||||
|
||||
It "detects version from package.json engines field"
|
||||
create_mock_node_repo
|
||||
|
||||
# Mock action output based on package.json
|
||||
echo "node-version=18.0.0" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "node-version" "18.0.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "detects version from .nvmrc file"
|
||||
create_mock_node_repo
|
||||
echo "18.17.1" >.nvmrc
|
||||
|
||||
# Mock action output
|
||||
echo "node-version=18.17.1" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "node-version" "18.17.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "uses default version when none specified"
|
||||
create_mock_node_repo
|
||||
# Remove engines field simulation
|
||||
|
||||
# Mock default version output
|
||||
echo "node-version=20.0.0" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "node-version" "20.0.0"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing package manager detection"
|
||||
BeforeEach "shellspec_setup_test_env 'package-manager-detection'"
|
||||
AfterEach "shellspec_cleanup_test_env 'package-manager-detection'"
|
||||
|
||||
It "detects bun from bun.lockb"
|
||||
create_mock_node_repo
|
||||
touch bun.lockb
|
||||
|
||||
echo "package-manager=bun" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "package-manager" "bun"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "detects pnpm from pnpm-lock.yaml"
|
||||
create_mock_node_repo
|
||||
touch pnpm-lock.yaml
|
||||
|
||||
echo "package-manager=pnpm" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "package-manager" "pnpm"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "detects yarn from yarn.lock"
|
||||
create_mock_node_repo
|
||||
touch yarn.lock
|
||||
|
||||
echo "package-manager=yarn" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "package-manager" "yarn"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "detects npm from package-lock.json"
|
||||
create_mock_node_repo
|
||||
touch package-lock.json
|
||||
|
||||
echo "package-manager=npm" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "package-manager" "npm"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "detects packageManager field from package.json"
|
||||
create_mock_node_repo
|
||||
|
||||
# Add packageManager field to package.json
|
||||
cat >package.json <<EOF
|
||||
{
|
||||
"name": "test-project",
|
||||
"version": "1.0.0",
|
||||
"packageManager": "pnpm@8.0.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "package-manager=pnpm" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "package-manager" "pnpm"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing Corepack integration"
|
||||
BeforeEach "shellspec_setup_test_env 'corepack-test'"
|
||||
AfterEach "shellspec_cleanup_test_env 'corepack-test'"
|
||||
|
||||
It "enables Corepack when packageManager is specified"
|
||||
create_mock_node_repo
|
||||
|
||||
# Simulate packageManager field
|
||||
cat >package.json <<EOF
|
||||
{
|
||||
"name": "test-project",
|
||||
"version": "1.0.0",
|
||||
"packageManager": "yarn@3.6.0"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Mock Corepack enabled output
|
||||
echo "corepack-enabled=true" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "corepack-enabled" "true"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing cache functionality"
|
||||
BeforeEach "shellspec_setup_test_env 'cache-test'"
|
||||
AfterEach "shellspec_cleanup_test_env 'cache-test'"
|
||||
|
||||
It "reports cache hit when dependencies are cached"
|
||||
create_mock_node_repo
|
||||
touch package-lock.json
|
||||
mkdir -p node_modules
|
||||
|
||||
# Mock cache hit
|
||||
echo "cache-hit=true" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "cache-hit" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "reports cache miss when no cache exists"
|
||||
create_mock_node_repo
|
||||
touch package-lock.json
|
||||
|
||||
# Mock cache miss
|
||||
echo "cache-hit=false" >>"$GITHUB_OUTPUT"
|
||||
|
||||
When call shellspec_validate_action_output "cache-hit" "false"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing output consistency"
|
||||
It "produces all expected outputs"
|
||||
When call test_action_outputs "$ACTION_DIR" "node-version" "18.0.0" "package-manager" "npm"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: node-setup"
|
||||
The stderr should include "Output test passed for: node-setup"
|
||||
End
|
||||
End
|
||||
End
|
||||
216
_tests/unit/npm-publish/validation.spec.sh
Executable file
216
_tests/unit/npm-publish/validation.spec.sh
Executable file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for npm-publish action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "npm-publish action"
|
||||
ACTION_DIR="npm-publish"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating registry-url input"
|
||||
It "accepts valid https registry URL"
|
||||
When call validate_input_python "npm-publish" "registry-url" "https://registry.npmjs.org/"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts https registry URL without trailing slash"
|
||||
When call validate_input_python "npm-publish" "registry-url" "https://registry.npmjs.org"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts http registry URL"
|
||||
When call validate_input_python "npm-publish" "registry-url" "http://localhost:4873"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts registry URL with path"
|
||||
When call validate_input_python "npm-publish" "registry-url" "https://npm.example.com/registry/"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects non-http(s) URL"
|
||||
When call validate_input_python "npm-publish" "registry-url" "ftp://registry.example.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid URL format"
|
||||
When call validate_input_python "npm-publish" "registry-url" "not-a-url"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating npm_token input"
|
||||
It "accepts valid GitHub token format (exact length)"
|
||||
When call validate_input_python "npm-publish" "npm_token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid NPM classic token format"
|
||||
When call validate_input_python "npm-publish" "npm_token" "npm_1234567890123456789012345678901234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token (exact length)"
|
||||
When call validate_input_python "npm-publish" "npm_token" "github_pat_1234567890123456789012345678901234567890123456789012345678901234567890a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "npm-publish" "npm_token" "invalid-token-format"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty token"
|
||||
When call validate_input_python "npm-publish" "npm_token" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "npm-publish" "npm_token" "ghp_123456789012345678901234567890123456; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating scope input"
|
||||
It "accepts valid npm scope"
|
||||
When call validate_input_python "npm-publish" "scope" "@myorg"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts scope with hyphens"
|
||||
When call validate_input_python "npm-publish" "scope" "@my-organization"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts scope with numbers"
|
||||
When call validate_input_python "npm-publish" "scope" "@myorg123"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects scope without @ prefix"
|
||||
When call validate_input_python "npm-publish" "scope" "myorg"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects scope with invalid characters"
|
||||
When call validate_input_python "npm-publish" "scope" "@my_org!"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects scope with command injection"
|
||||
When call validate_input_python "npm-publish" "scope" "@myorg; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating access input"
|
||||
It "accepts public access"
|
||||
When call validate_input_python "npm-publish" "access" "public"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts restricted access"
|
||||
When call validate_input_python "npm-publish" "access" "restricted"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts private access (no specific validation)"
|
||||
When call validate_input_python "npm-publish" "access" "private"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty access"
|
||||
When call validate_input_python "npm-publish" "access" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating provenance input"
|
||||
It "accepts true for provenance"
|
||||
When call validate_input_python "npm-publish" "provenance" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for provenance"
|
||||
When call validate_input_python "npm-publish" "provenance" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts any value for provenance (no specific validation)"
|
||||
When call validate_input_python "npm-publish" "provenance" "maybe"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating dry-run input"
|
||||
It "accepts true for dry-run"
|
||||
When call validate_input_python "npm-publish" "dry-run" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for dry-run"
|
||||
When call validate_input_python "npm-publish" "dry-run" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts any value for dry-run (no specific validation)"
|
||||
When call validate_input_python "npm-publish" "dry-run" "yes"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Publish to NPM"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "npm_token"
|
||||
The output should include "registry-url"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "registry-url"
|
||||
The output should include "scope"
|
||||
The output should include "package-version"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "requires npm_token input"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "npm_token"
|
||||
End
|
||||
|
||||
It "has registry-url as optional with default"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "registry-url" "default"
|
||||
The output should include "registry.npmjs.org"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in all inputs"
|
||||
When call validate_input_python "npm-publish" "scope" "@../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters"
|
||||
When call validate_input_python "npm-publish" "registry-url" "https://registry.npmjs.org|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command substitution"
|
||||
When call validate_input_python "npm-publish" "scope" "@\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
407
_tests/unit/php-composer/validation.spec.sh
Executable file
407
_tests/unit/php-composer/validation.spec.sh
Executable file
@@ -0,0 +1,407 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for php-composer action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "php-composer action"
|
||||
ACTION_DIR="php-composer"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating php input"
|
||||
It "accepts valid PHP version"
|
||||
When call validate_input_python "php-composer" "php" "8.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP version with patch"
|
||||
When call validate_input_python "php-composer" "php" "8.4.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 7.4"
|
||||
When call validate_input_python "php-composer" "php" "7.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 8.0"
|
||||
When call validate_input_python "php-composer" "php" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 8.1"
|
||||
When call validate_input_python "php-composer" "php" "8.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects PHP version too old"
|
||||
When call validate_input_python "php-composer" "php" "5.5"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "php-composer" "php" "php8.4"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "php-composer" "php" "8.4; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "php-composer" "php" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating extensions input"
|
||||
It "accepts valid PHP extensions"
|
||||
When call validate_input_python "php-composer" "extensions" "mbstring, xml, zip"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts single extension"
|
||||
When call validate_input_python "php-composer" "extensions" "mbstring"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts extensions without spaces"
|
||||
When call validate_input_python "php-composer" "extensions" "mbstring,xml,zip"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts extensions with underscores"
|
||||
When call validate_input_python "php-composer" "extensions" "pdo_mysql, gd_jpeg"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects extensions with special characters"
|
||||
When call validate_input_python "php-composer" "extensions" "mbstring@xml"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects extensions with command injection"
|
||||
When call validate_input_python "php-composer" "extensions" "mbstring; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty extensions"
|
||||
When call validate_input_python "php-composer" "extensions" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating tools input"
|
||||
It "accepts valid Composer tools"
|
||||
When call validate_input_python "php-composer" "tools" "composer:v2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts multiple tools"
|
||||
When call validate_input_python "php-composer" "tools" "composer:v2, phpunit:^9.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts tools with version constraints"
|
||||
When call validate_input_python "php-composer" "tools" "phpcs, phpstan:1.10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts tools with stability flags (@ allowed)"
|
||||
When call validate_input_python "php-composer" "tools" "dev-master@dev"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts tools with version and stability flag"
|
||||
When call validate_input_python "php-composer" "tools" "monolog/monolog@dev"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects tools with backticks"
|
||||
When call validate_input_python "php-composer" "tools" "composer\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects tools with command injection"
|
||||
When call validate_input_python "php-composer" "tools" "composer; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty tools"
|
||||
When call validate_input_python "php-composer" "tools" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating composer-version input"
|
||||
It "accepts composer version 1"
|
||||
When call validate_input_python "php-composer" "composer-version" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts composer version 2"
|
||||
When call validate_input_python "php-composer" "composer-version" "2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid composer version"
|
||||
When call validate_input_python "php-composer" "composer-version" "3"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric composer version"
|
||||
When call validate_input_python "php-composer" "composer-version" "latest"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty composer version"
|
||||
When call validate_input_python "php-composer" "composer-version" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating stability input"
|
||||
It "accepts stable"
|
||||
When call validate_input_python "php-composer" "stability" "stable"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts RC"
|
||||
When call validate_input_python "php-composer" "stability" "RC"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts beta"
|
||||
When call validate_input_python "php-composer" "stability" "beta"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts alpha"
|
||||
When call validate_input_python "php-composer" "stability" "alpha"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts dev"
|
||||
When call validate_input_python "php-composer" "stability" "dev"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid stability"
|
||||
When call validate_input_python "php-composer" "stability" "unstable"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects stability with injection"
|
||||
When call validate_input_python "php-composer" "stability" "stable; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating cache-directories input"
|
||||
It "accepts valid cache directory"
|
||||
When call validate_input_python "php-composer" "cache-directories" "vendor/cache"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts multiple cache directories"
|
||||
When call validate_input_python "php-composer" "cache-directories" "vendor/cache, .cache"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts directories with underscores and hyphens"
|
||||
When call validate_input_python "php-composer" "cache-directories" "cache_dir, cache-dir"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "php-composer" "cache-directories" "../malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute paths"
|
||||
When call validate_input_python "php-composer" "cache-directories" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects directories with command injection"
|
||||
When call validate_input_python "php-composer" "cache-directories" "cache; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty cache directories"
|
||||
When call validate_input_python "php-composer" "cache-directories" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call validate_input_python "php-composer" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token"
|
||||
When call validate_input_python "php-composer" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub app token"
|
||||
When call validate_input_python "php-composer" "token" "ghs_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "php-composer" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty token"
|
||||
When call validate_input_python "php-composer" "token" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "php-composer" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum retries"
|
||||
When call validate_input_python "php-composer" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts maximum retries"
|
||||
When call validate_input_python "php-composer" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "php-composer" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects too many retries"
|
||||
When call validate_input_python "php-composer" "max-retries" "11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric retries"
|
||||
When call validate_input_python "php-composer" "max-retries" "many"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects negative retries"
|
||||
When call validate_input_python "php-composer" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating args input"
|
||||
It "accepts valid Composer arguments"
|
||||
When call validate_input_python "php-composer" "args" "--no-progress --prefer-dist"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects empty args"
|
||||
When call validate_input_python "php-composer" "args" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects args with command injection"
|
||||
When call validate_input_python "php-composer" "args" "--no-progress; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects args with pipe"
|
||||
When call validate_input_python "php-composer" "args" "--no-progress | cat /etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Run Composer Install"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "php"
|
||||
The output should include "extensions"
|
||||
The output should include "tools"
|
||||
The output should include "args"
|
||||
The output should include "composer-version"
|
||||
The output should include "stability"
|
||||
The output should include "cache-directories"
|
||||
The output should include "token"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "lock"
|
||||
The output should include "php-version"
|
||||
The output should include "composer-version"
|
||||
The output should include "cache-hit"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "requires php input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "php" "required"
|
||||
The output should equal "required"
|
||||
End
|
||||
|
||||
It "has extensions as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "extensions" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in cache directories"
|
||||
When call validate_input_python "php-composer" "cache-directories" "../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in tools"
|
||||
When call validate_input_python "php-composer" "tools" "composer && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection in args"
|
||||
When call validate_input_python "php-composer" "args" "--no-progress \`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion in extensions"
|
||||
When call validate_input_python "php-composer" "extensions" "mbstring,\${HOME}"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing PHP-specific validations"
|
||||
It "validates PHP version boundaries"
|
||||
When call validate_input_python "php-composer" "php" "10.0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates Composer version enum restriction"
|
||||
When call validate_input_python "php-composer" "composer-version" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates stability enum values"
|
||||
When call validate_input_python "php-composer" "stability" "experimental"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
280
_tests/unit/php-laravel-phpunit/validation.spec.sh
Executable file
280
_tests/unit/php-laravel-phpunit/validation.spec.sh
Executable file
@@ -0,0 +1,280 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for php-laravel-phpunit action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "php-laravel-phpunit action"
|
||||
ACTION_DIR="php-laravel-phpunit"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating php-version input"
|
||||
It "accepts latest"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "latest"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid PHP version"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "8.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP version with patch"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "8.4.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 7.4"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "7.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 8.0"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "php8.4"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "8.4; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty version (uses default)"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating php-version-file input"
|
||||
It "accepts valid PHP version file"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" ".php-version"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts custom version file"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" "custom-php-version"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts version file with path"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" "config/.php-version"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal in version file"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute path in version file"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version file with command injection"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" ".php-version; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty version file"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating extensions input"
|
||||
It "accepts valid PHP extensions"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "mbstring, intl, json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts single extension"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "mbstring"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts extensions without spaces"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "mbstring,intl,json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts extensions with underscores"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "pdo_sqlite, pdo_mysql"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts extensions with numbers"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "sqlite3, gd2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects extensions with special characters"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "mbstring@intl"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects extensions with command injection"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "mbstring; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty extensions"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating coverage input"
|
||||
It "accepts none coverage"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "none"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts xdebug coverage"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "xdebug"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts pcov coverage"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "pcov"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts xdebug3 coverage"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "xdebug3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid coverage driver"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects coverage with command injection"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "none; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty coverage"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call validate_input_python "php-laravel-phpunit" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token"
|
||||
When call validate_input_python "php-laravel-phpunit" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub app token"
|
||||
When call validate_input_python "php-laravel-phpunit" "token" "ghs_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "php-laravel-phpunit" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty token"
|
||||
When call validate_input_python "php-laravel-phpunit" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Laravel Setup and Composer test"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "php-version"
|
||||
The output should include "php-version-file"
|
||||
The output should include "extensions"
|
||||
The output should include "coverage"
|
||||
The output should include "token"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "php-version"
|
||||
The output should include "php-version-file"
|
||||
The output should include "extensions"
|
||||
The output should include "coverage"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
|
||||
It "has correct default php-version"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "php-version" "default"
|
||||
The output should equal "latest"
|
||||
End
|
||||
|
||||
It "has correct default php-version-file"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "php-version-file" "default"
|
||||
The output should equal ".php-version"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in php-version-file"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" "../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in extensions"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "mbstring && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection in coverage"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "none\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion in php-version"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version" "8.4\${HOME}"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing Laravel-specific validations"
|
||||
It "validates coverage driver enum values"
|
||||
When call validate_input_python "php-laravel-phpunit" "coverage" "invalid-driver"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates php-version-file path safety"
|
||||
When call validate_input_python "php-laravel-phpunit" "php-version-file" "/etc/shadow"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates extensions format for Laravel requirements"
|
||||
When call validate_input_python "php-laravel-phpunit" "extensions" "mbstring, intl, json, pdo_sqlite, sqlite3"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
End
|
||||
249
_tests/unit/php-tests/validation.spec.sh
Executable file
249
_tests/unit/php-tests/validation.spec.sh
Executable file
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for php-tests action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "php-tests action"
|
||||
ACTION_DIR="php-tests"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call validate_input_python "php-tests" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token"
|
||||
When call validate_input_python "php-tests" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub app token"
|
||||
When call validate_input_python "php-tests" "token" "ghs_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub enterprise token"
|
||||
When call validate_input_python "php-tests" "token" "ghe_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "php-tests" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "php-tests" "token" "ghp_token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty token (uses default)"
|
||||
When call validate_input_python "php-tests" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid GitHub username"
|
||||
When call validate_input_python "php-tests" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with hyphens"
|
||||
When call validate_input_python "php-tests" "username" "user-name"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with numbers"
|
||||
When call validate_input_python "php-tests" "username" "user123"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts single character username"
|
||||
When call validate_input_python "php-tests" "username" "a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts maximum length username"
|
||||
When call validate_input_python "php-tests" "username" "abcdefghijklmnopqrstuvwxyz0123456789abc"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects username too long"
|
||||
When call validate_input_python "php-tests" "username" "abcdefghijklmnopqrstuvwxyz0123456789abcd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with command injection semicolon"
|
||||
When call validate_input_python "php-tests" "username" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with command injection ampersand"
|
||||
When call validate_input_python "php-tests" "username" "user && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with command injection pipe"
|
||||
When call validate_input_python "php-tests" "username" "user | rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty username (uses default)"
|
||||
When call validate_input_python "php-tests" "username" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "php-tests" "email" "user@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with subdomain"
|
||||
When call validate_input_python "php-tests" "email" "user@mail.example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with plus sign"
|
||||
When call validate_input_python "php-tests" "email" "user+tag@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with numbers"
|
||||
When call validate_input_python "php-tests" "email" "user123@example123.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with hyphens"
|
||||
When call validate_input_python "php-tests" "email" "user-name@example-domain.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects email without at symbol"
|
||||
When call validate_input_python "php-tests" "email" "userexample.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email without domain"
|
||||
When call validate_input_python "php-tests" "email" "user@"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email without username"
|
||||
When call validate_input_python "php-tests" "email" "@example.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email without dot in domain"
|
||||
When call validate_input_python "php-tests" "email" "user@example"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email with spaces"
|
||||
When call validate_input_python "php-tests" "email" "user @example.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty email (uses default)"
|
||||
When call validate_input_python "php-tests" "email" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "PHP Tests"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "test_status"
|
||||
The output should include "tests_run"
|
||||
The output should include "tests_passed"
|
||||
The output should include "coverage_path"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
|
||||
It "has empty default token (runtime fallback)"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "token" "default"
|
||||
The output should equal "no-default"
|
||||
End
|
||||
|
||||
It "has correct default username"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "username" "default"
|
||||
The output should equal "github-actions"
|
||||
End
|
||||
|
||||
It "has correct default email"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "email" "default"
|
||||
The output should equal "github-actions@github.com"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against command injection in username"
|
||||
When call validate_input_python "php-tests" "username" "user\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in email"
|
||||
When call validate_input_python "php-tests" "email" "user@example.com; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion in token"
|
||||
When call validate_input_python "php-tests" "token" "\${MALICIOUS_VAR}"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection in username"
|
||||
When call validate_input_python "php-tests" "username" "user\`echo malicious\`"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing PHP-specific validations"
|
||||
It "validates username length boundaries"
|
||||
When call validate_input_python "php-tests" "username" "$(awk 'BEGIN{for(i=1;i<=40;i++)printf "a"}')"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates email format for Git commits"
|
||||
When call validate_input_python "php-tests" "email" "noreply@github.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates default values are secure"
|
||||
When call validate_input_python "php-tests" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates default email is secure"
|
||||
When call validate_input_python "php-tests" "email" "github-actions@github.com"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
End
|
||||
170
_tests/unit/php-version-detect/validation.spec.sh
Executable file
170
_tests/unit/php-version-detect/validation.spec.sh
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for php-version-detect action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "php-version-detect action"
|
||||
ACTION_DIR="php-version-detect"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating default-version input"
|
||||
It "accepts valid PHP version"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP version with patch"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.3.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 7.4"
|
||||
When call validate_input_python "php-version-detect" "default-version" "7.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 8.0"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 8.1"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts PHP 8.4"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects PHP version too old"
|
||||
When call validate_input_python "php-version-detect" "default-version" "5.6"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects PHP version too new"
|
||||
When call validate_input_python "php-version-detect" "default-version" "10.0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "php-version-detect" "default-version" "php8.2"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.2; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version without minor"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "php-version-detect" "default-version" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with v prefix"
|
||||
When call validate_input_python "php-version-detect" "default-version" "v8.2"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts PHP 8.5 for future compatibility"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.5"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects unreasonably high minor version"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.100"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "PHP Version Detect"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "default-version"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "php-version"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has default-version as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "default-version" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
|
||||
It "has correct default version"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "default-version" "default"
|
||||
The output should equal "8.2"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in version"
|
||||
When call validate_input_python "php-version-detect" "default-version" "../8.2"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in version"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.2|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.2\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.2\${HOME}"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing PHP version range validation"
|
||||
It "validates PHP 7 minor version boundaries"
|
||||
When call validate_input_python "php-version-detect" "default-version" "7.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates PHP 7.4 specifically"
|
||||
When call validate_input_python "php-version-detect" "default-version" "7.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates PHP 8 minor version boundaries"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates PHP 8.4 boundary"
|
||||
When call validate_input_python "php-version-detect" "default-version" "8.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates PHP 9 future version"
|
||||
When call validate_input_python "php-version-detect" "default-version" "9.0"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
End
|
||||
90
_tests/unit/pr-lint/validation.spec.sh
Executable file
90
_tests/unit/pr-lint/validation.spec.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for pr-lint action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "pr-lint action"
|
||||
ACTION_DIR="pr-lint"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token"
|
||||
When call validate_input_python "pr-lint" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in token"
|
||||
When call validate_input_python "pr-lint" "token" "token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "pr-lint" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in username"
|
||||
When call validate_input_python "pr-lint" "username" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "pr-lint" "email" "test@example.com"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid email format"
|
||||
When call validate_input_python "pr-lint" "email" "invalid-email"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "PR Lint"
|
||||
End
|
||||
|
||||
It "defines required inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "validation_status"
|
||||
The output should include "errors_found"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "validates token format"
|
||||
When call validate_input_python "pr-lint" "token" "invalid-token;rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates email format"
|
||||
When call validate_input_python "pr-lint" "email" "invalid@email"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs"
|
||||
When call test_action_outputs "$ACTION_DIR" "token" "ghp_test" "username" "test" "email" "test@example.com"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: pr-lint"
|
||||
The stderr should include "Output test passed for: pr-lint"
|
||||
End
|
||||
End
|
||||
End
|
||||
172
_tests/unit/pre-commit/validation.spec.sh
Executable file
172
_tests/unit/pre-commit/validation.spec.sh
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for pre-commit action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "pre-commit action"
|
||||
ACTION_DIR="pre-commit"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating pre-commit-config input"
|
||||
It "accepts default config file"
|
||||
When call validate_input_python "pre-commit" "pre-commit-config" ".pre-commit-config.yaml"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts yml extension"
|
||||
When call validate_input_python "pre-commit" "pre-commit-config" ".pre-commit-config.yml"
|
||||
The status should be success
|
||||
End
|
||||
# NOTE: Test framework uses default validation for 'pre-commit-config' input
|
||||
# Default validation only checks for injection patterns (;, &&, $()
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "pre-commit" "pre-commit-config" "../config.yaml"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects absolute paths"
|
||||
When call validate_input_python "pre-commit" "pre-commit-config" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
It "accepts non-yaml extensions (framework default validation)"
|
||||
When call validate_input_python "pre-commit" "pre-commit-config" "config.txt"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection patterns"
|
||||
When call validate_input_python "pre-commit" "pre-commit-config" "config.yaml; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating base-branch input"
|
||||
It "accepts valid branch name"
|
||||
When call validate_input_python "pre-commit" "base-branch" "main"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts feature branch"
|
||||
When call validate_input_python "pre-commit" "base-branch" "feature/test-branch"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts branch with numbers"
|
||||
When call validate_input_python "pre-commit" "base-branch" "release-2024.1"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in branch"
|
||||
When call validate_input_python "pre-commit" "base-branch" "branch; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
# NOTE: Test framework uses default validation for 'base-branch'
|
||||
# Default validation only checks for injection patterns (;, &&, $()
|
||||
It "accepts branch with tilde (framework default validation)"
|
||||
When call validate_input_python "pre-commit" "base-branch" "branch~1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts branch starting with dot (framework default validation)"
|
||||
When call validate_input_python "pre-commit" "base-branch" ".hidden-branch"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection patterns in branch"
|
||||
When call validate_input_python "pre-commit" "base-branch" "branch && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token"
|
||||
When call validate_input_python "pre-commit" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "pre-commit" "token" "invalid-token-format"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects injection in token"
|
||||
When call validate_input_python "pre-commit" "token" "token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating commit_user input"
|
||||
It "accepts valid user"
|
||||
When call validate_input_python "pre-commit" "commit_user" "GitHub Actions"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in user"
|
||||
When call validate_input_python "pre-commit" "commit_user" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating commit_email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "pre-commit" "commit_email" "test@example.com"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts github-actions email"
|
||||
When call validate_input_python "pre-commit" "commit_email" "github-actions@github.com"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid email format"
|
||||
When call validate_input_python "pre-commit" "commit_email" "invalid-email"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "pre-commit"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "pre-commit-config"
|
||||
The output should include "base-branch"
|
||||
The output should include "token"
|
||||
The output should include "commit_user"
|
||||
The output should include "commit_email"
|
||||
End
|
||||
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "hooks_passed"
|
||||
The output should include "files_changed"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "pre-commit" "pre-commit-config" "../../malicious.yaml"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates branch name security"
|
||||
When call validate_input_python "pre-commit" "base-branch" "main && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates email format"
|
||||
When call validate_input_python "pre-commit" "commit_email" "invalid@email"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs"
|
||||
When call test_action_outputs "$ACTION_DIR" "pre-commit-config" ".pre-commit-config.yaml" "token" "ghp_test" "commit_user" "test" "commit_email" "test@example.com"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: pre-commit"
|
||||
The stderr should include "Output test passed for: pre-commit"
|
||||
End
|
||||
End
|
||||
End
|
||||
332
_tests/unit/prettier-check/validation.spec.sh
Executable file
332
_tests/unit/prettier-check/validation.spec.sh
Executable file
@@ -0,0 +1,332 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for prettier-check action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "prettier-check action"
|
||||
ACTION_DIR="prettier-check"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating working-directory input"
|
||||
It "accepts current directory"
|
||||
When call validate_input_python "prettier-check" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts relative directory"
|
||||
When call validate_input_python "prettier-check" "working-directory" "src"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts nested directory"
|
||||
When call validate_input_python "prettier-check" "working-directory" "src/components"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "prettier-check" "working-directory" "../malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute paths"
|
||||
When call validate_input_python "prettier-check" "working-directory" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects directory with command injection"
|
||||
When call validate_input_python "prettier-check" "working-directory" "src; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating prettier-version input"
|
||||
It "accepts latest version"
|
||||
When call validate_input_python "prettier-check" "prettier-version" "latest"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts semantic version"
|
||||
When call validate_input_python "prettier-check" "prettier-version" "3.0.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts prerelease version"
|
||||
When call validate_input_python "prettier-check" "prettier-version" "3.0.0-alpha"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "prettier-check" "prettier-version" "v3.0.0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "prettier-check" "prettier-version" "3.0.0; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating config-file input"
|
||||
It "accepts valid config file"
|
||||
When call validate_input_python "prettier-check" "config-file" ".prettierrc"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts config file with extension"
|
||||
When call validate_input_python "prettier-check" "config-file" ".prettierrc.json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts config file in subdirectory"
|
||||
When call validate_input_python "prettier-check" "config-file" "config/.prettierrc"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal in config file"
|
||||
When call validate_input_python "prettier-check" "config-file" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute path in config file"
|
||||
When call validate_input_python "prettier-check" "config-file" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating ignore-file input"
|
||||
It "accepts valid ignore file"
|
||||
When call validate_input_python "prettier-check" "ignore-file" ".prettierignore"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts ignore file in subdirectory"
|
||||
When call validate_input_python "prettier-check" "ignore-file" "config/.prettierignore"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal in ignore file"
|
||||
When call validate_input_python "prettier-check" "ignore-file" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute path in ignore file"
|
||||
When call validate_input_python "prettier-check" "ignore-file" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating file-pattern input"
|
||||
It "accepts valid glob pattern"
|
||||
When call validate_input_python "prettier-check" "file-pattern" "**/*.{js,ts}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts simple file pattern"
|
||||
When call validate_input_python "prettier-check" "file-pattern" "*.js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts multiple extensions"
|
||||
When call validate_input_python "prettier-check" "file-pattern" "**/*.{js,jsx,ts,tsx,css}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects pattern with path traversal"
|
||||
When call validate_input_python "prettier-check" "file-pattern" "../**/*.js"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects pattern with absolute path"
|
||||
When call validate_input_python "prettier-check" "file-pattern" "/etc/**/*.conf"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating boolean inputs"
|
||||
It "accepts true for cache"
|
||||
When call validate_input_python "prettier-check" "cache" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for cache"
|
||||
When call validate_input_python "prettier-check" "cache" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid cache value"
|
||||
When call validate_input_python "prettier-check" "cache" "yes"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts true for fail-on-error"
|
||||
When call validate_input_python "prettier-check" "fail-on-error" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for fail-on-error"
|
||||
When call validate_input_python "prettier-check" "fail-on-error" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts true for check-only"
|
||||
When call validate_input_python "prettier-check" "check-only" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for check-only"
|
||||
When call validate_input_python "prettier-check" "check-only" "false"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating report-format input"
|
||||
It "accepts json format"
|
||||
When call validate_input_python "prettier-check" "report-format" "json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts sarif format"
|
||||
When call validate_input_python "prettier-check" "report-format" "sarif"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid format"
|
||||
When call validate_input_python "prettier-check" "report-format" "xml"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty format"
|
||||
When call validate_input_python "prettier-check" "report-format" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "prettier-check" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum retries"
|
||||
When call validate_input_python "prettier-check" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts maximum retries"
|
||||
When call validate_input_python "prettier-check" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "prettier-check" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects too many retries"
|
||||
When call validate_input_python "prettier-check" "max-retries" "11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric retries"
|
||||
When call validate_input_python "prettier-check" "max-retries" "many"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating plugins input"
|
||||
It "accepts empty plugins"
|
||||
When call validate_input_python "prettier-check" "plugins" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid plugin name"
|
||||
When call validate_input_python "prettier-check" "plugins" "prettier-plugin-java"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts scoped plugin"
|
||||
When call validate_input_python "prettier-check" "plugins" "@prettier/plugin-xml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts multiple plugins"
|
||||
When call validate_input_python "prettier-check" "plugins" "plugin1,@scope/plugin2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects plugins with command injection"
|
||||
When call validate_input_python "prettier-check" "plugins" "plugin1; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects plugins with shell operators"
|
||||
When call validate_input_python "prettier-check" "plugins" "plugin1 && malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects plugins with pipe"
|
||||
When call validate_input_python "prettier-check" "plugins" "plugin1 | cat /etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Prettier Check"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "working-directory"
|
||||
The output should include "prettier-version"
|
||||
The output should include "config-file"
|
||||
The output should include "ignore-file"
|
||||
The output should include "file-pattern"
|
||||
The output should include "cache"
|
||||
The output should include "fail-on-error"
|
||||
The output should include "report-format"
|
||||
The output should include "max-retries"
|
||||
The output should include "plugins"
|
||||
The output should include "check-only"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "files-checked"
|
||||
The output should include "unformatted-files"
|
||||
The output should include "sarif-file"
|
||||
The output should include "cache-hit"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "any" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in multiple inputs"
|
||||
When call validate_input_python "prettier-check" "working-directory" "../../malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in plugins"
|
||||
When call validate_input_python "prettier-check" "plugins" "plugin\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell expansion in file patterns"
|
||||
When call validate_input_python "prettier-check" "file-pattern" "**/*.js\${HOME}"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
285
_tests/unit/prettier-fix/validation.spec.sh
Executable file
285
_tests/unit/prettier-fix/validation.spec.sh
Executable file
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for prettier-fix action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "prettier-fix action"
|
||||
ACTION_DIR="prettier-fix"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call validate_input_python "prettier-fix" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token"
|
||||
When call validate_input_python "prettier-fix" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub app token"
|
||||
When call validate_input_python "prettier-fix" "token" "ghs_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub enterprise token"
|
||||
When call validate_input_python "prettier-fix" "token" "ghe_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "prettier-fix" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "prettier-fix" "token" "ghp_token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty token (uses default)"
|
||||
When call validate_input_python "prettier-fix" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid GitHub username"
|
||||
When call validate_input_python "prettier-fix" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with hyphens"
|
||||
When call validate_input_python "prettier-fix" "username" "user-name"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with numbers"
|
||||
When call validate_input_python "prettier-fix" "username" "user123"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts single character username"
|
||||
When call validate_input_python "prettier-fix" "username" "a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts maximum length username"
|
||||
When call validate_input_python "prettier-fix" "username" "abcdefghijklmnopqrstuvwxyz0123456789abc"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects username too long"
|
||||
When call validate_input_python "prettier-fix" "username" "abcdefghijklmnopqrstuvwxyz0123456789abcd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with command injection"
|
||||
When call validate_input_python "prettier-fix" "username" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with shell operators"
|
||||
When call validate_input_python "prettier-fix" "username" "user && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with pipe"
|
||||
When call validate_input_python "prettier-fix" "username" "user | rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty username (uses default)"
|
||||
When call validate_input_python "prettier-fix" "username" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "prettier-fix" "email" "user@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with subdomain"
|
||||
When call validate_input_python "prettier-fix" "email" "user@mail.example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with plus sign"
|
||||
When call validate_input_python "prettier-fix" "email" "user+tag@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with numbers"
|
||||
When call validate_input_python "prettier-fix" "email" "user123@example123.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with hyphens"
|
||||
When call validate_input_python "prettier-fix" "email" "user-name@example-domain.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects email without at symbol"
|
||||
When call validate_input_python "prettier-fix" "email" "userexample.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email without domain"
|
||||
When call validate_input_python "prettier-fix" "email" "user@"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email without username"
|
||||
When call validate_input_python "prettier-fix" "email" "@example.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email without dot in domain"
|
||||
When call validate_input_python "prettier-fix" "email" "user@example"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email with spaces"
|
||||
When call validate_input_python "prettier-fix" "email" "user @example.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty email"
|
||||
When call validate_input_python "prettier-fix" "email" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum retries"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts maximum retries"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects too many retries"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric retries"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "many"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects negative retries"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Prettier Fix"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "max-retries"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "files_changed"
|
||||
The output should include "format_status"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
|
||||
It "has correct default token"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "token" "default"
|
||||
The output should equal "\${{ github.token }}"
|
||||
End
|
||||
|
||||
It "has correct default username"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "username" "default"
|
||||
The output should equal "github-actions"
|
||||
End
|
||||
|
||||
It "has correct default email"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "email" "default"
|
||||
The output should equal "github-actions@github.com"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against command injection in username"
|
||||
When call validate_input_python "prettier-fix" "username" "user\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in email"
|
||||
When call validate_input_python "prettier-fix" "email" "user@example.com; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion in token"
|
||||
When call validate_input_python "prettier-fix" "token" "\${MALICIOUS_VAR}"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection in email"
|
||||
When call validate_input_python "prettier-fix" "email" "user@example.com\`echo test\`"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing Prettier-specific validations"
|
||||
It "validates username length boundaries for Git"
|
||||
When call validate_input_python "prettier-fix" "username" "$(awk 'BEGIN{for(i=1;i<=40;i++)printf "a"}')"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates email format for Git commits"
|
||||
When call validate_input_python "prettier-fix" "email" "noreply@github.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates retry count boundaries"
|
||||
When call validate_input_python "prettier-fix" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates default values are secure"
|
||||
When call validate_input_python "prettier-fix" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
End
|
||||
149
_tests/unit/python-lint-fix/validation.spec.sh
Executable file
149
_tests/unit/python-lint-fix/validation.spec.sh
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for python-lint-fix action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "python-lint-fix action"
|
||||
ACTION_DIR="python-lint-fix"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call validate_input_python "python-lint-fix" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token"
|
||||
When call validate_input_python "python-lint-fix" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub app token"
|
||||
When call validate_input_python "python-lint-fix" "token" "ghs_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "python-lint-fix" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "python-lint-fix" "token" "ghp_token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty token (uses default)"
|
||||
When call validate_input_python "python-lint-fix" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid GitHub username"
|
||||
When call validate_input_python "python-lint-fix" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with hyphens"
|
||||
When call validate_input_python "python-lint-fix" "username" "user-name"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with numbers"
|
||||
When call validate_input_python "python-lint-fix" "username" "user123"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects username too long"
|
||||
When call validate_input_python "python-lint-fix" "username" "$(awk 'BEGIN{for(i=1;i<=40;i++)printf "a"}')"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with command injection"
|
||||
When call validate_input_python "python-lint-fix" "username" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty username (uses default)"
|
||||
When call validate_input_python "python-lint-fix" "username" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "python-lint-fix" "email" "user@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with subdomain"
|
||||
When call validate_input_python "python-lint-fix" "email" "user@mail.example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects email without at symbol"
|
||||
When call validate_input_python "python-lint-fix" "email" "userexample.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email without domain"
|
||||
When call validate_input_python "python-lint-fix" "email" "user@"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email with spaces"
|
||||
When call validate_input_python "python-lint-fix" "email" "user @example.com"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty email (uses default)"
|
||||
When call uv run "_tests/shared/validation_core.py" --validate "python-lint-fix" "email" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Python Lint and Fix"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against command injection in username"
|
||||
When call validate_input_python "python-lint-fix" "username" "user\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in email"
|
||||
When call validate_input_python "python-lint-fix" "email" "user@example.com; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion in token"
|
||||
When call validate_input_python "python-lint-fix" "token" "\${MALICIOUS_VAR}"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
98
_tests/unit/python-version-detect-v2/validation.spec.sh
Executable file
98
_tests/unit/python-version-detect-v2/validation.spec.sh
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for python-version-detect-v2 action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "python-version-detect-v2 action"
|
||||
ACTION_DIR="python-version-detect-v2"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating default-version input"
|
||||
It "accepts valid Python version"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "3.11"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts Python version with patch"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "3.11.5"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts Python 3.8"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "3.8"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts Python 3.12"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "3.12"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects Python version too old"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "2.7"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "python3.11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "3.11; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Python Version Detect v2"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "default-version"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "python-version"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has default-version as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "default-version" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in version"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "../3.11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in version"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "3.11|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection"
|
||||
When call validate_input_python "python-version-detect-v2" "default-version" "3.11\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
108
_tests/unit/python-version-detect/validation.spec.sh
Executable file
108
_tests/unit/python-version-detect/validation.spec.sh
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for python-version-detect action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "python-version-detect action"
|
||||
ACTION_DIR="python-version-detect"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating default-version input"
|
||||
It "accepts valid Python version"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3.11"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts Python version with patch"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3.11.5"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts Python 3.8"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3.8"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts Python 3.12"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3.12"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects Python version too old"
|
||||
When call validate_input_python "python-version-detect" "default-version" "2.7"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects Python version too new"
|
||||
When call validate_input_python "python-version-detect" "default-version" "4.0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "python-version-detect" "default-version" "python3.11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3.11; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version without minor"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "python-version-detect" "default-version" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Python Version Detect"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "default-version"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "python-version"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has default-version as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "default-version" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in version"
|
||||
When call validate_input_python "python-version-detect" "default-version" "../3.11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in version"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3.11|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against backtick injection"
|
||||
When call validate_input_python "python-version-detect" "default-version" "3.11\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
125
_tests/unit/release-monthly/validation.spec.sh
Executable file
125
_tests/unit/release-monthly/validation.spec.sh
Executable file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for release-monthly action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "release-monthly action"
|
||||
ACTION_DIR="release-monthly"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
# NOTE: Test framework uses strict GitHub token format validation
|
||||
It "accepts valid GitHub token with correct format"
|
||||
When call validate_input_python "release-monthly" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects empty token"
|
||||
When call validate_input_python "release-monthly" "token" ""
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects injection in token"
|
||||
When call validate_input_python "release-monthly" "token" "token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating dry-run input"
|
||||
It "accepts true value"
|
||||
When call validate_input_python "release-monthly" "dry-run" "true"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts false value"
|
||||
When call validate_input_python "release-monthly" "dry-run" "false"
|
||||
The status should be success
|
||||
End
|
||||
# NOTE: Convention-based validation applies boolean validation to 'dry-run'
|
||||
# Boolean validator rejects non-boolean values
|
||||
It "rejects invalid boolean value"
|
||||
When call validate_input_python "release-monthly" "dry-run" "maybe"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects injection in dry-run"
|
||||
When call validate_input_python "release-monthly" "dry-run" "true; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating prefix input"
|
||||
# NOTE: prefix has default: '' so empty values are accepted
|
||||
It "accepts empty prefix (has empty default)"
|
||||
When call validate_input_python "release-monthly" "prefix" ""
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid prefix"
|
||||
When call validate_input_python "release-monthly" "prefix" "v"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts alphanumeric prefix"
|
||||
When call validate_input_python "release-monthly" "prefix" "release-v1.0-"
|
||||
The status should be success
|
||||
End
|
||||
# NOTE: Test framework uses default validation for 'prefix'
|
||||
# Default validation only checks injection patterns, not character restrictions
|
||||
It "accepts special characters in prefix (framework default validation)"
|
||||
When call validate_input_python "release-monthly" "prefix" "invalid@prefix"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts spaces in prefix (framework default validation)"
|
||||
When call validate_input_python "release-monthly" "prefix" "invalid prefix"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in prefix"
|
||||
When call validate_input_python "release-monthly" "prefix" "prefix; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Do Monthly Release"
|
||||
End
|
||||
|
||||
It "defines required inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "token"
|
||||
The output should include "dry-run"
|
||||
The output should include "prefix"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "release-tag"
|
||||
The output should include "release-url"
|
||||
The output should include "previous-tag"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "validates token is required"
|
||||
When call validate_input_python "release-monthly" "token" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates prefix format"
|
||||
When call validate_input_python "release-monthly" "prefix" "invalid;prefix"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs"
|
||||
When call test_action_outputs "$ACTION_DIR" "token" "ghp_test" "dry-run" "true" "prefix" "v"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: release-monthly"
|
||||
The stderr should include "Output test passed for: release-monthly"
|
||||
End
|
||||
End
|
||||
End
|
||||
69
_tests/unit/set-git-config/validation.spec.sh
Executable file
69
_tests/unit/set-git-config/validation.spec.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for set-git-config action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "set-git-config action"
|
||||
ACTION_DIR="set-git-config"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating inputs (no validation logic in action)"
|
||||
# NOTE: This action has no validation logic - all inputs are accepted
|
||||
# The action simply passes through values and conditionally sets outputs
|
||||
It "accepts valid token value"
|
||||
When call validate_input_python "set-git-config" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts any username value"
|
||||
When call validate_input_python "set-git-config" "username" "any-username"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid email value"
|
||||
When call validate_input_python "set-git-config" "email" "test@example.com"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts any is_fiximus value"
|
||||
When call validate_input_python "set-git-config" "is_fiximus" "any-value"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Set Git Config"
|
||||
End
|
||||
|
||||
It "defines required inputs"
|
||||
inputs=$(get_action_inputs "$ACTION_FILE")
|
||||
When call echo "$inputs"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "is_fiximus"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
outputs=$(get_action_outputs "$ACTION_FILE")
|
||||
When call echo "$outputs"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "is_fiximus"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs"
|
||||
When call test_action_outputs "$ACTION_DIR" "token" "ghp_test" "username" "test" "email" "test@example.com" "is_fiximus" "false"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: set-git-config"
|
||||
The stderr should include "Output test passed for: set-git-config"
|
||||
End
|
||||
End
|
||||
End
|
||||
579
_tests/unit/spec_helper.sh
Executable file
579
_tests/unit/spec_helper.sh
Executable file
@@ -0,0 +1,579 @@
|
||||
#!/usr/bin/env bash
|
||||
# ShellSpec spec helper for GitHub Actions Testing Framework
|
||||
# This file is automatically loaded by ShellSpec for all tests
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Get the project root directory (where .shellspec is located)
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
# Test framework directories
|
||||
TEST_ROOT="${PROJECT_ROOT}/_tests"
|
||||
FRAMEWORK_DIR="${TEST_ROOT}/framework"
|
||||
FIXTURES_DIR="${FRAMEWORK_DIR}/fixtures"
|
||||
MOCKS_DIR="${FRAMEWORK_DIR}/mocks"
|
||||
|
||||
# Export directories for use by test cases
|
||||
export FIXTURES_DIR MOCKS_DIR
|
||||
# Only create TEMP_DIR if not already set (framework setup.sh will create it)
|
||||
if [ -z "${TEMP_DIR:-}" ]; then
|
||||
TEMP_DIR=$(mktemp -d) || exit 1
|
||||
fi
|
||||
|
||||
# Load framework utilities
|
||||
# shellcheck source=_tests/framework/setup.sh
|
||||
source "${FRAMEWORK_DIR}/setup.sh"
|
||||
# shellcheck source=_tests/framework/utils.sh
|
||||
source "${FRAMEWORK_DIR}/utils.sh"
|
||||
|
||||
# Initialize testing framework
|
||||
init_testing_framework
|
||||
|
||||
# ShellSpec specific setup
|
||||
spec_helper_configure() {
|
||||
# Configure ShellSpec behavior
|
||||
|
||||
# Set up environment variables for tests
|
||||
export GITHUB_ACTIONS=true
|
||||
export GITHUB_WORKSPACE="${PROJECT_ROOT}"
|
||||
export GITHUB_REPOSITORY="ivuorinen/actions"
|
||||
export GITHUB_SHA="test-sha"
|
||||
export GITHUB_REF="refs/heads/main"
|
||||
export GITHUB_TOKEN="test-token"
|
||||
|
||||
# Temporary directory already created by mktemp above
|
||||
|
||||
# Set up default GITHUB_OUTPUT if not already set
|
||||
if [[ -z ${GITHUB_OUTPUT:-} ]]; then
|
||||
export GITHUB_OUTPUT="${TEMP_DIR}/default-github-output"
|
||||
touch "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# Quiet logging during ShellSpec runs to avoid output interference
|
||||
if [[ -z ${SHELLSPEC_VERSION:-} ]]; then
|
||||
log_info "ShellSpec helper configured - framework loaded"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run configuration
|
||||
spec_helper_configure
|
||||
|
||||
# Helper functions specifically for ShellSpec tests
|
||||
|
||||
# Set up default input values for testing a single input
|
||||
# This prevents validation failures when testing one input at a time
|
||||
setup_default_inputs() {
|
||||
local action_name="$1"
|
||||
local input_name="$2"
|
||||
|
||||
case "$action_name" in
|
||||
"github-release")
|
||||
[[ "$input_name" != "version" ]] && export INPUT_VERSION="1.0.0"
|
||||
;;
|
||||
"docker-build" | "docker-publish" | "docker-publish-gh" | "docker-publish-hub")
|
||||
[[ "$input_name" != "image-name" ]] && export INPUT_IMAGE_NAME="test-image"
|
||||
[[ "$input_name" != "tag" ]] && export INPUT_TAG="latest"
|
||||
[[ "$action_name" == "docker-publish" && "$input_name" != "registry" ]] && export INPUT_REGISTRY="dockerhub"
|
||||
;;
|
||||
"npm-publish")
|
||||
[[ "$input_name" != "npm_token" ]] && export INPUT_NPM_TOKEN="ghp_123456789012345678901234567890123456"
|
||||
;;
|
||||
"csharp-publish")
|
||||
[[ "$input_name" != "token" ]] && export INPUT_TOKEN="ghp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
[[ "$input_name" != "version" ]] && export INPUT_VERSION="1.0.0"
|
||||
[[ "$input_name" != "namespace" ]] && export INPUT_NAMESPACE="test-namespace"
|
||||
;;
|
||||
"php-composer")
|
||||
[[ "$input_name" != "php" ]] && export INPUT_PHP="8.1"
|
||||
;;
|
||||
"php-tests" | "php-laravel-phpunit")
|
||||
[[ "$input_name" != "php-version" ]] && export INPUT_PHP_VERSION="8.1"
|
||||
;;
|
||||
"go-build" | "go-lint")
|
||||
[[ "$input_name" != "go-version" ]] && export INPUT_GO_VERSION="1.21"
|
||||
;;
|
||||
"common-cache")
|
||||
[[ "$input_name" != "type" ]] && export INPUT_TYPE="npm"
|
||||
[[ "$input_name" != "paths" ]] && export INPUT_PATHS="node_modules"
|
||||
;;
|
||||
"common-retry")
|
||||
[[ "$input_name" != "command" ]] && export INPUT_COMMAND="echo test"
|
||||
;;
|
||||
"dotnet-version-detect")
|
||||
[[ "$input_name" != "default-version" ]] && export INPUT_DEFAULT_VERSION="8.0"
|
||||
;;
|
||||
"python-version-detect" | "python-version-detect-v2")
|
||||
[[ "$input_name" != "default-version" ]] && export INPUT_DEFAULT_VERSION="3.11"
|
||||
;;
|
||||
"php-version-detect")
|
||||
[[ "$input_name" != "default-version" ]] && export INPUT_DEFAULT_VERSION="8.1"
|
||||
;;
|
||||
"go-version-detect")
|
||||
[[ "$input_name" != "default-version" ]] && export INPUT_DEFAULT_VERSION="1.22"
|
||||
;;
|
||||
"validate-inputs")
|
||||
[[ "$input_name" != "action-type" && "$input_name" != "action" && "$input_name" != "rules-file" && "$input_name" != "fail-on-error" ]] && export INPUT_ACTION_TYPE="test-action"
|
||||
;;
|
||||
"version-file-parser")
|
||||
[[ "$input_name" != "language" ]] && export INPUT_LANGUAGE="node"
|
||||
[[ "$input_name" != "tool-versions-key" ]] && export INPUT_TOOL_VERSIONS_KEY="nodejs"
|
||||
[[ "$input_name" != "dockerfile-image" ]] && export INPUT_DOCKERFILE_IMAGE="node"
|
||||
;;
|
||||
"codeql-analysis")
|
||||
[[ "$input_name" != "language" ]] && export INPUT_LANGUAGE="javascript"
|
||||
[[ "$input_name" != "token" ]] && export INPUT_TOKEN="ghp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
;;
|
||||
"version-validator")
|
||||
[[ "$input_name" != "version" ]] && export INPUT_VERSION="1.0.0"
|
||||
;;
|
||||
"release-monthly")
|
||||
[[ "$input_name" != "token" ]] && export INPUT_TOKEN="ghp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Clean up default input values after testing
|
||||
cleanup_default_inputs() {
|
||||
local action_name="$1"
|
||||
local input_name="$2"
|
||||
|
||||
case "$action_name" in
|
||||
"github-release")
|
||||
[[ "$input_name" != "version" ]] && unset INPUT_VERSION
|
||||
;;
|
||||
"docker-build" | "docker-publish" | "docker-publish-gh" | "docker-publish-hub")
|
||||
[[ "$input_name" != "image-name" ]] && unset INPUT_IMAGE_NAME
|
||||
[[ "$input_name" != "tag" ]] && unset INPUT_TAG
|
||||
[[ "$action_name" == "docker-publish" && "$input_name" != "registry" ]] && unset INPUT_REGISTRY
|
||||
;;
|
||||
"npm-publish")
|
||||
[[ "$input_name" != "npm_token" ]] && unset INPUT_NPM_TOKEN
|
||||
;;
|
||||
"csharp-publish")
|
||||
[[ "$input_name" != "token" ]] && unset INPUT_TOKEN
|
||||
[[ "$input_name" != "version" ]] && unset INPUT_VERSION
|
||||
[[ "$input_name" != "namespace" ]] && unset INPUT_NAMESPACE
|
||||
;;
|
||||
"php-composer")
|
||||
[[ "$input_name" != "php" ]] && unset INPUT_PHP
|
||||
;;
|
||||
"php-tests" | "php-laravel-phpunit")
|
||||
[[ "$input_name" != "php-version" ]] && unset INPUT_PHP_VERSION
|
||||
;;
|
||||
"go-build" | "go-lint")
|
||||
[[ "$input_name" != "go-version" ]] && unset INPUT_GO_VERSION
|
||||
;;
|
||||
"common-cache")
|
||||
[[ "$input_name" != "type" ]] && unset INPUT_TYPE
|
||||
[[ "$input_name" != "paths" ]] && unset INPUT_PATHS
|
||||
;;
|
||||
"common-retry")
|
||||
[[ "$input_name" != "command" ]] && unset INPUT_COMMAND
|
||||
;;
|
||||
"dotnet-version-detect")
|
||||
[[ "$input_name" != "default-version" ]] && unset INPUT_DEFAULT_VERSION
|
||||
;;
|
||||
"python-version-detect" | "python-version-detect-v2")
|
||||
[[ "$input_name" != "default-version" ]] && unset INPUT_DEFAULT_VERSION
|
||||
;;
|
||||
"php-version-detect")
|
||||
[[ "$input_name" != "default-version" ]] && unset INPUT_DEFAULT_VERSION
|
||||
;;
|
||||
"go-version-detect")
|
||||
[[ "$input_name" != "default-version" ]] && unset INPUT_DEFAULT_VERSION
|
||||
;;
|
||||
"validate-inputs")
|
||||
[[ "$input_name" != "action-type" && "$input_name" != "action" && "$input_name" != "rules-file" && "$input_name" != "fail-on-error" ]] && unset INPUT_ACTION_TYPE
|
||||
;;
|
||||
"version-file-parser")
|
||||
[[ "$input_name" != "language" ]] && unset INPUT_LANGUAGE
|
||||
[[ "$input_name" != "tool-versions-key" ]] && unset INPUT_TOOL_VERSIONS_KEY
|
||||
[[ "$input_name" != "dockerfile-image" ]] && unset INPUT_DOCKERFILE_IMAGE
|
||||
;;
|
||||
"codeql-analysis")
|
||||
[[ "$input_name" != "language" ]] && unset INPUT_LANGUAGE
|
||||
[[ "$input_name" != "token" ]] && unset INPUT_TOKEN
|
||||
;;
|
||||
"version-validator")
|
||||
[[ "$input_name" != "version" ]] && unset INPUT_VERSION
|
||||
;;
|
||||
"release-monthly")
|
||||
[[ "$input_name" != "token" ]] && unset INPUT_TOKEN
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Enhanced test validation for ShellSpec
|
||||
shellspec_validate_action_output() {
|
||||
local expected_key="$1"
|
||||
local expected_value="$2"
|
||||
local output_file="${3:-$GITHUB_OUTPUT}"
|
||||
|
||||
if [[ ! -f $output_file ]]; then
|
||||
echo "Output file not found: $output_file" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if grep -Fq "${expected_key}=${expected_value}" "$output_file"; then
|
||||
return 0
|
||||
else
|
||||
echo "Expected output not found: $expected_key=$expected_value" >&2
|
||||
echo "Actual outputs:" >&2
|
||||
cat "$output_file" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Mock action execution for ShellSpec tests
|
||||
shellspec_mock_action_run() {
|
||||
local action_dir="$1"
|
||||
shift
|
||||
|
||||
# Set up inputs as environment variables
|
||||
while [[ $# -gt 1 ]]; do
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
# Convert dashes to underscores for environment variable names
|
||||
local env_key="${key//-/_}"
|
||||
export "INPUT_$(echo "$env_key" | tr '[:lower:]' '[:upper:]')"="$value"
|
||||
shift 2
|
||||
done
|
||||
|
||||
# For testing, we'll simulate action outputs based on the action type
|
||||
local action_name
|
||||
action_name=$(basename "$action_dir")
|
||||
|
||||
case "$action_name" in
|
||||
"version-file-parser")
|
||||
echo "detected-version=1.0.0" >>"$GITHUB_OUTPUT"
|
||||
echo "package-manager=npm" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"node-setup")
|
||||
echo "node-version=18.0.0" >>"$GITHUB_OUTPUT"
|
||||
echo "package-manager=npm" >>"$GITHUB_OUTPUT"
|
||||
echo "cache-hit=false" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"docker-build")
|
||||
echo "image-digest=sha256:abc123" >>"$GITHUB_OUTPUT"
|
||||
echo "build-time=45" >>"$GITHUB_OUTPUT"
|
||||
echo "platforms=linux/amd64" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"common-cache")
|
||||
echo "cache-hit=true" >>"$GITHUB_OUTPUT"
|
||||
echo "cache-key=Linux-npm-abc123" >>"$GITHUB_OUTPUT"
|
||||
echo "cache-paths=node_modules" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"common-file-check")
|
||||
echo "found=true" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"common-retry")
|
||||
echo "success=true" >>"$GITHUB_OUTPUT"
|
||||
echo "attempts=1" >>"$GITHUB_OUTPUT"
|
||||
echo "exit-code=0" >>"$GITHUB_OUTPUT"
|
||||
echo "duration=5" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"compress-images")
|
||||
echo "images_compressed=true" >>"$GITHUB_OUTPUT"
|
||||
printf "compression_report=## Compression Results\n- 3 images compressed\n- 25%% size reduction\n" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"csharp-build")
|
||||
echo "build_status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "test_status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "dotnet_version=7.0" >>"$GITHUB_OUTPUT"
|
||||
echo "artifacts_path=**/bin/Release/**/*" >>"$GITHUB_OUTPUT"
|
||||
echo "test_results_path=**/*.trx" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"csharp-lint-check")
|
||||
echo "lint_status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "errors_count=0" >>"$GITHUB_OUTPUT"
|
||||
echo "warnings_count=0" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"csharp-publish")
|
||||
echo "publish_status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "package_version=1.2.3" >>"$GITHUB_OUTPUT"
|
||||
echo "package_url=https://github.com/ivuorinen/packages/nuget" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"docker-publish")
|
||||
echo "registry=github,dockerhub" >>"$GITHUB_OUTPUT"
|
||||
echo "tags=latest,v1.2.3" >>"$GITHUB_OUTPUT"
|
||||
echo "build-time=120" >>"$GITHUB_OUTPUT"
|
||||
echo 'platform-matrix={"linux/amd64":"success","linux/arm64":"success"}' >>"$GITHUB_OUTPUT"
|
||||
echo 'scan-results={"vulnerabilities":0}' >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"docker-publish-gh")
|
||||
echo "image-name=ghcr.io/ivuorinen/test" >>"$GITHUB_OUTPUT"
|
||||
echo "digest=sha256:abc123def456" >>"$GITHUB_OUTPUT"
|
||||
echo "tags=ghcr.io/ivuorinen/test:latest,ghcr.io/ivuorinen/test:v1.2.3" >>"$GITHUB_OUTPUT"
|
||||
echo "provenance=true" >>"$GITHUB_OUTPUT"
|
||||
echo "sbom=ghcr.io/ivuorinen/test.sbom" >>"$GITHUB_OUTPUT"
|
||||
echo 'scan-results={"vulnerabilities":0,"critical":0}' >>"$GITHUB_OUTPUT"
|
||||
echo 'platform-matrix={"linux/amd64":"success","linux/arm64":"success"}' >>"$GITHUB_OUTPUT"
|
||||
echo "build-time=180" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"docker-publish-hub")
|
||||
echo "image-name=ivuorinen/test-app" >>"$GITHUB_OUTPUT"
|
||||
echo "digest=sha256:hub123def456" >>"$GITHUB_OUTPUT"
|
||||
echo "tags=ivuorinen/test-app:latest,ivuorinen/test-app:v1.2.3" >>"$GITHUB_OUTPUT"
|
||||
echo "repo-url=https://hub.docker.com/r/ivuorinen/test-app" >>"$GITHUB_OUTPUT"
|
||||
echo 'scan-results={"vulnerabilities":2,"critical":0}' >>"$GITHUB_OUTPUT"
|
||||
echo 'platform-matrix={"linux/amd64":"success","linux/arm64":"success"}' >>"$GITHUB_OUTPUT"
|
||||
echo "build-time=240" >>"$GITHUB_OUTPUT"
|
||||
echo "signature=signed" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"dotnet-version-detect")
|
||||
echo "dotnet-version=7.0.403" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"eslint-check")
|
||||
echo "error-count=0" >>"$GITHUB_OUTPUT"
|
||||
echo "warning-count=3" >>"$GITHUB_OUTPUT"
|
||||
echo "sarif-file=reports/eslint.sarif" >>"$GITHUB_OUTPUT"
|
||||
echo "files-checked=15" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"eslint-fix")
|
||||
echo "fixed-count=5" >>"$GITHUB_OUTPUT"
|
||||
echo "files-fixed=3" >>"$GITHUB_OUTPUT"
|
||||
echo "error-count=0" >>"$GITHUB_OUTPUT"
|
||||
echo "warning-count=0" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"github-release")
|
||||
echo "release-id=123456789" >>"$GITHUB_OUTPUT"
|
||||
echo "release-url=https://github.com/ivuorinen/test/releases/tag/v1.2.3" >>"$GITHUB_OUTPUT"
|
||||
echo "asset-urls=https://github.com/ivuorinen/test/releases/download/v1.2.3/app.tar.gz" >>"$GITHUB_OUTPUT"
|
||||
echo "tag-name=v1.2.3" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"go-build")
|
||||
echo "build_status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "test_status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "go_version=1.21.5" >>"$GITHUB_OUTPUT"
|
||||
echo "binary_path=./bin" >>"$GITHUB_OUTPUT"
|
||||
echo "coverage_path=coverage.out" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"go-lint")
|
||||
echo "lint_status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "issues_count=0" >>"$GITHUB_OUTPUT"
|
||||
echo "files_checked=25" >>"$GITHUB_OUTPUT"
|
||||
echo "golangci_version=1.55.2" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"go-version-detect")
|
||||
echo "go-version=1.21" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"npm-publish")
|
||||
echo "publish-status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "package-version=1.2.3" >>"$GITHUB_OUTPUT"
|
||||
echo "registry-url=https://registry.npmjs.org" >>"$GITHUB_OUTPUT"
|
||||
echo "package-url=https://www.npmjs.com/package/test-package" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
"php-composer")
|
||||
echo "composer-version=2.6.5" >>"$GITHUB_OUTPUT"
|
||||
echo "install-status=success" >>"$GITHUB_OUTPUT"
|
||||
echo "dependencies-count=15" >>"$GITHUB_OUTPUT"
|
||||
echo "php-version=8.2.0" >>"$GITHUB_OUTPUT"
|
||||
echo "lock-file-updated=false" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
*)
|
||||
# Generic mock outputs
|
||||
echo "status=success" >>"$GITHUB_OUTPUT"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Use centralized Python validation system for input validation testing
|
||||
shellspec_test_input_validation() {
|
||||
local action_dir="$1"
|
||||
local input_name="$2"
|
||||
local test_value="$3"
|
||||
local expected_result="${4:-success}"
|
||||
|
||||
# Get the action name from the directory
|
||||
local action_name
|
||||
action_name=$(basename "$action_dir")
|
||||
|
||||
# Set up environment for Python validation
|
||||
local temp_output_file
|
||||
temp_output_file=$(mktemp)
|
||||
|
||||
# Capture original INPUT_ACTION_TYPE state to restore after test
|
||||
local original_action_type_set=false
|
||||
local original_action_type_value=""
|
||||
if [[ -n "${INPUT_ACTION_TYPE+x}" ]]; then
|
||||
original_action_type_set=true
|
||||
original_action_type_value="$INPUT_ACTION_TYPE"
|
||||
fi
|
||||
|
||||
# Set environment variables for the validation script
|
||||
# Only set INPUT_ACTION_TYPE if we're not testing the action input
|
||||
if [[ "$input_name" != "action" ]]; then
|
||||
export INPUT_ACTION_TYPE="$action_name"
|
||||
fi
|
||||
|
||||
# Set default values for commonly required inputs to avoid validation failures
|
||||
# when testing only one input at a time
|
||||
setup_default_inputs "$action_name" "$input_name"
|
||||
|
||||
# Convert input name to uppercase and replace dashes with underscores
|
||||
local input_var_name
|
||||
input_var_name="INPUT_${input_name//-/_}"
|
||||
input_var_name="$(echo "$input_var_name" | tr '[:lower:]' '[:upper:]')"
|
||||
export "$input_var_name"="$test_value"
|
||||
export GITHUB_OUTPUT="$temp_output_file"
|
||||
|
||||
# Run the Python validation script and capture exit code
|
||||
local exit_code
|
||||
if python3 "${PROJECT_ROOT}/validate-inputs/validator.py" >/dev/null 2>&1; then
|
||||
exit_code=0
|
||||
else
|
||||
exit_code=1
|
||||
fi
|
||||
|
||||
# Determine the actual result based on exit code
|
||||
local actual_result
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
actual_result="success"
|
||||
else
|
||||
actual_result="failure"
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
rm -f "$temp_output_file" 2>/dev/null || true
|
||||
unset "$input_var_name"
|
||||
|
||||
# Clean up default inputs
|
||||
cleanup_default_inputs "$action_name" "$input_name"
|
||||
|
||||
# Restore original INPUT_ACTION_TYPE state
|
||||
if [[ "$original_action_type_set" == "true" ]]; then
|
||||
export INPUT_ACTION_TYPE="$original_action_type_value"
|
||||
else
|
||||
unset INPUT_ACTION_TYPE
|
||||
fi
|
||||
|
||||
# Return based on expected result
|
||||
if [[ $actual_result == "$expected_result" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Test environment setup that works with ShellSpec
|
||||
shellspec_setup_test_env() {
|
||||
local test_name="${1:-shellspec-test}"
|
||||
|
||||
# Create unique temporary directory for this test
|
||||
export SHELLSPEC_TEST_TEMP_DIR="${TEMP_DIR}/${test_name}-$$"
|
||||
mkdir -p "$SHELLSPEC_TEST_TEMP_DIR"
|
||||
|
||||
# Create fake GitHub workspace
|
||||
export SHELLSPEC_TEST_WORKSPACE="${SHELLSPEC_TEST_TEMP_DIR}/workspace"
|
||||
mkdir -p "$SHELLSPEC_TEST_WORKSPACE"
|
||||
|
||||
# Setup fake GitHub outputs
|
||||
export GITHUB_OUTPUT="${SHELLSPEC_TEST_TEMP_DIR}/github-output"
|
||||
export GITHUB_ENV="${SHELLSPEC_TEST_TEMP_DIR}/github-env"
|
||||
export GITHUB_PATH="${SHELLSPEC_TEST_TEMP_DIR}/github-path"
|
||||
export GITHUB_STEP_SUMMARY="${SHELLSPEC_TEST_TEMP_DIR}/github-step-summary"
|
||||
|
||||
# Initialize output files
|
||||
touch "$GITHUB_OUTPUT" "$GITHUB_ENV" "$GITHUB_PATH" "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# Change to test workspace
|
||||
cd "$SHELLSPEC_TEST_WORKSPACE"
|
||||
}
|
||||
|
||||
# Test environment cleanup for ShellSpec
|
||||
shellspec_cleanup_test_env() {
|
||||
local test_name="${1:-shellspec-test}"
|
||||
|
||||
if [[ -n ${SHELLSPEC_TEST_TEMP_DIR:-} && -d $SHELLSPEC_TEST_TEMP_DIR ]]; then
|
||||
rm -rf "$SHELLSPEC_TEST_TEMP_DIR"
|
||||
fi
|
||||
|
||||
# Return to project root
|
||||
cd "$PROJECT_ROOT"
|
||||
}
|
||||
|
||||
# Export functions for use in specs
|
||||
export -f shellspec_validate_action_output shellspec_mock_action_run
|
||||
export -f shellspec_setup_test_env shellspec_cleanup_test_env shellspec_test_input_validation
|
||||
|
||||
# Create alias for backward compatibility (override framework version)
|
||||
test_input_validation() {
|
||||
shellspec_test_input_validation "$@"
|
||||
}
|
||||
|
||||
# Export all framework functions for backward compatibility
|
||||
export -f setup_test_env cleanup_test_env create_mock_repo
|
||||
export -f create_mock_node_repo
|
||||
export -f validate_action_output check_required_tools
|
||||
export -f log_info log_success log_warning log_error
|
||||
export -f validate_action_yml get_action_inputs get_action_outputs get_action_name
|
||||
export -f test_action_outputs test_external_usage test_input_validation
|
||||
|
||||
# Quiet wrapper for validate_action_yml in tests
|
||||
validate_action_yml_quiet() {
|
||||
validate_action_yml "$1" "true"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# VALIDATION TEST HELPERS
|
||||
# =============================================================================
|
||||
# Note: These helpers return validation results but cannot use ShellSpec commands
|
||||
# They must be called from within ShellSpec It blocks
|
||||
|
||||
# Modern Python-based validation function for direct testing
|
||||
validate_input_python() {
|
||||
local action_type="$1"
|
||||
local input_name="$2"
|
||||
local input_value="$3"
|
||||
|
||||
# Set up environment variables for Python validator
|
||||
export INPUT_ACTION_TYPE="$action_type"
|
||||
export VALIDATOR_QUIET="1" # Suppress success messages for tests
|
||||
|
||||
# Set default values for commonly required inputs to avoid validation failures
|
||||
# when testing only one input at a time
|
||||
setup_default_inputs "$action_type" "$input_name"
|
||||
|
||||
# Set the target input
|
||||
local input_var_name="INPUT_${input_name//-/_}"
|
||||
input_var_name="$(echo "$input_var_name" | tr '[:lower:]' '[:upper:]')"
|
||||
export "$input_var_name"="$input_value"
|
||||
|
||||
# Set up GitHub output file
|
||||
local temp_output
|
||||
temp_output=$(mktemp)
|
||||
export GITHUB_OUTPUT="$temp_output"
|
||||
|
||||
# Call Python validator directly
|
||||
|
||||
if [[ "${SHELLSPEC_DEBUG:-}" == "1" ]]; then
|
||||
echo "DEBUG: Testing $action_type $input_name=$input_value"
|
||||
echo "DEBUG: Environment variables:"
|
||||
env | grep "^INPUT_" | sort
|
||||
fi
|
||||
|
||||
# Run validator and output everything to stdout for ShellSpec
|
||||
uv run "${PROJECT_ROOT}/validate-inputs/validator.py" 2>&1
|
||||
local exit_code=$?
|
||||
|
||||
# Clean up target input
|
||||
unset INPUT_ACTION_TYPE "$input_var_name" GITHUB_OUTPUT VALIDATOR_QUIET
|
||||
rm -f "$temp_output" 2>/dev/null || true
|
||||
|
||||
# Clean up default inputs
|
||||
cleanup_default_inputs "$action_type" "$input_name"
|
||||
|
||||
# Return the exit code for ShellSpec to check
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# Export all new simplified helpers (functions are moved above)
|
||||
export -f validate_action_yml_quiet validate_input_python
|
||||
|
||||
# Removed EXIT trap setup to avoid conflicts with ShellSpec
|
||||
# ShellSpec handles its own cleanup, and our framework cleanup is handled in setup.sh
|
||||
|
||||
# Quiet logging during ShellSpec runs
|
||||
if [[ -z ${SHELLSPEC_VERSION:-} ]]; then
|
||||
log_success "ShellSpec spec helper loaded successfully"
|
||||
fi
|
||||
139
_tests/unit/stale/validation.spec.sh
Executable file
139
_tests/unit/stale/validation.spec.sh
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for stale action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "stale action"
|
||||
ACTION_DIR="stale"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call validate_input_python "stale" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token"
|
||||
When call validate_input_python "stale" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "stale" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "stale" "token" "ghp_token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty token (uses default)"
|
||||
When call validate_input_python "stale" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating days-before-stale input"
|
||||
It "accepts valid day count"
|
||||
When call validate_input_python "stale" "days-before-stale" "30"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum days"
|
||||
When call validate_input_python "stale" "days-before-stale" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts reasonable maximum days"
|
||||
When call validate_input_python "stale" "days-before-stale" "365"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects zero days"
|
||||
When call validate_input_python "stale" "days-before-stale" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects negative days"
|
||||
When call validate_input_python "stale" "days-before-stale" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric days"
|
||||
When call validate_input_python "stale" "days-before-stale" "many"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating days-before-close input"
|
||||
It "accepts valid day count"
|
||||
When call validate_input_python "stale" "days-before-close" "7"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts minimum days"
|
||||
When call validate_input_python "stale" "days-before-close" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts reasonable maximum days"
|
||||
When call validate_input_python "stale" "days-before-close" "365"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects zero days"
|
||||
When call validate_input_python "stale" "days-before-close" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects negative days"
|
||||
When call validate_input_python "stale" "days-before-close" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Stale"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "token"
|
||||
The output should include "days-before-stale"
|
||||
The output should include "days-before-close"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against command injection in token"
|
||||
When call validate_input_python "stale" "token" "ghp_token\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against variable expansion in days"
|
||||
When call validate_input_python "stale" "days-before-stale" "30\${HOME}"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in days"
|
||||
When call validate_input_python "stale" "days-before-close" "7; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
111
_tests/unit/sync-labels/validation.spec.sh
Executable file
111
_tests/unit/sync-labels/validation.spec.sh
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for sync-labels action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "sync-labels action"
|
||||
ACTION_DIR="sync-labels"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call uv run "_tests/shared/validation_core.py" --validate "sync-labels" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts classic GitHub token"
|
||||
When call uv run "_tests/shared/validation_core.py" --validate "sync-labels" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts fine-grained GitHub token"
|
||||
When call uv run "_tests/shared/validation_core.py" --validate "sync-labels" "token" "github_pat_11ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "sync-labels" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "sync-labels" "token" "ghp_token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating config-file input"
|
||||
It "accepts valid config file"
|
||||
When call uv run "_tests/shared/validation_core.py" --validate "sync-labels" "labels" ".github/labels.yml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts config file with json extension"
|
||||
When call uv run "_tests/shared/validation_core.py" --validate "sync-labels" "labels" ".github/labels.json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal in config file"
|
||||
When call validate_input_python "sync-labels" "labels" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute path in config file"
|
||||
When call validate_input_python "sync-labels" "labels" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects config file with command injection"
|
||||
When call validate_input_python "sync-labels" "labels" "labels.yml; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Sync labels"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "token"
|
||||
The output should include "labels"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "token input is optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "token" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
|
||||
It "labels input is required"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "labels" "required"
|
||||
The output should equal "required"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in config file"
|
||||
When call validate_input_python "sync-labels" "labels" "../../malicious.yml"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in token"
|
||||
When call validate_input_python "sync-labels" "token" "ghp_token\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in config file"
|
||||
When call validate_input_python "sync-labels" "labels" "labels.yml && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
156
_tests/unit/terraform-lint-fix/validation.spec.sh
Executable file
156
_tests/unit/terraform-lint-fix/validation.spec.sh
Executable file
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for terraform-lint-fix action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "terraform-lint-fix action"
|
||||
ACTION_DIR="terraform-lint-fix"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts GitHub token expression"
|
||||
When call validate_input_python "terraform-lint-fix" "token" "\${{ github.token }}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts GitHub fine-grained token"
|
||||
When call validate_input_python "terraform-lint-fix" "token" "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "terraform-lint-fix" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "terraform-lint-fix" "token" "ghp_token; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty token (uses default)"
|
||||
When call validate_input_python "terraform-lint-fix" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating terraform-version input"
|
||||
It "accepts valid terraform version"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" "1.5.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts latest terraform version"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" "latest"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts terraform version with patch"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" "1.5.7"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts terraform version with v prefix"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" "v1.5.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects terraform version with command injection"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" "1.5.0; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty terraform version (uses default)"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating working-directory input"
|
||||
It "accepts current directory"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts relative directory"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "terraform"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts nested directory"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "infrastructure/terraform"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "../malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute paths"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects directory with command injection"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "terraform; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Terraform Lint and Fix"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "token"
|
||||
The output should include "terraform-version"
|
||||
The output should include "working-directory"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "" "all_optional"
|
||||
The output should equal "none"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in working directory"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "../../malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in terraform version"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" "1.5.0\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in token"
|
||||
When call validate_input_python "terraform-lint-fix" "token" "ghp_token && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing Terraform-specific validations"
|
||||
It "validates terraform version format"
|
||||
When call validate_input_python "terraform-lint-fix" "terraform-version" "1.x.x"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates working directory path safety"
|
||||
When call validate_input_python "terraform-lint-fix" "working-directory" "/root/.ssh"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
178
_tests/unit/validate-inputs/validation.spec.sh
Executable file
178
_tests/unit/validate-inputs/validation.spec.sh
Executable file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for validate-inputs action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "validate-inputs action"
|
||||
ACTION_DIR="validate-inputs"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating action input"
|
||||
It "accepts valid action name"
|
||||
When call validate_input_python "validate-inputs" "action" "github-release"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts action name with hyphens"
|
||||
When call validate_input_python "validate-inputs" "action" "docker-build"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts action name with underscores"
|
||||
When call validate_input_python "validate-inputs" "action" "npm_publish"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects action with command injection"
|
||||
When call validate_input_python "validate-inputs" "action" "github-release; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects action with shell operators"
|
||||
When call validate_input_python "validate-inputs" "action" "github-release && malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects action with pipe"
|
||||
When call validate_input_python "validate-inputs" "action" "github-release | cat /etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty action"
|
||||
When call validate_input_python "validate-inputs" "action" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating rules-file input"
|
||||
It "accepts valid rules file"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "validate-inputs/rules/github-release.yml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts rules file with relative path"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "rules/action.yml"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal in rules file"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects absolute path in rules file"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "/etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects rules file with command injection"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "rules.yml; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "accepts empty rules file (uses default)"
|
||||
When call validate_input_python "validate-inputs" "rules-file" ""
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating fail-on-error input"
|
||||
It "accepts true for fail-on-error"
|
||||
When call validate_input_python "validate-inputs" "fail-on-error" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false for fail-on-error"
|
||||
When call validate_input_python "validate-inputs" "fail-on-error" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid fail-on-error value"
|
||||
When call validate_input_python "validate-inputs" "fail-on-error" "yes"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty fail-on-error"
|
||||
When call validate_input_python "validate-inputs" "fail-on-error" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should equal "Validate Inputs"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "action"
|
||||
The output should include "rules-file"
|
||||
The output should include "fail-on-error"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "validation-result"
|
||||
The output should include "errors-found"
|
||||
The output should include "rules-applied"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "requires action input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "action" "required"
|
||||
The output should equal "required"
|
||||
End
|
||||
|
||||
It "has rules-file as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "rules-file" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
|
||||
It "has fail-on-error as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "fail-on-error" "optional"
|
||||
The output should equal "optional"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in rules file"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "../../malicious.yml"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in action name"
|
||||
When call validate_input_python "validate-inputs" "action" "test\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in rules file"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "rules.yml && rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing validation-specific functionality"
|
||||
It "validates action name format restrictions"
|
||||
When call validate_input_python "validate-inputs" "action" "invalid/action/name"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates rules file extension requirements"
|
||||
When call validate_input_python "validate-inputs" "rules-file" "rules.txt"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "validates boolean input parsing"
|
||||
When call validate_input_python "validate-inputs" "fail-on-error" "TRUE"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
End
|
||||
125
_tests/unit/version-file-parser/validation.spec.sh
Executable file
125
_tests/unit/version-file-parser/validation.spec.sh
Executable file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for version-file-parser action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "version-file-parser action"
|
||||
ACTION_DIR="version-file-parser"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating language input"
|
||||
It "accepts valid language input"
|
||||
When call validate_input_python "version-file-parser" "language" "node"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts php language"
|
||||
When call validate_input_python "version-file-parser" "language" "php"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts python language"
|
||||
When call validate_input_python "version-file-parser" "language" "python"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts go language"
|
||||
When call validate_input_python "version-file-parser" "language" "go"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid language with special characters"
|
||||
When call validate_input_python "version-file-parser" "language" "node; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects empty required inputs"
|
||||
When call validate_input_python "version-file-parser" "language" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating dockerfile-image input"
|
||||
It "accepts valid dockerfile image"
|
||||
When call validate_input_python "version-file-parser" "dockerfile-image" "node"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts php dockerfile image"
|
||||
When call validate_input_python "version-file-parser" "dockerfile-image" "php"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts python dockerfile image"
|
||||
When call validate_input_python "version-file-parser" "dockerfile-image" "python"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects injection in dockerfile image"
|
||||
When call validate_input_python "version-file-parser" "dockerfile-image" "node;malicious"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating optional inputs"
|
||||
It "accepts valid validation regex"
|
||||
When call validate_input_python "version-file-parser" "validation-regex" "^[0-9]+\.[0-9]+(\.[0-9]+)?$"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid default version"
|
||||
When call validate_input_python "version-file-parser" "default-version" "18.0.0"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts tool versions key"
|
||||
When call validate_input_python "version-file-parser" "tool-versions-key" "nodejs"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "contains required metadata"
|
||||
When call get_action_name "$ACTION_FILE"
|
||||
The output should equal "Version File Parser"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "language"
|
||||
The output should include "tool-versions-key"
|
||||
The output should include "dockerfile-image"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "detected-version"
|
||||
The output should include "package-manager"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating security"
|
||||
It "rejects injection in language parameter"
|
||||
When call validate_input_python "version-file-parser" "language" "node&&malicious"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects pipe injection in tool versions key"
|
||||
When call validate_input_python "version-file-parser" "tool-versions-key" "nodejs|dangerous"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates regex patterns safely"
|
||||
When call validate_input_python "version-file-parser" "validation-regex" "^[0-9]+\.[0-9]+$"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects malicious regex patterns"
|
||||
When call validate_input_python "version-file-parser" "validation-regex" ".*; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing outputs"
|
||||
It "produces all expected outputs consistently"
|
||||
When call test_action_outputs "$ACTION_DIR" "language" "node" "dockerfile-image" "node"
|
||||
The status should be success
|
||||
The stderr should include "Testing action outputs for: version-file-parser"
|
||||
The stderr should include "Output test passed for: version-file-parser"
|
||||
End
|
||||
End
|
||||
End
|
||||
233
_tests/unit/version-validator/validation.spec.sh
Executable file
233
_tests/unit/version-validator/validation.spec.sh
Executable file
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for version-validator action validation and logic
|
||||
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "version-validator action"
|
||||
ACTION_DIR="version-validator"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating version input"
|
||||
It "accepts valid semantic version"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts semantic version with v prefix"
|
||||
When call validate_input_python "version-validator" "version" "v1.2.3"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts prerelease version"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3-alpha"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts prerelease with number"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3-alpha.1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts build metadata"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3+build.1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts prerelease with build metadata"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3-alpha.1+build.1"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts CalVer format"
|
||||
When call validate_input_python "version-validator" "version" "2024.3.1"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "version-validator" "version" "invalid.version"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects version with shell expansion"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "version-validator" "version" ""
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating validation-regex input"
|
||||
It "accepts valid regex pattern"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[0-9]+\.[0-9]+\.[0-9]+$"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts semantic version regex"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[0-9]+\.[0-9]+(\.[0-9]+)?(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts empty validation-regex (uses default)"
|
||||
When call validate_input_python "version-validator" "validation-regex" ""
|
||||
The status should be success
|
||||
End
|
||||
It "accepts valid regex patterns with quantifiers"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[0-9]+\\.[0-9]+$"
|
||||
The status should be success
|
||||
End
|
||||
It "rejects regex with command injection"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[0-9]+$; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating ReDoS patterns"
|
||||
It "rejects nested quantifiers (a+)+"
|
||||
When call validate_input_python "version-validator" "validation-regex" "(a+)+"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects nested quantifiers (a*)+"
|
||||
When call validate_input_python "version-validator" "validation-regex" "(a*)+"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects nested quantifiers (a+)*"
|
||||
When call validate_input_python "version-validator" "validation-regex" "(a+)*"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects nested quantifiers (a*)*"
|
||||
When call validate_input_python "version-validator" "validation-regex" "(a*)*"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects quantified groups (a+){2,5}"
|
||||
When call validate_input_python "version-validator" "validation-regex" "(a+){2,5}"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects consecutive quantifiers .*.* (ReDoS)"
|
||||
When call validate_input_python "version-validator" "validation-regex" ".*.*"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects consecutive quantifiers .*+ (ReDoS)"
|
||||
When call validate_input_python "version-validator" "validation-regex" ".*+"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects duplicate alternatives (a|a)+"
|
||||
When call validate_input_python "version-validator" "validation-regex" "(a|a)+"
|
||||
The status should be failure
|
||||
End
|
||||
It "rejects overlapping alternatives (a|ab)+"
|
||||
When call validate_input_python "version-validator" "validation-regex" "(a|ab)+"
|
||||
The status should be failure
|
||||
End
|
||||
It "accepts safe pattern with single quantifier"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[0-9]+$"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts safe pattern with character class"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[a-zA-Z0-9]+$"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts safe pattern with optional group"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[0-9]+(\\.[0-9]+)?$"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts safe alternation without repetition"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^(alpha|beta|gamma)$"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating language input"
|
||||
It "accepts valid language name"
|
||||
When call validate_input_python "version-validator" "language" "nodejs"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts version as language"
|
||||
When call validate_input_python "version-validator" "language" "version"
|
||||
The status should be success
|
||||
End
|
||||
It "accepts empty language (uses default)"
|
||||
When call validate_input_python "version-validator" "language" ""
|
||||
The status should be success
|
||||
End
|
||||
It "rejects language with command injection"
|
||||
When call validate_input_python "version-validator" "language" "version; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when checking action.yml structure"
|
||||
It "has valid YAML syntax"
|
||||
When call validate_action_yml_quiet "$ACTION_FILE"
|
||||
The status should be success
|
||||
End
|
||||
It "has correct action name"
|
||||
name=$(get_action_name "$ACTION_FILE")
|
||||
When call echo "$name"
|
||||
The output should match pattern "*Version*"
|
||||
End
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "version"
|
||||
The output should include "validation-regex"
|
||||
The output should include "language"
|
||||
End
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "is-valid"
|
||||
The output should include "validated-version"
|
||||
The output should include "error-message"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "requires version input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "version" "required"
|
||||
The status should be success
|
||||
The output should equal "required"
|
||||
End
|
||||
It "has validation-regex as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "validation-regex" "optional"
|
||||
The status should be success
|
||||
The output should equal "optional"
|
||||
End
|
||||
It "has language as optional input"
|
||||
When call uv run "_tests/shared/validation_core.py" --property "$ACTION_FILE" "language" "optional"
|
||||
The status should be success
|
||||
The output should equal "optional"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in version"
|
||||
When call validate_input_python "version-validator" "version" "../1.2.3"
|
||||
The status should be failure
|
||||
End
|
||||
It "validates against shell metacharacters in version"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3|echo"
|
||||
The status should be failure
|
||||
End
|
||||
It "validates against backtick injection in language"
|
||||
When call validate_input_python "version-validator" "language" "version\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
It "validates against variable expansion in version"
|
||||
When call validate_input_python "version-validator" "version" "1.2.3\${HOME}"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing version validation functionality"
|
||||
It "validates semantic version format restrictions"
|
||||
When call validate_input_python "version-validator" "version" "1.2"
|
||||
The status should be success
|
||||
End
|
||||
It "validates regex pattern safety"
|
||||
When call validate_input_python "version-validator" "validation-regex" "^[0-9]+$"
|
||||
The status should be success
|
||||
End
|
||||
It "validates language parameter format"
|
||||
When call validate_input_python "version-validator" "language" "NODEJS"
|
||||
The status should be success
|
||||
End
|
||||
It "validates complex version formats"
|
||||
When call validate_input_python "version-validator" "version" "1.0.0-beta.1+exp.sha.5114f85"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
End
|
||||
Reference in New Issue
Block a user