diff --git a/tests/run.sh b/tests/run.sh index 44d1235..2b48da8 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -1,6 +1,7 @@ #!/bin/bash set -euo pipefail +# Root of the project directory ROOT="$(dirname "$(realpath "$0")")" echo "===================================" @@ -8,37 +9,37 @@ echo " WordPress Recipe Test Suite" echo "===================================" echo "" -# Collect all compose files +# Collect all compose files for YAML validation COMPOSE_FILES=() while IFS= read -r f; do COMPOSE_FILES+=("$f") done < <(find "$ROOT/.." -maxdepth 1 -name 'compose*.yml' | sort) -# Collect all tmpl files with bash content +# Collect all tmpl files for shellcheck (only those with bash content) SHELL_TMPL_FILES=() while IFS= read -r f; do SHELL_TMPL_FILES+=("$f") done < <(find "$ROOT/.." -maxdepth 1 -name '*.tmpl' | sort) -TMPL_FILES=() -while IFS= read -r f; do - TMPL_FILES+=("$f") -done < <(find "$ROOT/.." -maxdepth 1 -name '*.tmpl' | sort) - +# Track total failures across all test suites failures=0 +# Validate YAML syntax of all compose files echo "========================================================================" "$ROOT/test_yaml.sh" "${COMPOSE_FILES[@]}" || failures=$((failures + 1)) echo "" +# Lint shell scripts inside Go templates (after stripping template tags) echo "========================================================================" "$ROOT/test_shell.sh" "${SHELL_TMPL_FILES[@]}" || failures=$((failures + 1)) echo "" +# Render templates with fixture env files and verify expected output echo "========================================================================" "$ROOT/test_templates.sh" || failures=$((failures + 1)) echo "" +# Validate compose files against the Docker Compose specification echo "========================================================================" "$ROOT/test_compose_config.sh" || failures=$((failures + 1)) echo "" diff --git a/tests/test_compose_config.sh b/tests/test_compose_config.sh index 6f9f4c1..3edb796 100755 --- a/tests/test_compose_config.sh +++ b/tests/test_compose_config.sh @@ -5,6 +5,7 @@ ROOT="$(dirname "$(realpath "$0")")/.." pass=0 fail=0 +# Detect available Docker Compose command compose_cmd="" if command -v docker &>/dev/null && docker compose version &>/dev/null 2>&1; then compose_cmd="docker compose" @@ -12,14 +13,17 @@ elif command -v docker-compose &>/dev/null; then compose_cmd="docker-compose" fi +# Validate a compose file against the Docker Compose specification test_compose_config() { local file=$1 + # Skip if Docker Compose is not available on this system if [ -z "$compose_cmd" ]; then echo " SKIP $file (no docker compose available)" return fi + # config -q exits with non-zero if the compose file is invalid if $compose_cmd -f "$file" config -q 2>/dev/null; then echo " PASS $file" pass=$((pass + 1)) @@ -31,13 +35,16 @@ test_compose_config() { echo "=== Docker Compose Config Validation ===" +# Validate main compose file first, then all override files test_compose_config "$ROOT/compose.yml" while IFS= read -r f; do + # Skip the main compose file (already tested above) [ "$f" = "$ROOT/compose.yml" ] && continue [ -f "$f" ] && test_compose_config "$f" done < <(find "$ROOT" -maxdepth 1 -name 'compose*.yml' | sort) echo "---" echo "Passed: $pass Failed: $fail" +# Exit with failure if any compose file failed validation [ "$fail" -eq 0 ] diff --git a/tests/test_shell.sh b/tests/test_shell.sh index 65a7844..eb2511e 100755 --- a/tests/test_shell.sh +++ b/tests/test_shell.sh @@ -4,8 +4,10 @@ set -euo pipefail pass=0 fail=0 +# Allow overriding shellcheck options via env var (e.g. -s bash) EXTRA_SHELLCHECK_OPTS="${SHELLCHECK_OPTS:-}" +# Run shellcheck on a Go template after stripping template tags shellcheck_tmpl() { local tmpl=$1 # Strip Go template tags ({{ ... }} and {{- ... -}}) into whitespace @@ -28,10 +30,12 @@ shellcheck_tmpl() { } echo "=== ShellCheck ===" +# Lint each template file passed as argument for f in "$@"; do [ -f "$f" ] && shellcheck_tmpl "$f" done echo "---" echo "Passed: $pass Failed: $fail" +# Exit with failure if any template failed shellcheck [ "$fail" -eq 0 ] diff --git a/tests/test_templates.sh b/tests/test_templates.sh index ca9ed61..12e6a78 100755 --- a/tests/test_templates.sh +++ b/tests/test_templates.sh @@ -5,8 +5,10 @@ ROOT="$(dirname "$(realpath "$0")")/.." pass=0 fail=0 +# Allow overriding gomplate binary path via env var gomplate="${GOMPLATE_BIN:-gomplate}" +# Ensure gomplate is installed before running template tests require_gomplate() { if ! command -v "$gomplate" &>/dev/null; then echo "gomplate not found. Install it from https://github.com/hairyhenderson/gomplate" @@ -24,14 +26,19 @@ render() { -f "$tmpl" 2>/dev/null } -# Render by exporting env vars directly (avoids gomplate datasource quirks) +# Render a template by exporting env vars directly +# This avoids gomplate datasource quirks with .env files render_via_env() { local tmpl=$1 envfile=$2 # shellcheck disable=2046 env $(xargs < "$envfile") "$gomplate" -f "$tmpl" 2>/dev/null } -# --- entrypoint.sh.tmpl tests --- +# --------------------------------------------------------------------------- +# entrypoint.sh.tmpl tests +# --------------------------------------------------------------------------- + +# Default entrypoint: no multisite, uploads guard, chown with --from, wp-cli test_entrypoint_default() { local envfile=$1 local output @@ -60,6 +67,7 @@ test_entrypoint_default() { echo " PASS entrypoint default" } +# Multisite enable: should set WP_ALLOW_MULTISITE test_entrypoint_multisite_enable() { local envfile=$1 local output @@ -72,6 +80,7 @@ test_entrypoint_multisite_enable() { echo " PASS entrypoint multisite enable" } +# Multisite subdomain: should set MULTISITE, SUBDOMAIN_INSTALL, DOMAIN_CURRENT_SITE test_entrypoint_multisite_subdomain() { local envfile=$1 local output @@ -92,6 +101,7 @@ test_entrypoint_multisite_subdomain() { echo " PASS entrypoint multisite subdomain" } +# Multisite subfolder: should set MULTISITE but not SUBDOMAIN_INSTALL test_entrypoint_multisite_subfolder() { local envfile=$1 local output @@ -108,6 +118,7 @@ test_entrypoint_multisite_subfolder() { echo " PASS entrypoint multisite subfolder" } +# CORS: should enable Apache headers module and set Access-Control-Allow-Origin test_entrypoint_cors() { local envfile=$1 local output @@ -124,6 +135,7 @@ test_entrypoint_cors() { echo " PASS entrypoint CORS" } +# PHP extensions: should install additional PHP extensions like calendar test_entrypoint_php_extensions() { local envfile=$1 local output @@ -136,6 +148,7 @@ test_entrypoint_php_extensions() { echo " PASS entrypoint PHP extensions" } +# Composer: should download Composer via getcomposer.org test_entrypoint_composer() { local envfile=$1 local output @@ -148,7 +161,11 @@ test_entrypoint_composer() { echo " PASS entrypoint composer" } -# --- htaccess.tmpl tests --- +# --------------------------------------------------------------------------- +# htaccess.tmpl tests +# --------------------------------------------------------------------------- + +# Default htaccess: standard WordPress rewrite rule, no multisite section test_htaccess_default() { local envfile=$1 local output @@ -165,6 +182,7 @@ test_htaccess_default() { echo " PASS htaccess default" } +# Multisite htaccess: multisite section present, no standard rewrite rule test_htaccess_multisite() { local envfile=$1 mode=$2 local output @@ -181,7 +199,11 @@ test_htaccess_multisite() { echo " PASS htaccess multisite $mode" } -# --- uploads.ini.tmpl tests --- +# --------------------------------------------------------------------------- +# uploads.ini.tmpl tests +# --------------------------------------------------------------------------- + +# Default uploads config: 256M upload limit, 30s execution time test_uploads_default() { local output output=$(render_via_env "uploads.ini.tmpl" "tests/fixtures/default.env") @@ -197,6 +219,7 @@ test_uploads_default() { echo " PASS uploads default" } +# Custom uploads config: 512M upload limit, 60s execution time test_uploads_custom() { local envfile=$1 local output @@ -213,7 +236,11 @@ test_uploads_custom() { echo " PASS uploads custom" } -# --- msmtp.conf.tmpl tests --- +# --------------------------------------------------------------------------- +# msmtp.conf.tmpl tests +# --------------------------------------------------------------------------- + +# Full SMTP config: host, from, auth, passwordeval, TLS, set_from_header test_msmtp_default() { local output output=$(render_via_env "msmtp.conf.tmpl" "tests/fixtures/smtp-full.env") @@ -245,7 +272,9 @@ test_msmtp_default() { echo " PASS msmtp full config" } -# --- Run all template tests --- +# --------------------------------------------------------------------------- +# Run all template tests +# --------------------------------------------------------------------------- echo "=== Template Rendering Tests ===" cd "$ROOT" @@ -275,4 +304,5 @@ test_msmtp_default && pass=$((pass+1)) || fail=$((fail+1)) echo "---" echo "Passed: $pass Failed: $fail" +# Exit with failure if any template test failed [ "$fail" -eq 0 ] diff --git a/tests/test_yaml.sh b/tests/test_yaml.sh index a722b84..17a3936 100755 --- a/tests/test_yaml.sh +++ b/tests/test_yaml.sh @@ -4,6 +4,7 @@ set -euo pipefail pass=0 fail=0 +# Detect available YAML checker: prefer yamllint, fall back to PyYAML checker="" if command -v yamllint &>/dev/null; then checker=yamllint @@ -11,9 +12,11 @@ elif python3 -c "import yaml" 2>/dev/null; then checker=python fi +# Validate a single YAML file using the available checker test_yaml() { local file=$1 + # yamllint provides richer output; PyYAML just checks parseability case "$checker" in yamllint) if yamllint -d "{extends: relaxed, rules: {line-length: disable}}" "$file"; then @@ -37,6 +40,7 @@ with open('$file') as f: fail=$((fail+1)) fi ;; + # Skip silently if no YAML checker is installed *) echo " SKIP $file (no yamllint or PyYAML)" pass=$((pass+1)) @@ -45,10 +49,12 @@ with open('$file') as f: } echo "=== YAML Validation ===" +# Test each compose file passed as argument for f in "$@"; do [ -f "$f" ] && test_yaml "$f" done echo "---" echo "Passed: $pass Failed: $fail" +# Exit with failure if any file failed validation [ "$fail" -eq 0 ]