[WOOTAX-292] Tweak - Modernize PHPUnit test-environment installation#2957
[WOOTAX-292] Tweak - Modernize PHPUnit test-environment installation#2957bartech wants to merge 11 commits into
Conversation
There was a problem hiding this comment.
🤖 Automated AI pre-flight review — generated by Claude Code (
ultra-reviewskill) on behalf of @Abdalsalaam.
This is NOT an approval and not a substitute for human review; it is an early automated pass to surface likely issues. Treat every finding as a suggestion to verify.
Findings
Blockers (must fix before merge)
None that meet the strict bar (no production/CI/data-loss/security-in-prod impact). Note: two reviewers rated the exit-code-masking issue below as a BLOCKER; it is downgraded here only because CI — the actual merge gate — bypasses this script. It remains the #1 fix.
Majors (should fix before merge)
M1 — composer test reports success even when PHPUnit fails (exit-code masking)
tests/bin/run-tests.sh (final pipeline):
"$PROJECT_ROOT/vendor/bin/phpunit" --testdox "$@" 2>&1 \
| grep -Ev "^(PHP Deprecated|PHP Notice|PHP Warning|Xdebug)" \
|| trueset -e is on but set -o pipefail is not, so the pipeline's status is grep's. grep -Ev exits 0 whenever it prints ≥1 line (always true for real PHPUnit output), and || true then discards even that. A failing suite — test failures, fatals, even a segfault — makes composer test exit 0. This directly defeats the PR's purpose: a developer (or any wrapper/pre-commit hook) is told "green" on red.
Fix: set -o pipefail after set -e, drop || true, and propagate the real status, e.g.:
set -o pipefail
"$PROJECT_ROOT/vendor/bin/phpunit" --testdox "$@" 2>&1 \
| grep -Ev "^(PHP Deprecated|PHP Notice|PHP Warning|Xdebug)" || true
exit "${PIPESTATUS[0]}"(Confirmed by Correctness, Silent-failure, and Conventions reviewers.)
M2 — Local suite is non-reproducible: WC_VERSION defaults to latest
tests/bin/install-wc-tests.sh:
WC_VERSION=${WC_VERSION:-latest}The old installer pinned ${6-"10.3.4"}; CI tests a pinned matrix of the 3 latest WC minors. Defaulting local to latest means a fresh composer test can clone a different WC than CI, so local green/red can diverge from CI green/red (and may pull a WC requiring a newer PHP than the contributor has). Fix: pin a known-good WC_VERSION (ideally one CI also exercises), or document/require it explicitly.
M3 — mysql/mysqladmin shim planted in a predictable shared-/tmp path on PATH
tests/bin/install-wc-tests.sh:
SHIM_DIR="${TEST_TMP_DIR}/wcservices-mysql-shim"
mkdir -p "$SHIM_DIR"
...
export PATH="$SHIM_DIR:$PATH"SHIM_DIR is a fixed name under shared /tmp, created with default umask and prepended to PATH. On a multi-user host, another user can pre-create/own /tmp/wcservices-mysql-shim and plant a malicious mysql/mysqladmin that then executes in the developer's shell. Proportionate (dev-only, common single-user case is fine), but a genuine local code-execution vector. Fix: use mktemp -d (or verify the dir is owned by the current uid and chmod 700 it; bail if pre-owned by another user).
Minors (nice to fix)
- No re-verification after a granular repair.
run-tests.shcallscheck-test-env.sh, whichexecs the installer; on return PHPUnit runs with no second probe. A partial repair leaves a broken env that M1 then hides. Fix: re-run the health check after repair and fail non-zero if still unhealthy. (Mostly mitigated once M1 is fixed.) - Over-broad output filter. The
grep -Ev "^(PHP Deprecated|PHP Notice|PHP Warning|...)"drops every such line, including aPHP Warningthat precedes a fatal. Never filterPHP Fatal/PHP Parse error. Tie this to the M1 fix. - Cleartext WP version-check.
download http://api.wordpress.org/core/version-check/1.7/ ...— the parsed version drives a download URL; usehttps://. - MariaDB version drift. Local
mariadb:10.11vs CImariadb:10.9vs rootdocker-compose.ymlmariadb:10.5.8. Align local to10.9to match CI. - DB ports bind
0.0.0.0.tests/docker-compose.yml:- 4416:3306and- "8062:8080"expose the test DB and an unauthenticated Adminer to the LAN. Bind to loopback:127.0.0.1:4416:3306,127.0.0.1:8062:8080(and consider droppingadminerfrom the default stack). - DB password on argv.
--password="$TEST_DB_PASS"indrop_test_db/ensure_test_db/wait_for_dbis visible viaps//procand triggers mysql's own cleartext warning. PreferMYSQL_PWDor a defaults-extra-file. (Throwaway cred, low impact.) - No download integrity checks. WP zip / WC clone are fetched over https but never checksum/SHA-verified. Dev-only; acceptable, but worth a note.
- Port-parse fragility.
grep -oE '[0-9]+:3306' | head -1relies ontest_dbappearing first in the compose file. Scope the grep totest_db'sports:block. - Orphan volume.
tests/docker-compose.ymldeclaresvolumes: dockerdirectory:but no service mounts it; MariaDB data isn't persisted to a named volume. Remove it, or mount it at/var/lib/mysqlif persistence was intended. docker compose(v2) assumed unconditionally. No presence/version check; contributors on v1docker-composeor without Docker get cryptic failures. Detect/alias, or fail early with a clear message.- Errors swallowed by
2>/dev/null || true.down -v --remove-orphansanddrop_test_dbhide daemon-down / auth failures; the real cause only surfaces (less clearly) at the next step. Keep|| truefor the "already absent" case; let connection/daemon errors propagate. AGENTS.mdgaps. Newcomposer test:checkis undocumented; no escape hatch documented for contributors without Docker (they can still runphpunit -c phpunit.xml.distafter manual setup, as CI does).
Nits
- macOS
sed -i .bakleaves*.baklitter in$WP_TESTS_DIR(install_wp_test_suite,repair_wp_config). Addrm -f "${cfg}.bak"after the Darwin branch. version: '3'intests/docker-compose.ymlis obsolete under Compose v2 (deprecation warning). Drop the key. (Pre-existing in the root compose file too.)- Clone-fallback error reporting is opaque — the ERR trap reports a line number, not a cause. Splitting
run_quiet ... || run_quiet ...intoif ! ...; then ...; fireads clearer (behavior is already correct). /dev/tcpbashism in the DB probe — fine under the#!/usr/bin/env bashshebang; would break only if run viash.bootstrap.phptemp-dir edge:sys_get_temp_dir()honorsTMP/TEMPtoo, whiletest-config.shuses${TMPDIR:-/tmp}— they can diverge only ifTMP/TEMPis set whileTMPDIRis unset (rare on Unix; CI and macOS both resolve consistently).
Addresses the WOOTAX-292 review (PR #2957): - run-tests.sh: propagate PHPUnit's real exit code via pipefail + PIPESTATUS so `composer test` fails on red instead of masking it; add a TESTDOX=1/0/auto output toggle (compact when captured, --testdox at an interactive terminal). - install-wc-tests.sh: create the mysql/mysqladmin shim with `mktemp -d` to avoid a shared-/tmp PATH hijack; fetch the WP version-check over https; remove macOS sed .bak files with if/fi (a `[[ ]] && rm` tail returned non-zero on Linux and aborted the repair dispatch). - docker-compose.yml: bind the DB and Adminer to 127.0.0.1, align MariaDB to CI's 10.9, drop the obsolete `version:` key and the unused named volume. - AGENTS.md: document the WC_VERSION/EXPECTED_WP_VERSION override and TESTDOX. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-phpunit-test-env-installation-port-shipstation # Conflicts: # AGENTS.md
Replace the GNU-only `\s` regex shorthand with the POSIX `[[:space:]]` class in the wp-tests-config.php DB_HOST health probe and sed repair. Stock macOS grep/sed (BSD) treat `\s` as a literal 's', so the probe never matched a healthy config — reporting drift every run and escalating the cheap sed repair to a full WP reinstall. POSIX classes behave identically on GNU (Linux/CI) and BSD.
Capture the BSD/macOS vs GNU regex and `sed -i` pitfalls learned from the DB_HOST probe fix so future shell edits stay cross-platform.
PHPUnit exits 0 even when it runs no tests ("No tests executed!"), so a
broken bootstrap or misconfigured suite would make `composer test` report
green — the same gap #2638 hit in CI. tee the filtered output to a temp log
and fail the run when it reports an empty suite; PHPUnit's real exit code
still wins via PIPESTATUS otherwise. mktemp uses an explicit template for
BSD/macOS portability.
CezaryDrewniak
left a comment
There was a problem hiding this comment.
LGTM and works as advertised
Reviewed locally, happy path is green (OK (180 tests, 394 assertions)), the granular repair correctly handled a stale DB container + wp-tests-config.php DB_HOST drift with no full reinstall, and the e2f1628/e089c0b exit-code fixes work: --filter=NoSuchTestXyz now correctly surfaces exit=1 with the "PHPUnit executed zero tests" guard.
Two small follow-ups before merge:
tests/bin/install-wc-tests.sh:390—{ resolve_wc_version; install_wc; }runs underset -e+ERRtrap, so whenresolve_wc_versionreturns 1 (GitHub API rate-limit / offline) the script aborts on the API failure andinstall_wc's built-ingit clone … || git clone …default-branch fallback never gets a chance to run — defeating the self-heal in the exact scenario it was written for. Trivial fix:[[ "$REPAIR_WC" == "1" ]] && { resolve_wc_version || true; install_wc; }
tests/docker-compose.yml:20—image: admineris the only unpinned image; please pin to e.g.adminer:4.8.1to match the discipline now applied tomariadb:10.9and the SHA-pinned third-party actions.
resolve_wc_version ran under set -e inside the REPAIR_WC dispatch group, so a GitHub API rate-limit or offline failure killed the script before install_wc's default-branch clone fallback could run — defeating the self-heal in the exact scenario it was written for. Make the resolution best-effort so the fallback clone (not subject to the API rate limit) takes over.
adminer was the only unpinned image; pin it to 4.8.1 to match the reproducibility discipline of the mariadb:10.9 pin.
Description
composer test(the PHPUnit integration suite) could not run in a fresh shell. Two root causes:/tmpdrift.tests/php/bootstrap.phpresolved the WC root from a hard-coded/tmp/woocommerce/...(ignoringTMPDIR), and a stalewp-tests-config.phpleft behind by a sibling shipping plugin baked in the wrong DB port — so the suite pointed at a database that wasn't running.tests/docker-compose.yml/tests/test.env, and the oldtests/bin/install-wc-tests.shassumed a pre-existingDB_HOSTand shelled out to a hostmysqladmin.This ports the proven, resilient test-env pattern from the shipping plugins (
woocommerce-shipping-usps#550,woocommerce-shipstation#370/#380) into this repo, adapted for thecomposer-driven (non-pnpm) workflow.What changed:
tests/docker-compose.yml(MariaDB on127.0.0.1:4416, adminer on8062) +tests/test.env. An explicit Compose project name (wcservices_test) isolates this plugin so adown --remove-orphansno longer tears down a sibling plugin's DB container.tests/bin/test-config.sh— single source of truth for DB params (readstest.env+ the published compose port).tests/bin/check-test-env.sh— cheap preflight probes that dispatch a granular repair: only the broken component is rebuilt. The cross-plugin port drift is fixed with a singlesed— no downloads.tests/bin/install-wc-tests.sh(rewritten) — namespaced/TMPDIR-aware paths, docker DB lifecycle, on-diskmysql/mysqladminshims that proxy through the container (so no local mysql client is required), idempotentCREATE DATABASE IF NOT EXISTS+skip-database-creation=true, per-componentREPAIR_*flags, and--force. Usesphp bin/generate-feature-config.php(not pnpm).tests/bin/run-tests.sh— health-check + repair, then PHPUnit.tests/php/bootstrap.php— the WC-root fallback now resolves under the system temp dir (honorsTMPDIR) so plaincomposer testfinds the installer's path.composer.json—composer testnow runs the preflight + PHPUnit; addedcomposer test:setupandcomposer test:check.AGENTS.md— documents the one-command flow.No production code (
classes/,src/) is touched;composer.lockis unchanged; no Node/toolchain pin bump.Related issue(s)
Closes WOOTAX-292.
Steps to reproduce & screenshots/GIFs
On a machine with Docker but no host mysql client, with no
/tmp/wordpress*:The command brings the DB container up, installs WP + WC test deps, bootstraps the schema, and runs the suite:
Verified scenarios:
wp-tests-config.php→ preflight repairs onlyWP_CONFIG(singlesed, no downloads), suite green.bash tests/bin/install-wc-tests.sh --force→ clean full reinstall, green.Checklist
changelog.txtentry addedreadme.txtentry added