mirror of
https://github.com/ivuorinen/actions.git
synced 2026-01-26 03:23:59 +00:00
feat: simplify actions (#353)
* feat: first pass simplification
* refactor: simplify actions repository structure
Major simplification reducing actions from 44 to 30:
Consolidations:
- Merge biome-check + biome-fix → biome-lint (mode: check/fix)
- Merge eslint-check + eslint-fix → eslint-lint (mode: check/fix)
- Merge prettier-check + prettier-fix → prettier-lint (mode: check/fix)
- Merge 5 version-detect actions → language-version-detect (language param)
Removals:
- common-file-check, common-retry (better served by external tools)
- docker-publish-gh, docker-publish-hub (consolidated into docker-publish)
- github-release (redundant with existing tooling)
- set-git-config (no longer needed)
- version-validator (functionality moved to language-version-detect)
Fixes:
- Rewrite docker-publish to use official Docker actions directly
- Update validate-inputs example (eslint-fix → eslint-lint)
- Update tests and documentation for new structure
Result: ~6,000 lines removed, cleaner action catalog, maintained functionality.
* refactor: complete action simplification and cleanup
Remove deprecated actions and update remaining actions:
Removed:
- common-file-check, common-retry: utility actions
- docker-publish-gh, docker-publish-hub: replaced by docker-publish wrapper
- github-release, version-validator, set-git-config: no longer needed
- Various version-detect actions: replaced by language-version-detect
Updated:
- docker-publish: rewrite as simple wrapper using official Docker actions
- validate-inputs: update example (eslint-fix → eslint-lint)
- Multiple actions: update configurations and remove deprecated dependencies
- Tests: update integration/unit tests for new structure
- Documentation: update README, remove test for deleted actions
Configuration updates:
- Linter configs, ignore files for new structure
- Makefile, pyproject.toml updates
* fix: enforce POSIX compliance in GitHub workflows
Convert all workflow shell scripts to POSIX-compliant sh:
Critical fixes:
- Replace bash with sh in all shell declarations
- Replace [[ with [ for test conditions
- Replace == with = for string comparisons
- Replace set -euo pipefail with set -eu
- Split compound AND conditions into separate [ ] tests
Files updated:
- .github/workflows/test-actions.yml (7 shell declarations, 10 test operators)
- .github/workflows/security-suite.yml (set -eu)
- .github/workflows/action-security.yml (2 shell declarations)
- .github/workflows/pr-lint.yml (3 shell declarations)
- .github/workflows/issue-stats.yml (1 shell declaration)
Ensures compatibility with minimal sh implementations and aligns with
CLAUDE.md standards requiring POSIX shell compliance across all scripts.
All tests pass: 764 pytest tests, 100% coverage.
* fix: add missing permissions for private repository support
Add critical permissions to pr-lint workflow for private repositories:
Workflow-level permissions:
+ packages: read - Access private npm/PyPI/Composer packages
Job-level permissions:
+ packages: read - Access private packages during dependency installation
+ checks: write - Create and update check runs
Fixes failures when:
- Installing private npm packages from GitHub Packages
- Installing private Composer dependencies
- Installing private Python packages
- Creating status checks with github-script
Valid permission scopes per actionlint:
actions, attestations, checks, contents, deployments, discussions,
id-token, issues, models, packages, pages, pull-requests,
repository-projects, security-events, statuses
Note: "workflows" and "metadata" are NOT valid permission scopes
(they are PAT-only scopes or auto-granted respectively).
* docs: update readmes
* fix: replace bash-specific 'source' with POSIX '.' command
Replace all occurrences of 'source' with '.' (dot) for POSIX compliance:
Changes in python-lint-fix/action.yml:
- Line 165: source .venv/bin/activate → . .venv/bin/activate
- Line 179: source .venv/bin/activate → . .venv/bin/activate
- Line 211: source .venv/bin/activate → . .venv/bin/activate
Also fixed bash-specific test operator:
- Line 192: [[ "$FAIL_ON_ERROR" == "true" ]] → [ "$FAIL_ON_ERROR" = "true" ]
The 'source' command is bash-specific. POSIX sh uses '.' (dot) to source files.
Both commands have identical functionality but '.' is portable across all
POSIX-compliant shells.
* security: fix code injection vulnerability in docker-publish
Fix CodeQL code injection warning (CWE-094, CWE-095, CWE-116):
Issue: inputs.context was used directly in GitHub Actions expression
without sanitization at line 194, allowing potential code injection
by external users.
Fix: Use environment variable indirection to prevent expression injection:
- Added env.BUILD_CONTEXT to capture inputs.context
- Changed context parameter to use ${{ env.BUILD_CONTEXT }}
Environment variables are evaluated after expression compilation,
preventing malicious code execution during workflow parsing.
Security Impact: Medium severity (CVSS 5.0)
Identified by: GitHub Advanced Security (CodeQL)
Reference: https://github.com/ivuorinen/actions/pull/353#pullrequestreview-3481935924
* security: prevent credential persistence in pr-lint checkout
Add persist-credentials: false to checkout step to mitigate untrusted
checkout vulnerability. This prevents GITHUB_TOKEN from being accessible
to potentially malicious PR code.
Fixes: CodeQL finding CWE-829 (untrusted checkout on privileged workflow)
* fix: prevent security bot from overwriting unrelated comments
Replace broad string matching with unique HTML comment marker for
identifying bot-generated comments. Previously, any comment containing
'Security Analysis' or '🔐 GitHub Actions Permissions' would be
overwritten, causing data loss.
Changes:
- Add unique marker: <!-- security-analysis-bot-comment -->
- Prepend marker to generated comment body
- Update comment identification to use marker only
- Add defensive null check for comment.body
This fixes critical data loss bug where user comments could be
permanently overwritten by the security analysis bot.
Follows same proven pattern as test-actions.yml coverage comments.
* improve: show concise permissions diff instead of full blocks
Replace verbose full-block permissions diff with line-by-line changes.
Now shows only added/removed permissions, making output much more
readable.
Changes:
- Parse permissions into individual lines
- Compare old vs new to identify actual changes
- Show only removed (-) and added (+) lines in diff
- Collapse unchanged permissions into details section (≤3 items)
- Show count summary for many unchanged permissions (>3 items)
Example output:
Before: 30+ lines showing entire permissions block
After: 3-5 lines showing only what changed
This addresses user feedback that permissions changes were too verbose.
* security: add input validation and trust model documentation
Add comprehensive security validation for docker-publish action to prevent
code injection attacks (CWE-094, CWE-116).
Changes:
- Add validation for context input (reject absolute paths, warn on URLs)
- Add validation for dockerfile input (reject absolute/URL paths)
- Document security trust model in README
- Add best practices for secure usage
- Explain validation rules and threat model
Prevents malicious actors from:
- Building from arbitrary file system locations
- Fetching Dockerfiles from untrusted remote sources
- Executing code injection through build context manipulation
Addresses: CodeRabbit review comments #2541434325, #2541549615
Fixes: GitHub Advanced Security code injection findings
* security: replace unmaintained nick-fields/retry with step-security/retry
Replace nick-fields/retry with step-security/retry across all 4 actions:
- csharp-build/action.yml
- php-composer/action.yml
- go-build/action.yml
- ansible-lint-fix/action.yml
The nick-fields/retry action has security vulnerabilities and low maintenance.
step-security/retry is a drop-in replacement with full API compatibility.
All inputs (timeout_minutes, max_attempts, command, retry_wait_seconds) are
compatible. Using SHA-pinned version for security.
Addresses CodeRabbit review comment #2541549598
* test: add is_input_required() helper function
Add helper function to check if an action input is required, reducing
duplication across test suites.
The function:
- Takes action_file and input_name as parameters
- Uses validation_core.py to query the 'required' property
- Returns 0 (success) if input is required
- Returns 1 (failure) if input is optional
This DRY improvement addresses CodeRabbit review comment #2541549572
* feat: add mode validation convention mapping
Add "mode" to the validation conventions mapping for lint actions
(eslint-lint, biome-lint, prettier-lint).
Note: The update-validators script doesn't currently recognize "string"
as a validator type, so mode validation coverage remains at 93%. The
actions already have inline validation for mode (check|fix), so this is
primarily for improving coverage metrics.
Addresses part of CodeRabbit review comment #2541549570
(validation coverage improvement)
* docs: fix CLAUDE.md action counts and add missing action
- Update action count from 31 to 29 (line 42)
- Add missing 'action-versioning' to Utilities category (line 74)
Addresses CodeRabbit review comments #2541553130 and #2541553110
* docs: add security considerations to docker-publish
Add security documentation to both action.yml header and README.md:
- Trust model explanation
- Input validation details for context and dockerfile
- Attack prevention information
- Best practices for secure usage
The documentation was previously removed when README was autogenerated.
Now documented in both places to ensure it persists.
* fix: correct step ID reference in docker-build
Fix incorrect step ID reference in platforms output:
- Changed steps.platforms.outputs.built to steps.detect-platforms.outputs.platforms
- The step is actually named 'detect-platforms' not 'platforms'
- Ensures output correctly references the detect-platforms step defined at line 188
* fix: ensure docker-build platforms output is always available
Make detect-platforms step unconditional to fix broken output contract.
The platforms output (line 123) references steps.detect-platforms.outputs.platforms,
but the step only ran when auto-detect-platforms was true (default: false).
This caused undefined output in most cases.
Changes:
- Remove 'if' condition from detect-platforms step
- Step now always runs and always produces platforms output
- When auto-detect is false: outputs configured architectures
- When auto-detect is true: outputs detected platforms or falls back to architectures
- Add '|| true' to grep to prevent errors when no platforms detected
Fixes CodeRabbit review comment #2541824904
* security: remove env var indirection in docker-publish BUILD_CONTEXT
Remove BUILD_CONTEXT env var indirection to address GitHub Advanced Security alert.
The inputs.context is validated at lines 137-147 (rejects absolute paths, warns on URLs)
before being used, so the env var indirection is unnecessary and triggers false positive
code injection warnings.
Changes:
- Remove BUILD_CONTEXT env var (line 254)
- Use inputs.context directly (line 256 → 254)
- Input validation remains in place (lines 137-147)
Fixes GitHub Advanced Security code injection alerts (comments #2541405269, #2541522320)
* feat: implement mode_enum validator for lint actions
Add mode_enum validator to validate mode inputs in linting actions.
Changes to conventions.py:
- Add 'mode_enum' to exact_matches mapping (line 215)
- Add 'mode_enum' to PHP-specific validators list (line 560)
- Implement _validate_mode_enum() method (lines 642-660)
- Validates mode values against ['check', 'fix']
- Returns clear error messages for invalid values
Updated rules.yml files:
- biome-lint: Add mode: mode_enum convention
- eslint-lint: Add mode: mode_enum convention
- prettier-lint: Add mode: mode_enum convention
- All rules.yml: Fix YAML formatting with yamlfmt
This addresses PR #353 comment #2541522326 which reported that mode validation
was being skipped due to unrecognized 'string' type, reducing coverage to 93%.
Tested with biome-lint action - correctly rejects invalid values and accepts
valid 'check' and 'fix' values.
* docs: update action count from 29 to 30 in CLAUDE.md
Update two references to action count in CLAUDE.md:
- Line 42: repository_overview memory description
- Line 74: Repository Structure section header
The repository has 30 actions total (29 listed + validate-inputs).
Addresses PR #353 comment #2541549588.
* docs: use pinned version ref in language-version-detect README
Change usage example from @main to @v2025 for security best practices.
Using pinned version refs (instead of @main) ensures:
- Predictable behavior across workflow runs
- Protection against breaking changes
- Better security through immutable references
Follows repository convention documented in main README and CLAUDE.md.
Addresses PR #353 comment #2541549588.
* refactor: remove deprecated add-snippets input from codeql-analysis
Remove add-snippets input which has been deprecated by GitHub's CodeQL action
and no longer has any effect.
Changes:
- Remove add-snippets input definition (lines 93-96)
- Remove reference in init step (line 129)
- Remove reference in analyze step (line 211)
- Regenerate README and rules.yml
This is a non-breaking change since:
- Default was 'false' (minimal usage expected)
- GitHub's action already ignores this parameter
- Aligns with recent repository simplification efforts
* feat: add mode_enum validator and update rules
Add mode_enum validator support for lint actions and regenerate all validation rules:
Validator Changes:
- Add mode_enum to action_overrides for biome-lint, eslint-lint, prettier-lint
- Remove deprecated add-snippets from codeql-analysis overrides
Rules Updates:
- All 29 action rules.yml files regenerated with consistent YAML formatting
- biome-lint, eslint-lint, prettier-lint now validate mode input (check/fix)
- Improved coverage for lint actions (79% → 83% for biome, 93% for eslint, 79% for prettier)
Documentation:
- Fix language-version-detect README to use @v2025 (not @main)
- Remove outdated docker-publish security docs (now handled by official actions)
This completes PR #353 review feedback implementation.
* fix: replace bash-specific $'\n' with POSIX-compliant printf
Replace non-POSIX $'\n' syntax in tag building loop with printf-based
approach that works in any POSIX shell.
Changed:
- Line 216: tags="${tags}"$'\n'"${image}:${tag}"
+ Line 216: tags="$(printf '%s\n%s' "$tags" "${image}:${tag}")"
This ensures docker-publish/action.yml runs correctly on systems using
/bin/sh instead of bash.
This commit is contained in:
@@ -57,6 +57,21 @@ get_action_name() {
|
||||
uv run "$script_dir/../shared/validation_core.py" --name "$action_file"
|
||||
}
|
||||
|
||||
# Check if an input is required in an action.yml file
|
||||
is_input_required() {
|
||||
local action_file="$1"
|
||||
local input_name="$2"
|
||||
local script_dir
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Get the 'required' property for the input
|
||||
local required_status
|
||||
required_status=$(uv run "$script_dir/../shared/validation_core.py" --property "$action_file" "$input_name" "required")
|
||||
|
||||
# Return 0 (success) if input is required, 1 (failure) if optional
|
||||
[[ $required_status == "required" ]]
|
||||
}
|
||||
|
||||
# Test input validation using Python validation module
|
||||
test_input_validation() {
|
||||
local action_dir="$1"
|
||||
@@ -348,5 +363,5 @@ run_action_tests() {
|
||||
}
|
||||
|
||||
# Export all functions
|
||||
export -f validate_action_yml get_action_inputs get_action_outputs get_action_name
|
||||
export -f validate_action_yml get_action_inputs get_action_outputs get_action_name is_input_required
|
||||
export -f test_input_validation test_action_outputs test_external_usage measure_action_time run_action_tests
|
||||
|
||||
@@ -1,440 +0,0 @@
|
||||
---
|
||||
name: Integration Test - GitHub Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'github-release/**'
|
||||
- '_tests/integration/workflows/github-release-test.yml'
|
||||
|
||||
jobs:
|
||||
test-github-release-validation:
|
||||
name: Test Input Validation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test invalid version format
|
||||
run: |
|
||||
VERSION='not.a.version'
|
||||
if [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Invalid version format should have failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Invalid version format correctly rejected"
|
||||
|
||||
- name: Test version with alphabetic characters
|
||||
run: |
|
||||
VERSION='abc.def.ghi'
|
||||
if [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Alphabetic version should have failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Alphabetic version correctly rejected"
|
||||
|
||||
- name: Test valid version formats
|
||||
run: |
|
||||
for version in "1.2.3" "v1.2.3" "1.0.0-alpha" "2.0.0+build"; do
|
||||
if ! [[ "$version" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Valid version '$version' should have passed"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Valid version '$version' accepted"
|
||||
done
|
||||
|
||||
test-github-release-version-formats:
|
||||
name: Test Version Format Support
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test basic SemVer (dry run)
|
||||
run: |
|
||||
echo "Testing basic SemVer format: 1.2.3"
|
||||
VERSION="1.2.3"
|
||||
if ! [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Valid version rejected"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Basic SemVer format accepted"
|
||||
|
||||
- name: Test SemVer with v prefix
|
||||
run: |
|
||||
echo "Testing SemVer with v prefix: v1.2.3"
|
||||
VERSION="v1.2.3"
|
||||
if ! [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Valid version rejected"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ SemVer with v prefix accepted"
|
||||
|
||||
- name: Test prerelease version
|
||||
run: |
|
||||
echo "Testing prerelease version: 1.0.0-alpha.1"
|
||||
VERSION="1.0.0-alpha.1"
|
||||
if ! [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Valid prerelease version rejected"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Prerelease version accepted"
|
||||
|
||||
- name: Test version with build metadata
|
||||
run: |
|
||||
echo "Testing version with build metadata: 1.0.0+build.123"
|
||||
VERSION="1.0.0+build.123"
|
||||
if ! [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Valid build metadata version rejected"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Version with build metadata accepted"
|
||||
|
||||
- name: Test complex version
|
||||
run: |
|
||||
echo "Testing complex version: v2.1.0-rc.1+build.456"
|
||||
VERSION="v2.1.0-rc.1+build.456"
|
||||
if ! [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Valid complex version rejected"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Complex version accepted"
|
||||
|
||||
test-github-release-tool-availability:
|
||||
name: Test Tool Availability Checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test gh CLI detection logic
|
||||
run: |
|
||||
# Test the logic for checking gh availability
|
||||
# In actual action, this would fail if gh is not available
|
||||
if command -v gh >/dev/null 2>&1; then
|
||||
echo "✓ gh CLI is available in this environment"
|
||||
gh --version
|
||||
else
|
||||
echo "⚠️ gh CLI not available in test environment (would fail in actual action)"
|
||||
echo "✓ Tool detection logic works correctly"
|
||||
fi
|
||||
|
||||
- name: Test jq detection logic
|
||||
run: |
|
||||
# Test the logic for checking jq availability
|
||||
# In actual action, this would fail if jq is not available
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
echo "✓ jq is available in this environment"
|
||||
jq --version
|
||||
else
|
||||
echo "⚠️ jq not available in test environment (would fail in actual action)"
|
||||
echo "✓ Tool detection logic works correctly"
|
||||
fi
|
||||
|
||||
- name: Test tool requirement validation
|
||||
run: |
|
||||
# Verify the action correctly checks for required tools
|
||||
REQUIRED_TOOLS=("gh" "jq")
|
||||
echo "Testing tool requirement checks..."
|
||||
|
||||
for tool in "${REQUIRED_TOOLS[@]}"; do
|
||||
if command -v "$tool" >/dev/null 2>&1; then
|
||||
echo " ✓ $tool: available"
|
||||
else
|
||||
echo " ⚠️ $tool: not available (action would fail at this check)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "✓ Tool requirement validation logic verified"
|
||||
|
||||
- name: Test gh authentication logic
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Test authentication detection logic
|
||||
if [[ -n "$GITHUB_TOKEN" ]]; then
|
||||
echo "✓ GITHUB_TOKEN environment variable is set"
|
||||
else
|
||||
echo "⚠️ GITHUB_TOKEN not set in test environment"
|
||||
fi
|
||||
|
||||
# Test gh auth status check (without requiring it to pass)
|
||||
if command -v gh >/dev/null 2>&1; then
|
||||
if gh auth status >/dev/null 2>&1; then
|
||||
echo "✓ gh authentication successful"
|
||||
else
|
||||
echo "⚠️ gh auth check failed (expected without proper setup)"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ gh not available, skipping auth check"
|
||||
fi
|
||||
|
||||
echo "✓ Authentication detection logic verified"
|
||||
|
||||
test-github-release-changelog-validation:
|
||||
name: Test Changelog Validation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test empty changelog (should trigger autogenerated notes)
|
||||
run: |
|
||||
echo "Testing empty changelog handling..."
|
||||
CHANGELOG=""
|
||||
if [[ -n "$CHANGELOG" ]]; then
|
||||
echo "❌ ERROR: Empty string should be empty"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Empty changelog correctly detected"
|
||||
|
||||
- name: Test normal changelog
|
||||
run: |
|
||||
echo "Testing normal changelog..."
|
||||
CHANGELOG="## Features
|
||||
- Added new feature
|
||||
- Improved performance"
|
||||
|
||||
if [[ -z "$CHANGELOG" ]]; then
|
||||
echo "❌ ERROR: Changelog should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${#CHANGELOG} -gt 10000 ]]; then
|
||||
echo "⚠️ Changelog is very long"
|
||||
fi
|
||||
|
||||
echo "✓ Normal changelog processed correctly"
|
||||
|
||||
- name: Test very long changelog warning
|
||||
run: |
|
||||
echo "Testing very long changelog..."
|
||||
# Create a changelog with >10000 characters
|
||||
CHANGELOG=$(printf 'A%.0s' {1..10001})
|
||||
|
||||
if [[ ${#CHANGELOG} -le 10000 ]]; then
|
||||
echo "❌ ERROR: Test changelog should be >10000 chars"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Long changelog warning would trigger (${#CHANGELOG} characters)"
|
||||
|
||||
test-github-release-changelog-types:
|
||||
name: Test Changelog Content Types
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test multiline changelog
|
||||
run: |
|
||||
echo "Testing multiline changelog..."
|
||||
CHANGELOG="## Version 1.2.3
|
||||
|
||||
### Features
|
||||
- Feature A
|
||||
- Feature B
|
||||
|
||||
### Bug Fixes
|
||||
- Fix #123
|
||||
- Fix #456"
|
||||
|
||||
echo "✓ Multiline changelog supported"
|
||||
|
||||
- name: Test changelog with special characters
|
||||
run: |
|
||||
echo "Testing changelog with special characters..."
|
||||
CHANGELOG='## Release Notes
|
||||
|
||||
Special chars: $, `, \, ", '\'', !, @, #, %, &, *, (, )'
|
||||
|
||||
echo "✓ Special characters in changelog supported"
|
||||
|
||||
- name: Test changelog with markdown
|
||||
run: |
|
||||
echo "Testing changelog with markdown..."
|
||||
CHANGELOG="## Changes
|
||||
|
||||
**Bold** and *italic* text
|
||||
|
||||
- [x] Task completed
|
||||
- [ ] Task pending
|
||||
|
||||
\`\`\`bash
|
||||
echo 'code block'
|
||||
\`\`\`
|
||||
|
||||
| Table | Header |
|
||||
|-------|--------|
|
||||
| Cell | Data |"
|
||||
|
||||
echo "✓ Markdown in changelog supported"
|
||||
|
||||
test-github-release-output-format:
|
||||
name: Test Output Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Verify output structure (mock test)
|
||||
run: |
|
||||
echo "Testing output structure..."
|
||||
|
||||
# Check if jq is available for this test
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "⚠️ jq not available, skipping JSON parsing test"
|
||||
echo "✓ Output format validation logic verified (jq would be required in actual action)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Mock release info JSON (similar to gh release view output)
|
||||
RELEASE_INFO='{
|
||||
"url": "https://github.com/owner/repo/releases/tag/v1.0.0",
|
||||
"id": "RE_12345",
|
||||
"uploadUrl": "https://uploads.github.com/repos/owner/repo/releases/12345/assets{?name,label}"
|
||||
}'
|
||||
|
||||
# Extract outputs
|
||||
release_url=$(echo "$RELEASE_INFO" | jq -r '.url')
|
||||
release_id=$(echo "$RELEASE_INFO" | jq -r '.id')
|
||||
upload_url=$(echo "$RELEASE_INFO" | jq -r '.uploadUrl')
|
||||
|
||||
echo "Release URL: $release_url"
|
||||
echo "Release ID: $release_id"
|
||||
echo "Upload URL: $upload_url"
|
||||
|
||||
# Verify output format
|
||||
if [[ ! "$release_url" =~ ^https://github\.com/.*/releases/tag/.* ]]; then
|
||||
echo "❌ ERROR: Invalid release URL format"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$release_id" =~ ^RE_.* ]]; then
|
||||
echo "❌ ERROR: Invalid release ID format"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$upload_url" =~ ^https://uploads\.github\.com/.* ]]; then
|
||||
echo "❌ ERROR: Invalid upload URL format"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Output format validation passed"
|
||||
|
||||
test-github-release-integration-scenarios:
|
||||
name: Test Integration Scenarios
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test release workflow without actual creation
|
||||
run: |
|
||||
echo "Simulating release workflow..."
|
||||
|
||||
# Validate version
|
||||
VERSION="v1.2.3-test"
|
||||
if ! [[ "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ ERROR: Version validation failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Version validation passed"
|
||||
|
||||
# Check tool availability (non-fatal for test environment)
|
||||
gh_available=false
|
||||
jq_available=false
|
||||
|
||||
if command -v gh >/dev/null 2>&1; then
|
||||
echo "✓ gh CLI is available"
|
||||
gh_available=true
|
||||
else
|
||||
echo "⚠️ gh not available (would fail in actual action)"
|
||||
fi
|
||||
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
echo "✓ jq is available"
|
||||
jq_available=true
|
||||
else
|
||||
echo "⚠️ jq not available (would fail in actual action)"
|
||||
fi
|
||||
|
||||
# Create mock changelog
|
||||
CHANGELOG="Test release notes"
|
||||
NOTES_FILE="$(mktemp)"
|
||||
printf '%s' "$CHANGELOG" > "$NOTES_FILE"
|
||||
|
||||
# Verify changelog file
|
||||
if [[ ! -f "$NOTES_FILE" ]]; then
|
||||
echo "❌ ERROR: Changelog file not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONTENT=$(cat "$NOTES_FILE")
|
||||
if [[ "$CONTENT" != "$CHANGELOG" ]]; then
|
||||
echo "❌ ERROR: Changelog content mismatch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$NOTES_FILE"
|
||||
|
||||
echo "✓ Release workflow simulation passed"
|
||||
|
||||
- name: Test autogenerated changelog scenario
|
||||
run: |
|
||||
echo "Testing autogenerated changelog scenario..."
|
||||
|
||||
VERSION="v2.0.0"
|
||||
CHANGELOG=""
|
||||
|
||||
if [[ -z "$CHANGELOG" ]]; then
|
||||
echo "✓ Would use --generate-notes flag"
|
||||
else
|
||||
echo "✓ Would use custom changelog"
|
||||
fi
|
||||
|
||||
- name: Test custom changelog scenario
|
||||
run: |
|
||||
echo "Testing custom changelog scenario..."
|
||||
|
||||
VERSION="v2.1.0"
|
||||
CHANGELOG="## Custom Release Notes
|
||||
|
||||
This release includes:
|
||||
- Feature X
|
||||
- Bug fix Y"
|
||||
|
||||
if [[ -n "$CHANGELOG" ]]; then
|
||||
echo "✓ Would use --notes-file with custom changelog"
|
||||
else
|
||||
echo "✓ Would use --generate-notes"
|
||||
fi
|
||||
|
||||
integration-test-summary:
|
||||
name: Integration Test Summary
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- test-github-release-validation
|
||||
- test-github-release-version-formats
|
||||
- test-github-release-tool-availability
|
||||
- test-github-release-changelog-validation
|
||||
- test-github-release-changelog-types
|
||||
- test-github-release-output-format
|
||||
- test-github-release-integration-scenarios
|
||||
steps:
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "=========================================="
|
||||
echo "GitHub Release Integration Tests - PASSED"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "✓ Input validation tests"
|
||||
echo "✓ Version format tests"
|
||||
echo "✓ Tool availability tests"
|
||||
echo "✓ Changelog validation tests"
|
||||
echo "✓ Changelog content tests"
|
||||
echo "✓ Output format tests"
|
||||
echo "✓ Integration scenario tests"
|
||||
echo ""
|
||||
echo "All github-release integration tests completed successfully!"
|
||||
@@ -4,10 +4,8 @@ on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'eslint-check/**'
|
||||
- 'eslint-fix/**'
|
||||
- 'prettier-check/**'
|
||||
- 'prettier-fix/**'
|
||||
- 'eslint-lint/**'
|
||||
- 'prettier-lint/**'
|
||||
- 'node-setup/**'
|
||||
- 'common-cache/**'
|
||||
- '_tests/integration/workflows/lint-fix-chain-test.yml'
|
||||
@@ -64,14 +62,15 @@ jobs:
|
||||
node-version: '18'
|
||||
working-directory: './test-project'
|
||||
|
||||
- name: Test eslint-check (should find errors)
|
||||
- name: Test eslint-lint check mode (should find errors)
|
||||
id: eslint-check
|
||||
uses: ./eslint-check
|
||||
uses: ./eslint-lint
|
||||
with:
|
||||
mode: 'check'
|
||||
working-directory: './test-project'
|
||||
continue-on-error: true
|
||||
|
||||
- name: Validate eslint-check found issues
|
||||
- name: Validate eslint-lint check found issues
|
||||
run: |
|
||||
echo "ESLint check outcome: ${{ steps.eslint-check.outcome }}"
|
||||
echo "Error count: ${{ steps.eslint-check.outputs.error-count }}"
|
||||
@@ -86,23 +85,24 @@ jobs:
|
||||
|
||||
echo "✅ ESLint check validated"
|
||||
|
||||
- name: Test eslint-fix (should fix issues)
|
||||
- name: Test eslint-lint fix mode (should fix issues)
|
||||
id: eslint-fix
|
||||
uses: ./eslint-fix
|
||||
uses: ./eslint-lint
|
||||
with:
|
||||
mode: 'fix'
|
||||
working-directory: './test-project'
|
||||
token: ${{ github.token }}
|
||||
email: 'test@example.com'
|
||||
username: 'test-user'
|
||||
|
||||
- name: Validate eslint-fix ran
|
||||
- name: Validate eslint-lint fix ran
|
||||
run: |
|
||||
echo "Fixed count: ${{ steps.eslint-fix.outputs.fixed-count }}"
|
||||
echo "Files fixed: ${{ steps.eslint-fix.outputs.files-fixed }}"
|
||||
echo "Errors fixed: ${{ steps.eslint-fix.outputs.errors-fixed }}"
|
||||
echo "Files changed: ${{ steps.eslint-fix.outputs.files-changed }}"
|
||||
|
||||
# Check that fixes were attempted
|
||||
if [[ -n "${{ steps.eslint-fix.outputs.fixed-count }}" ]]; then
|
||||
echo "✅ ESLint fixed ${{ steps.eslint-fix.outputs.fixed-count }} issues"
|
||||
if [[ -n "${{ steps.eslint-fix.outputs.errors-fixed }}" ]]; then
|
||||
echo "✅ ESLint fixed ${{ steps.eslint-fix.outputs.errors-fixed }} issues"
|
||||
else
|
||||
echo "⚠️ No fix count reported (may be expected if no fixable issues)"
|
||||
fi
|
||||
@@ -156,10 +156,11 @@ jobs:
|
||||
node-version: '18'
|
||||
working-directory: './test-prettier'
|
||||
|
||||
- name: Test prettier-check (should find issues)
|
||||
- name: Test prettier-lint check mode (should find issues)
|
||||
id: prettier-check
|
||||
uses: ./prettier-check
|
||||
uses: ./prettier-lint
|
||||
with:
|
||||
mode: 'check'
|
||||
working-directory: './test-prettier'
|
||||
continue-on-error: true
|
||||
|
||||
@@ -174,16 +175,17 @@ jobs:
|
||||
echo "⚠️ WARNING: Expected Prettier to find formatting issues"
|
||||
fi
|
||||
|
||||
- name: Test prettier-fix (should fix issues)
|
||||
- name: Test prettier-lint fix mode (should fix issues)
|
||||
id: prettier-fix
|
||||
uses: ./prettier-fix
|
||||
uses: ./prettier-lint
|
||||
with:
|
||||
mode: 'fix'
|
||||
working-directory: './test-prettier'
|
||||
token: ${{ github.token }}
|
||||
email: 'test@example.com'
|
||||
username: 'test-user'
|
||||
|
||||
- name: Validate prettier-fix ran
|
||||
- name: Validate prettier-lint fix ran
|
||||
run: |
|
||||
echo "Prettier fix completed"
|
||||
|
||||
@@ -261,22 +263,25 @@ jobs:
|
||||
|
||||
- name: Run ESLint check
|
||||
id: lint-check
|
||||
uses: ./eslint-check
|
||||
uses: ./eslint-lint
|
||||
with:
|
||||
mode: 'check'
|
||||
working-directory: './test-chain'
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run Prettier check
|
||||
id: format-check
|
||||
uses: ./prettier-check
|
||||
uses: ./prettier-lint
|
||||
with:
|
||||
mode: 'check'
|
||||
working-directory: './test-chain'
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run ESLint fix
|
||||
id: lint-fix
|
||||
uses: ./eslint-fix
|
||||
uses: ./eslint-lint
|
||||
with:
|
||||
mode: 'fix'
|
||||
working-directory: './test-chain'
|
||||
token: ${{ github.token }}
|
||||
email: 'test@example.com'
|
||||
@@ -284,8 +289,9 @@ jobs:
|
||||
|
||||
- name: Run Prettier fix
|
||||
id: format-fix
|
||||
uses: ./prettier-fix
|
||||
uses: ./prettier-lint
|
||||
with:
|
||||
mode: 'fix'
|
||||
working-directory: './test-chain'
|
||||
token: ${{ github.token }}
|
||||
email: 'test@example.com'
|
||||
|
||||
@@ -5,7 +5,6 @@ on:
|
||||
push:
|
||||
paths:
|
||||
- 'pre-commit/**'
|
||||
- 'set-git-config/**'
|
||||
- 'validate-inputs/**'
|
||||
- '_tests/integration/workflows/pre-commit-test.yml'
|
||||
|
||||
|
||||
@@ -151,14 +151,14 @@ discover_actions() {
|
||||
if [[ $action_name == *"$ACTION_FILTER"* ]]; then
|
||||
actions+=("$action_name")
|
||||
fi
|
||||
done < <(find "${TEST_ROOT}/.." -mindepth 1 -maxdepth 1 -type d -name "*-*" | sort)
|
||||
done < <(find "${TEST_ROOT}/.." -mindepth 2 -maxdepth 2 -type f -name "action.yml" -exec dirname {} \; | sort)
|
||||
else
|
||||
# All actions
|
||||
while IFS= read -r action_dir; do
|
||||
local action_name
|
||||
action_name=$(basename "$action_dir")
|
||||
actions+=("$action_name")
|
||||
done < <(find "${TEST_ROOT}/.." -mindepth 1 -maxdepth 1 -type d -name "*-*" | sort)
|
||||
done < <(find "${TEST_ROOT}/.." -mindepth 2 -maxdepth 2 -type f -name "action.yml" -exec dirname {} \; | sort)
|
||||
fi
|
||||
|
||||
log_info "Discovered ${#actions[@]} actions to test: ${actions[*]}"
|
||||
|
||||
142
_tests/unit/action-versioning/validation.spec.sh
Executable file
142
_tests/unit/action-versioning/validation.spec.sh
Executable file
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for action-versioning action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "action-versioning action"
|
||||
ACTION_DIR="action-versioning"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating major-version input"
|
||||
It "accepts valid year-based version (vYYYY)"
|
||||
When call validate_input_python "action-versioning" "major-version" "v2025"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid semantic version (v1)"
|
||||
When call validate_input_python "action-versioning" "major-version" "v1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid semantic version (v2)"
|
||||
When call validate_input_python "action-versioning" "major-version" "v2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts year-based version from 2020"
|
||||
When call validate_input_python "action-versioning" "major-version" "v2020"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts year-based version for 2030"
|
||||
When call validate_input_python "action-versioning" "major-version" "v2030"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects version without v prefix"
|
||||
When call validate_input_python "action-versioning" "major-version" "2025"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "action-versioning" "major-version" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty version"
|
||||
When call validate_input_python "action-versioning" "major-version" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "action-versioning" "major-version" "v2025; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token (classic)"
|
||||
When call validate_input_python "action-versioning" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid GitHub fine-grained token"
|
||||
When call validate_input_python "action-versioning" "token" "github_pat_1234567890123456789012345678901234567890123456789012345678901234567890a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty token (optional input)"
|
||||
When call validate_input_python "action-versioning" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "action-versioning" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "action-versioning" "token" "ghp_123456789012345678901234567890123456; 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 "Action Versioning"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "major-version"
|
||||
The output should include "token"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "updated"
|
||||
The output should include "commit-sha"
|
||||
The output should include "needs-annual-bump"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "requires major-version input"
|
||||
When call is_input_required "$ACTION_FILE" "major-version"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has token as optional input"
|
||||
When call is_input_required "$ACTION_FILE" "token"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in major-version"
|
||||
When call validate_input_python "action-versioning" "major-version" "v../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in major-version"
|
||||
When call validate_input_python "action-versioning" "major-version" "v2025|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command substitution in major-version"
|
||||
When call validate_input_python "action-versioning" "major-version" "v\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against path traversal in token"
|
||||
When call validate_input_python "action-versioning" "token" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
@@ -1,149 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,148 +0,0 @@
|
||||
#!/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
|
||||
230
_tests/unit/biome-lint/validation.spec.sh
Executable file
230
_tests/unit/biome-lint/validation.spec.sh
Executable file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for biome-lint action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "biome-lint action"
|
||||
ACTION_DIR="biome-lint"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating mode input"
|
||||
It "accepts check mode"
|
||||
When call validate_input_python "biome-lint" "mode" "check"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts fix mode"
|
||||
When call validate_input_python "biome-lint" "mode" "fix"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty mode (uses default)"
|
||||
When call validate_input_python "biome-lint" "mode" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid mode"
|
||||
When call validate_input_python "biome-lint" "mode" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects mode with command injection"
|
||||
When call validate_input_python "biome-lint" "mode" "check; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token (classic)"
|
||||
When call validate_input_python "biome-lint" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid GitHub fine-grained token"
|
||||
When call validate_input_python "biome-lint" "token" "github_pat_1234567890123456789012345678901234567890123456789012345678901234567890a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty token (optional)"
|
||||
When call validate_input_python "biome-lint" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "biome-lint" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "biome-lint" "token" "ghp_123456789012345678901234567890123456; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "biome-lint" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with hyphens"
|
||||
When call validate_input_python "biome-lint" "username" "my-bot-user"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty username (uses default)"
|
||||
When call validate_input_python "biome-lint" "username" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects username with command injection"
|
||||
When call validate_input_python "biome-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 "biome-lint" "email" "github-actions@github.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with plus sign"
|
||||
When call validate_input_python "biome-lint" "email" "user+bot@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with subdomain"
|
||||
When call validate_input_python "biome-lint" "email" "bot@ci.example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty email (uses default)"
|
||||
When call validate_input_python "biome-lint" "email" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid email format"
|
||||
When call validate_input_python "biome-lint" "email" "not-an-email"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email with command injection"
|
||||
When call validate_input_python "biome-lint" "email" "user@example.com; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts valid retry count (default)"
|
||||
When call validate_input_python "biome-lint" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts retry count of 1"
|
||||
When call validate_input_python "biome-lint" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts retry count of 10"
|
||||
When call validate_input_python "biome-lint" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty max-retries (uses default)"
|
||||
When call validate_input_python "biome-lint" "max-retries" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects negative retry count"
|
||||
When call validate_input_python "biome-lint" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric retry count"
|
||||
When call validate_input_python "biome-lint" "max-retries" "abc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects retry count with command injection"
|
||||
When call validate_input_python "biome-lint" "max-retries" "3; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating fail-on-error input"
|
||||
It "accepts true"
|
||||
When call validate_input_python "biome-lint" "fail-on-error" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false"
|
||||
When call validate_input_python "biome-lint" "fail-on-error" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty (uses default)"
|
||||
When call validate_input_python "biome-lint" "fail-on-error" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid boolean value"
|
||||
When call validate_input_python "biome-lint" "fail-on-error" "maybe"
|
||||
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 Lint"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "mode"
|
||||
The output should include "token"
|
||||
The output should include "username"
|
||||
The output should include "email"
|
||||
The output should include "max-retries"
|
||||
The output should include "fail-on-error"
|
||||
End
|
||||
|
||||
It "defines expected outputs"
|
||||
When call get_action_outputs "$ACTION_FILE"
|
||||
The output should include "status"
|
||||
The output should include "errors_count"
|
||||
The output should include "warnings_count"
|
||||
The output should include "files_changed"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional (with defaults)"
|
||||
When call is_input_required "$ACTION_FILE" "mode"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal"
|
||||
When call validate_input_python "biome-lint" "username" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters"
|
||||
When call validate_input_python "biome-lint" "email" "user@example.com|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command substitution"
|
||||
When call validate_input_python "biome-lint" "username" "\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,165 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,355 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,115 +0,0 @@
|
||||
#!/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
|
||||
527
_tests/unit/eslint-lint/validation.spec.sh
Executable file
527
_tests/unit/eslint-lint/validation.spec.sh
Executable file
@@ -0,0 +1,527 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for eslint-lint action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "eslint-lint action"
|
||||
ACTION_DIR="eslint-lint"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating mode input"
|
||||
It "accepts check mode"
|
||||
When call validate_input_python "eslint-lint" "mode" "check"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts fix mode"
|
||||
When call validate_input_python "eslint-lint" "mode" "fix"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty mode (uses default)"
|
||||
When call validate_input_python "eslint-lint" "mode" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid mode"
|
||||
When call validate_input_python "eslint-lint" "mode" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects mode with command injection"
|
||||
When call validate_input_python "eslint-lint" "mode" "check; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating working-directory input"
|
||||
It "accepts default directory"
|
||||
When call validate_input_python "eslint-lint" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid subdirectory"
|
||||
When call validate_input_python "eslint-lint" "working-directory" "src"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty working-directory (uses default)"
|
||||
When call validate_input_python "eslint-lint" "working-directory" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "eslint-lint" "working-directory" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects directory with command injection"
|
||||
When call validate_input_python "eslint-lint" "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-lint" "eslint-version" "latest"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts semantic version"
|
||||
When call validate_input_python "eslint-lint" "eslint-version" "8.57.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts major.minor version"
|
||||
When call validate_input_python "eslint-lint" "eslint-version" "8.57"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts major version"
|
||||
When call validate_input_python "eslint-lint" "eslint-version" "8"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts version with pre-release"
|
||||
When call validate_input_python "eslint-lint" "eslint-version" "9.0.0-beta.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty version (uses default)"
|
||||
When call validate_input_python "eslint-lint" "eslint-version" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "eslint-lint" "eslint-version" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "eslint-lint" "eslint-version" "8.57.0; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating config-file input"
|
||||
It "accepts default config file"
|
||||
When call validate_input_python "eslint-lint" "config-file" ".eslintrc"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts custom config file"
|
||||
When call validate_input_python "eslint-lint" "config-file" ".eslintrc.js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts config file in subdirectory"
|
||||
When call validate_input_python "eslint-lint" "config-file" "config/eslint.config.js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty config-file (uses default)"
|
||||
When call validate_input_python "eslint-lint" "config-file" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects config file with path traversal"
|
||||
When call validate_input_python "eslint-lint" "config-file" "../../../.eslintrc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects config file with command injection"
|
||||
When call validate_input_python "eslint-lint" "config-file" ".eslintrc; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating ignore-file input"
|
||||
It "accepts default ignore file"
|
||||
When call validate_input_python "eslint-lint" "ignore-file" ".eslintignore"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts custom ignore file"
|
||||
When call validate_input_python "eslint-lint" "ignore-file" ".eslintignore.custom"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty ignore-file (uses default)"
|
||||
When call validate_input_python "eslint-lint" "ignore-file" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects ignore file with path traversal"
|
||||
When call validate_input_python "eslint-lint" "ignore-file" "../../../.eslintignore"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects ignore file with command injection"
|
||||
When call validate_input_python "eslint-lint" "ignore-file" ".eslintignore; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating file-extensions input"
|
||||
It "accepts default extensions"
|
||||
When call validate_input_python "eslint-lint" "file-extensions" ".js,.jsx,.ts,.tsx"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts single extension"
|
||||
When call validate_input_python "eslint-lint" "file-extensions" ".js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts multiple extensions"
|
||||
When call validate_input_python "eslint-lint" "file-extensions" ".js,.ts,.mjs"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty file-extensions (uses default)"
|
||||
When call validate_input_python "eslint-lint" "file-extensions" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects extensions without leading dot"
|
||||
When call validate_input_python "eslint-lint" "file-extensions" "js,jsx"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects extensions with command injection"
|
||||
When call validate_input_python "eslint-lint" "file-extensions" ".js; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating cache input"
|
||||
It "accepts true"
|
||||
When call validate_input_python "eslint-lint" "cache" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false"
|
||||
When call validate_input_python "eslint-lint" "cache" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty cache (uses default)"
|
||||
When call validate_input_python "eslint-lint" "cache" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid boolean value"
|
||||
When call validate_input_python "eslint-lint" "cache" "maybe"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-warnings input"
|
||||
It "accepts default value 0"
|
||||
When call validate_input_python "eslint-lint" "max-warnings" "0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts positive integer"
|
||||
When call validate_input_python "eslint-lint" "max-warnings" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts large number"
|
||||
When call validate_input_python "eslint-lint" "max-warnings" "1000"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty max-warnings (uses default)"
|
||||
When call validate_input_python "eslint-lint" "max-warnings" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects negative number"
|
||||
When call validate_input_python "eslint-lint" "max-warnings" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric value"
|
||||
When call validate_input_python "eslint-lint" "max-warnings" "abc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects max-warnings with command injection"
|
||||
When call validate_input_python "eslint-lint" "max-warnings" "0; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating fail-on-error input"
|
||||
It "accepts true"
|
||||
When call validate_input_python "eslint-lint" "fail-on-error" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false"
|
||||
When call validate_input_python "eslint-lint" "fail-on-error" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty fail-on-error (uses default)"
|
||||
When call validate_input_python "eslint-lint" "fail-on-error" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid boolean value"
|
||||
When call validate_input_python "eslint-lint" "fail-on-error" "yes"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating report-format input"
|
||||
It "accepts stylish format"
|
||||
When call validate_input_python "eslint-lint" "report-format" "stylish"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts json format"
|
||||
When call validate_input_python "eslint-lint" "report-format" "json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts sarif format"
|
||||
When call validate_input_python "eslint-lint" "report-format" "sarif"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty report-format (uses default)"
|
||||
When call validate_input_python "eslint-lint" "report-format" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid format"
|
||||
When call validate_input_python "eslint-lint" "report-format" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects format with command injection"
|
||||
When call validate_input_python "eslint-lint" "report-format" "json; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts default value 3"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts retry count of 1"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts retry count of 10"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty max-retries (uses default)"
|
||||
When call validate_input_python "eslint-lint" "max-retries" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects negative retry count"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects retry count above 10"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric retry count"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "abc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects retry count with command injection"
|
||||
When call validate_input_python "eslint-lint" "max-retries" "3; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token (classic)"
|
||||
When call validate_input_python "eslint-lint" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid GitHub fine-grained token"
|
||||
When call validate_input_python "eslint-lint" "token" "github_pat_1234567890123456789012345678901234567890123456789012345678901234567890a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty token (optional)"
|
||||
When call validate_input_python "eslint-lint" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "eslint-lint" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "eslint-lint" "token" "ghp_123456789012345678901234567890123456; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "eslint-lint" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with hyphens"
|
||||
When call validate_input_python "eslint-lint" "username" "my-bot-user"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts alphanumeric username"
|
||||
When call validate_input_python "eslint-lint" "username" "user123"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty username (uses default)"
|
||||
When call validate_input_python "eslint-lint" "username" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects username with command injection"
|
||||
When call validate_input_python "eslint-lint" "username" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with special characters"
|
||||
When call validate_input_python "eslint-lint" "username" "user@bot"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "eslint-lint" "email" "github-actions@github.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with plus sign"
|
||||
When call validate_input_python "eslint-lint" "email" "user+bot@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with subdomain"
|
||||
When call validate_input_python "eslint-lint" "email" "bot@ci.example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty email (uses default)"
|
||||
When call validate_input_python "eslint-lint" "email" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid email format"
|
||||
When call validate_input_python "eslint-lint" "email" "not-an-email"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email with command injection"
|
||||
When call validate_input_python "eslint-lint" "email" "user@example.com; 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 "ESLint Lint"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "mode"
|
||||
The output should include "working-directory"
|
||||
The output should include "eslint-version"
|
||||
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"
|
||||
The output should include "max-retries"
|
||||
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 "status"
|
||||
The output should include "error-count"
|
||||
The output should include "warning-count"
|
||||
The output should include "sarif-file"
|
||||
The output should include "files-checked"
|
||||
The output should include "files-changed"
|
||||
The output should include "errors-fixed"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional (with defaults)"
|
||||
When call is_input_required "$ACTION_FILE" "mode"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in working-directory"
|
||||
When call validate_input_python "eslint-lint" "working-directory" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in mode"
|
||||
When call validate_input_python "eslint-lint" "mode" "check|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command substitution in config-file"
|
||||
When call validate_input_python "eslint-lint" "config-file" "\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against path traversal in token"
|
||||
When call validate_input_python "eslint-lint" "token" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in username"
|
||||
When call validate_input_python "eslint-lint" "username" "user&whoami"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in email"
|
||||
When call validate_input_python "eslint-lint" "email" "user@example.com\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
@@ -1,141 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,171 +0,0 @@
|
||||
#!/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
|
||||
297
_tests/unit/language-version-detect/validation.spec.sh
Executable file
297
_tests/unit/language-version-detect/validation.spec.sh
Executable file
@@ -0,0 +1,297 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for language-version-detect action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "language-version-detect action"
|
||||
ACTION_DIR="language-version-detect"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating language input"
|
||||
It "accepts php language"
|
||||
When call validate_input_python "language-version-detect" "language" "php"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts python language"
|
||||
When call validate_input_python "language-version-detect" "language" "python"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts go language"
|
||||
When call validate_input_python "language-version-detect" "language" "go"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts dotnet language"
|
||||
When call validate_input_python "language-version-detect" "language" "dotnet"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid language"
|
||||
When call validate_input_python "language-version-detect" "language" "javascript"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects empty language (required)"
|
||||
When call validate_input_python "language-version-detect" "language" ""
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects language with command injection"
|
||||
When call validate_input_python "language-version-detect" "language" "php; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects language with shell metacharacters"
|
||||
When call validate_input_python "language-version-detect" "language" "php|echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating default-version input for PHP"
|
||||
It "accepts valid PHP version 8.4"
|
||||
When call validate_input_python "language-version-detect" "default-version" "8.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid PHP version 8.3"
|
||||
When call validate_input_python "language-version-detect" "default-version" "8.3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid PHP version 7.4"
|
||||
When call validate_input_python "language-version-detect" "default-version" "7.4"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid PHP version with patch 8.3.1"
|
||||
When call validate_input_python "language-version-detect" "default-version" "8.3.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty default-version (uses language default)"
|
||||
When call validate_input_python "language-version-detect" "default-version" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid PHP version format"
|
||||
When call validate_input_python "language-version-detect" "default-version" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating default-version input for Python"
|
||||
It "accepts valid Python version 3.12"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.12"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Python version 3.11"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.11"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Python version 3.10"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Python version with patch 3.12.1"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.12.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Python version 3.9"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.9"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Python version 3.8"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.8"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating default-version input for Go"
|
||||
It "accepts valid Go version 1.21"
|
||||
When call validate_input_python "language-version-detect" "default-version" "1.21"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Go version 1.20"
|
||||
When call validate_input_python "language-version-detect" "default-version" "1.20"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Go version with patch 1.21.5"
|
||||
When call validate_input_python "language-version-detect" "default-version" "1.21.5"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid Go version 1.22"
|
||||
When call validate_input_python "language-version-detect" "default-version" "1.22"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating default-version input for .NET"
|
||||
It "accepts valid .NET version 7.0"
|
||||
When call validate_input_python "language-version-detect" "default-version" "7.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid .NET version 8.0"
|
||||
When call validate_input_python "language-version-detect" "default-version" "8.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid .NET version 6.0"
|
||||
When call validate_input_python "language-version-detect" "default-version" "6.0"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid .NET version with patch 7.0.1"
|
||||
When call validate_input_python "language-version-detect" "default-version" "7.0.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid .NET major version 7"
|
||||
When call validate_input_python "language-version-detect" "default-version" "7"
|
||||
The status should be success
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating default-version input edge cases"
|
||||
It "rejects version with v prefix"
|
||||
When call validate_input_python "language-version-detect" "default-version" "v3.12"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.12; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with shell metacharacters"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.12|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command substitution"
|
||||
When call validate_input_python "language-version-detect" "default-version" "\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects alphabetic version"
|
||||
When call validate_input_python "language-version-detect" "default-version" "latest"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token (classic)"
|
||||
When call validate_input_python "language-version-detect" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid GitHub fine-grained token"
|
||||
When call validate_input_python "language-version-detect" "token" "github_pat_1234567890123456789012345678901234567890123456789012345678901234567890a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty token (optional)"
|
||||
When call validate_input_python "language-version-detect" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "language-version-detect" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "language-version-detect" "token" "ghp_123456789012345678901234567890123456; 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 "Language Version Detect"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "language"
|
||||
The output should include "default-version"
|
||||
The output should include "token"
|
||||
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 testing input requirements"
|
||||
It "requires language input"
|
||||
When call is_input_required "$ACTION_FILE" "language"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "has default-version as optional input"
|
||||
When call is_input_required "$ACTION_FILE" "default-version"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "has token as optional input"
|
||||
When call is_input_required "$ACTION_FILE" "token"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in language"
|
||||
When call validate_input_python "language-version-detect" "language" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in language"
|
||||
When call validate_input_python "language-version-detect" "language" "php&whoami"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command substitution in language"
|
||||
When call validate_input_python "language-version-detect" "language" "php\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against path traversal in default-version"
|
||||
When call validate_input_python "language-version-detect" "default-version" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in default-version"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.12&echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command substitution in default-version"
|
||||
When call validate_input_python "language-version-detect" "default-version" "3.12\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against path traversal in token"
|
||||
When call validate_input_python "language-version-detect" "token" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
@@ -1,170 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,332 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,285 +0,0 @@
|
||||
#!/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
|
||||
515
_tests/unit/prettier-lint/validation.spec.sh
Executable file
515
_tests/unit/prettier-lint/validation.spec.sh
Executable file
@@ -0,0 +1,515 @@
|
||||
#!/usr/bin/env shellspec
|
||||
# Unit tests for prettier-lint action validation and logic
|
||||
# Framework is automatically loaded via spec_helper.sh
|
||||
|
||||
Describe "prettier-lint action"
|
||||
ACTION_DIR="prettier-lint"
|
||||
ACTION_FILE="$ACTION_DIR/action.yml"
|
||||
|
||||
Context "when validating mode input"
|
||||
It "accepts check mode"
|
||||
When call validate_input_python "prettier-lint" "mode" "check"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts fix mode"
|
||||
When call validate_input_python "prettier-lint" "mode" "fix"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty mode (uses default)"
|
||||
When call validate_input_python "prettier-lint" "mode" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid mode"
|
||||
When call validate_input_python "prettier-lint" "mode" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects mode with command injection"
|
||||
When call validate_input_python "prettier-lint" "mode" "check; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating working-directory input"
|
||||
It "accepts default directory"
|
||||
When call validate_input_python "prettier-lint" "working-directory" "."
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid subdirectory"
|
||||
When call validate_input_python "prettier-lint" "working-directory" "src"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty working-directory (uses default)"
|
||||
When call validate_input_python "prettier-lint" "working-directory" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects path traversal"
|
||||
When call validate_input_python "prettier-lint" "working-directory" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects directory with command injection"
|
||||
When call validate_input_python "prettier-lint" "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-lint" "prettier-version" "latest"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts semantic version"
|
||||
When call validate_input_python "prettier-lint" "prettier-version" "3.2.5"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts major.minor version"
|
||||
When call validate_input_python "prettier-lint" "prettier-version" "3.2"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts major version"
|
||||
When call validate_input_python "prettier-lint" "prettier-version" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts version with pre-release"
|
||||
When call validate_input_python "prettier-lint" "prettier-version" "3.0.0-alpha.1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty version (uses default)"
|
||||
When call validate_input_python "prettier-lint" "prettier-version" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid version format"
|
||||
When call validate_input_python "prettier-lint" "prettier-version" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects version with command injection"
|
||||
When call validate_input_python "prettier-lint" "prettier-version" "3.2.5; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating config-file input"
|
||||
It "accepts default config file"
|
||||
When call validate_input_python "prettier-lint" "config-file" ".prettierrc"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts custom config file"
|
||||
When call validate_input_python "prettier-lint" "config-file" ".prettierrc.js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts config file in subdirectory"
|
||||
When call validate_input_python "prettier-lint" "config-file" "config/prettier.config.js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty config-file (uses default)"
|
||||
When call validate_input_python "prettier-lint" "config-file" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects config file with path traversal"
|
||||
When call validate_input_python "prettier-lint" "config-file" "../../../.prettierrc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects config file with command injection"
|
||||
When call validate_input_python "prettier-lint" "config-file" ".prettierrc; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating ignore-file input"
|
||||
It "accepts default ignore file"
|
||||
When call validate_input_python "prettier-lint" "ignore-file" ".prettierignore"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts custom ignore file"
|
||||
When call validate_input_python "prettier-lint" "ignore-file" ".prettierignore.custom"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty ignore-file (uses default)"
|
||||
When call validate_input_python "prettier-lint" "ignore-file" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects ignore file with path traversal"
|
||||
When call validate_input_python "prettier-lint" "ignore-file" "../../../.prettierignore"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects ignore file with command injection"
|
||||
When call validate_input_python "prettier-lint" "ignore-file" ".prettierignore; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating file-pattern input"
|
||||
It "accepts default pattern"
|
||||
When call validate_input_python "prettier-lint" "file-pattern" "**/*.{js,jsx,ts,tsx,css,scss,json,md,yaml,yml}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts simple pattern"
|
||||
When call validate_input_python "prettier-lint" "file-pattern" "**/*.js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts multiple patterns"
|
||||
When call validate_input_python "prettier-lint" "file-pattern" "**/*.{js,ts}"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts specific directory pattern"
|
||||
When call validate_input_python "prettier-lint" "file-pattern" "src/**/*.js"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty file-pattern (uses default)"
|
||||
When call validate_input_python "prettier-lint" "file-pattern" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects pattern with command injection"
|
||||
When call validate_input_python "prettier-lint" "file-pattern" "**/*.js; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating cache input"
|
||||
It "accepts true"
|
||||
When call validate_input_python "prettier-lint" "cache" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false"
|
||||
When call validate_input_python "prettier-lint" "cache" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty cache (uses default)"
|
||||
When call validate_input_python "prettier-lint" "cache" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid boolean value"
|
||||
When call validate_input_python "prettier-lint" "cache" "maybe"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating fail-on-error input"
|
||||
It "accepts true"
|
||||
When call validate_input_python "prettier-lint" "fail-on-error" "true"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts false"
|
||||
When call validate_input_python "prettier-lint" "fail-on-error" "false"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty fail-on-error (uses default)"
|
||||
When call validate_input_python "prettier-lint" "fail-on-error" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid boolean value"
|
||||
When call validate_input_python "prettier-lint" "fail-on-error" "yes"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating report-format input"
|
||||
It "accepts json format"
|
||||
When call validate_input_python "prettier-lint" "report-format" "json"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts sarif format"
|
||||
When call validate_input_python "prettier-lint" "report-format" "sarif"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty report-format (uses default)"
|
||||
When call validate_input_python "prettier-lint" "report-format" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid format"
|
||||
When call validate_input_python "prettier-lint" "report-format" "invalid"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects format with command injection"
|
||||
When call validate_input_python "prettier-lint" "report-format" "json; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating max-retries input"
|
||||
It "accepts default value 3"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "3"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts retry count of 1"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "1"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts retry count of 10"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "10"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty max-retries (uses default)"
|
||||
When call validate_input_python "prettier-lint" "max-retries" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects zero retries"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "0"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects negative retry count"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "-1"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects retry count above 10"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "11"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects non-numeric retry count"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "abc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects retry count with command injection"
|
||||
When call validate_input_python "prettier-lint" "max-retries" "3; echo"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating plugins input"
|
||||
It "accepts empty plugins (optional)"
|
||||
When call validate_input_python "prettier-lint" "plugins" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts single plugin"
|
||||
When call validate_input_python "prettier-lint" "plugins" "prettier-plugin-tailwindcss"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts multiple plugins"
|
||||
When call validate_input_python "prettier-lint" "plugins" "prettier-plugin-tailwindcss,prettier-plugin-organize-imports"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts scoped plugin"
|
||||
When call validate_input_python "prettier-lint" "plugins" "@trivago/prettier-plugin-sort-imports"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects plugins with command injection"
|
||||
When call validate_input_python "prettier-lint" "plugins" "prettier-plugin-tailwindcss; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating token input"
|
||||
It "accepts valid GitHub token (classic)"
|
||||
When call validate_input_python "prettier-lint" "token" "ghp_123456789012345678901234567890123456"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts valid GitHub fine-grained token"
|
||||
When call validate_input_python "prettier-lint" "token" "github_pat_1234567890123456789012345678901234567890123456789012345678901234567890a"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty token (optional)"
|
||||
When call validate_input_python "prettier-lint" "token" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid token format"
|
||||
When call validate_input_python "prettier-lint" "token" "invalid-token"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects token with command injection"
|
||||
When call validate_input_python "prettier-lint" "token" "ghp_123456789012345678901234567890123456; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating username input"
|
||||
It "accepts valid username"
|
||||
When call validate_input_python "prettier-lint" "username" "github-actions"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts username with hyphens"
|
||||
When call validate_input_python "prettier-lint" "username" "my-bot-user"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts alphanumeric username"
|
||||
When call validate_input_python "prettier-lint" "username" "user123"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty username (uses default)"
|
||||
When call validate_input_python "prettier-lint" "username" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects username with command injection"
|
||||
When call validate_input_python "prettier-lint" "username" "user; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects username with special characters"
|
||||
When call validate_input_python "prettier-lint" "username" "user@bot"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when validating email input"
|
||||
It "accepts valid email"
|
||||
When call validate_input_python "prettier-lint" "email" "github-actions@github.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with plus sign"
|
||||
When call validate_input_python "prettier-lint" "email" "user+bot@example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts email with subdomain"
|
||||
When call validate_input_python "prettier-lint" "email" "bot@ci.example.com"
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "accepts empty email (uses default)"
|
||||
When call validate_input_python "prettier-lint" "email" ""
|
||||
The status should be success
|
||||
End
|
||||
|
||||
It "rejects invalid email format"
|
||||
When call validate_input_python "prettier-lint" "email" "not-an-email"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "rejects email with command injection"
|
||||
When call validate_input_python "prettier-lint" "email" "user@example.com; 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 "Prettier Lint"
|
||||
End
|
||||
|
||||
It "defines expected inputs"
|
||||
When call get_action_inputs "$ACTION_FILE"
|
||||
The output should include "mode"
|
||||
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 "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 "status"
|
||||
The output should include "files-checked"
|
||||
The output should include "unformatted-files"
|
||||
The output should include "sarif-file"
|
||||
The output should include "files-changed"
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing input requirements"
|
||||
It "has all inputs as optional (with defaults)"
|
||||
When call is_input_required "$ACTION_FILE" "mode"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
|
||||
Context "when testing security validations"
|
||||
It "validates against path traversal in working-directory"
|
||||
When call validate_input_python "prettier-lint" "working-directory" "../../../etc"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in mode"
|
||||
When call validate_input_python "prettier-lint" "mode" "check|echo"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command substitution in config-file"
|
||||
When call validate_input_python "prettier-lint" "config-file" "\$(whoami)"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against path traversal in token"
|
||||
When call validate_input_python "prettier-lint" "token" "../../../etc/passwd"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against shell metacharacters in username"
|
||||
When call validate_input_python "prettier-lint" "username" "user&whoami"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in email"
|
||||
When call validate_input_python "prettier-lint" "email" "user@example.com\`whoami\`"
|
||||
The status should be failure
|
||||
End
|
||||
|
||||
It "validates against command injection in plugins"
|
||||
When call validate_input_python "prettier-lint" "plugins" "plugin1,plugin2; rm -rf /"
|
||||
The status should be failure
|
||||
End
|
||||
End
|
||||
End
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,233 +0,0 @@
|
||||
#!/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