Skip to content

test(shell): add a Behat suite and CI workflow - #26

Open
mrrobot47 wants to merge 26 commits into
EasyEngine:developfrom
mrrobot47:test/behat-suite-r3
Open

mrrobot47 wants to merge 26 commits into
EasyEngine:developfrom
mrrobot47:test/behat-suite-r3

Conversation

@mrrobot47

@mrrobot47 mrrobot47 commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Adds a Behat suite for shell-command (features/: 96 scenarios, outline rows counted) and a Behat workflow that runs it on every pull request, on pushes to develop and on demand.

It covers interactive and --command shells for html, php and WordPress sites, global services, --user and argument validation. features/COVERAGE.md maps each command, flag and behaviour to its scenarios.

  • The workflow runs the suite inside a fresh core checkout: core develop is cloned, every easyengine/* package is set to dev-develop, and this package is installed from the pull request's checkout through a Composer path repository, so the code under test is the PR's, not Packagist's.
  • Lanes: main and notty (the same scenarios with STDIN on /dev/null and no controlling TTY, since ee shell is TTY-sensitive), on PHP 7.4 and 8.5. @destructive scenarios run there too, since the runner is thrown away.
  • Scenarios written for open bugs are left out for now: they assert the fixed behaviour, so each comes back with its fix. Every scenario in this PR gates, and COVERAGE.md lists the bugs that aren't covered yet.

To run it locally (as root, on a test machine, since the suite creates and deletes *.test sites): replace a core checkout's features/ with this package's (rm -rf features && cp -R <package>/features features, as the workflow does; copying over it would keep core's own cli.feature, whose scenario runs ee cli self-uninstall --yes) and run sudo -E vendor/bin/behat from the core root; add --tags='~@destructive' to skip the scenarios that change global services or config.

Green run on my fork: https://github.com/mrrobot47/shell-command/actions/runs/39292430974

Verbatim copy of site-command's features/bootstrap (upstream/develop 9dc44e1) as the base for the shell-command suite.
Cleanup now deletes only bh-shell-command-* sites instead of a fixed list, the I run step is anchored so it no longer swallows every step with a double-quoted argument, and stopped containers are restarted after each scenario. Adds generic steps for idempotent site setup, exact return codes, STDOUT/STDERR assertions, working directory, files and container state.
Core's init-ee.php already defines EE_ROOT_DIR, so the context's unconditional define warned (an error in PHP 9). Adds steps to assert a site is absent and that two consecutive runs produced the same output.
Default service mapping to nginx, unsupported services, site auto-detection from the site directory, trailing slash and scheme handling, stopped containers and disabled sites.
Output and exit status handling, STDIN forwarding, quoting, --skip-tty, --user, per-service shells (php, nginx, postfix, local db, mailhog) and services from docker-compose-custom.yml.
Piped sessions, exit status propagation, --user and --service, and TTY behaviour from a terminal via script.
global-redis, global-nginx-proxy and global-db (my.cnf creation and reuse), site argument ignored, --user fallback and stopped global containers.
The my.cnf and custom compose scenarios cleaned up in their last steps, so a failing step left the live global-db my.cnf (or the site's custom compose file and container) behind. Snapshot files and register created containers in steps, and undo both in @AfterScenario, failing the hook when a restore fails.

Also: start the mailhog container directly instead of `ee mailhog enable` (which creates the global admin-tools auth), create a php site with --with-db --cache so global-db/global-redis exist on a fresh stack, use the `mariadb` client (MariaDB 11+ images drop `mysql`), register a container before stopping it, make listSites() and the feature cleanup fail instead of treating a broken `ee site list` as "no sites", fix the inverted container-count step, and rename the deleting "the file … does not exist" step to "… is removed".
Mark @known-bug scenarios as expected to fail until fixed, prove SHELL-4's host-side command substitution by comparing `$(cat /etc/hostname)` with the container's hostname, add `--command=0` to SHELL-5, and detach `script` from Behat's stdin so the TTY scenarios don't depend on how Behat was started.
Reference the issues.md IDs (SHELL-1..9, CORE-2), correct the SHELL-8 exposure note, describe the new restore mechanism, and mark --skip-tty and the idempotency check as only weakly observable.
Untagged scenarios asserted exit 1 for `exit 2/3/4/255` and the interactive fallback of a valueless --command, so fixing SHELL-2 or SHELL-5 would have turned them red; they now assert only what holds either way, and the exact statuses live in the @known-bug outline. Also check the local db shell by the server datadir (the php image ships a mariadb client too), add return-code checks to the TTY scenario, give the SHELL-7 help check a positive assertion and a fixed COLUMNS, pin the php site to 8.4, and drop the redis ping right after restarting global-redis (it can still be loading).
Run PHP 7.4 and 8.5, strip control characters from the junit files before building failures.txt, and cap the Behat step at 75 minutes so a hung run still uploads its logs. The notty lane keeps running Behat without a controlling terminal. Diagnostics: the host addresses and routes, and the docker-compose version after the first run (MIG-20).
EE's first run downloads docker-compose 2.27.0 without --fail (MIG-10); on a GitHub error it moves the HTML error page over the binary, as on an admin-tools-command runner. Fetch 2.27.0 again with --fail and retries in that case.
The 18 scenario rows written for open bugs assert the fixed behaviour and fail today, so they stay out of the suite until their fixes land, and COVERAGE.md lists those bugs as not covered yet. Every scenario that ships now gates, and the non-gating known-bug CI lane goes with them.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Cleanup removes unrelated local builds, a gating assertion preserves a known bug, and the coverage summary overstates coverage.

3 open issues
What changed in this PR

Adds Behat integration coverage for ee shell, testing the package checkout inside EasyEngine core.

Changes:

  • Adds 96 scenarios covering site shells, global services, validation, and terminal behavior.
  • Introduces shared test helpers and coverage documentation.
  • Runs CI across PHP 7.4/8.5 and main/notty lanes.
File Description
features/​shell-wp.feature Tests WordPress shells and WP-CLI.
features/​shell-validation.feature Tests argument validation and help.
features/​shell-php-interactive.feature Tests interactive input, output, and terminal behavior.
features/​shell-php-command.feature Tests command execution and service selection.
features/​shell-html.feature Tests HTML shells and site detection.
features/​shell-global-services.feature Tests global shells and database configuration.
features/​COVERAGE.md Maps coverage and deferred cases.
features/​bootstrap/​support.php Adds assertion utilities.
features/​bootstrap/​FeatureContext.php Adds setup, assertions, and cleanup hooks.
.github/​workflows/​behat.yml Runs the suite and uploads diagnostics.

Review effort: Balanced

Comment thread features/COVERAGE.md Outdated
Comment thread features/bootstrap/FeatureContext.php Outdated
Comment thread features/shell-php-command.feature Outdated
The AfterFeature cleanup deleted ee.phar and ee-old.phar from the core checkout after every feature, although no scenario here builds them, so a local run removed a developer's own builds. The 'ee phar is generated' step now records that it built them, and the cleanup only removes them then.
…rage estimate

'An unknown binary fails with the shell's error' expected exit 1, which is SHELL-2 collapsing bash's 127; it now only requires a failure. The coverage estimate counted the items whose scenarios are held back as covered.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No unresolved blocking findings remain, and the linked Behat workflow completed successfully.

No open issues 🎉

3 resolved since last review

Review effort: Balanced

…iline output

compareContents() compared list elements by position, so [1,3,5] wasn't found in [1,2,3,4,5], and a missing key or property raised a notice. The indexed exact-output step and both something-like steps only used the first line of the expected text.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The CSV assertion helper can report success when an expected row is missing.

1 open issue

Review effort: Balanced

Comment thread features/bootstrap/support.php

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No unresolved blocking issues remain in the reviewed scenarios, test helpers, or CI workflow.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants