fix: revive the inert Feature test suite, drop the dead behat Mink block - #5054
Merged
Conversation
tuxpiper
force-pushed
the
chore/devops-cleanup
branch
from
July 13, 2026 17:13
3751a70 to
c642bfe
Compare
tuxpiper
force-pushed
the
chore/devops-fixes
branch
from
July 13, 2026 17:13
1a757b5 to
706b1fd
Compare
Three defects found while auditing the devops config. All verified against a
throwaway mariadb:10.11 stack mirroring the CI job.
1. The tests/Feature phpunit suite matched zero tests. phpunit.xml.dist selects
./tests/Feature with suffix="Test.php", but the two files there were named
ExportJobAPI.php and WebhooksPostsUpdateAPI.php, so nothing ever ran. They had
also rotted since the 2022 Lumen -> Laravel migration and could not simply be
renamed: ExportJobAPI imported Laravel\Lumen\Testing\DatabaseTransactions (Lumen
is no longer installed), WebhooksPostsUpdateAPI declared setUp()/tearDown()
without the ': void' return types PHPUnit 9 requires, and both used the Lumen
assertion API (seeStatusCode/seeJson/seeJsonStructure/expectsJobs), which does
not exist on Laravel's TestCase.
Ported both to Laravel 8 + PHPUnit 9 and renamed them so the suite collects them:
- seeStatusCode/seeJson/seeJsonStructure -> assertStatus/assertJson/
assertJsonStructure on the returned TestResponse
- expectsJobs(ExportPostsJob::class) -> Bus::fake() + Bus::assertDispatched()
- ExportJobAPI referenced \Ushahidi\Modules\Auth\GenericUser, which does not
exist; the real class is \Ushahidi\Authzn\GenericUser
ExportJobAPITest now creates its own admin user in setUp() instead of assuming
user id 2. Ushahidi\Authzn\Session::getUser() loads the acting user from the
database, and id 2 is only an admin in the behat fixture (tests/datasets), which
phpunit never loads -- and which rewrites the users table when behat does load it.
Owning the user keeps the suite independent of run order.
phpunit now reports 176 tests / 552 assertions, up from 172 / 534.
2. behat.yml.dist declared a Behat\MinkExtension block, but it was indented as a
child of suites.default rather than at profile level, so behat silently ignored
it. That was load-bearing: mink is not in composer.lock at all, so hoisting the
block to where it "belongs" would break all 738 scenarios with an
extension-not-found error. Removed the block rather than fix its indentation.
The three contexts use no Mink. behat: 738 scenarios / 6339 steps still pass.
3. .editorconfig set indent_style = spaces. The spec only accepts 'tab' or 'space',
so editors ignored the value and indent style was unenforced. Now 'space'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…repo
The Vagrantfile only looked for Homestead.yaml or Homestead.json, but the repo
ships Homestead.yml -- and .gitignore ignores the two names it did look for. So
following the setup docs ("the Vagrant and Homestead.yml files that ship with
Ushahidi", then `vagrant up`) aborted with "Homestead settings file not found".
Broken since Homestead.yml was re-added in 2023.
Add Homestead.yml to the lookup chain, after Homestead.yaml/.json so a developer's
local override still wins. Preferred over renaming the tracked file, which would
land it under a gitignored name.
Also point the abort message at the project directory it actually searches, rather
than confDir (the vendor Homestead install).
Verified statically: Vagrantfile parses (ruby -c), Homestead.yml parses as YAML and
its settings resolve, and the lookup chain now selects Homestead.yml where it
previously aborted. NOT boot-tested: Homestead.yml pins provider: virtualbox, and
VirtualBox on Apple Silicon cannot run the x86_64 Homestead box, so `vagrant up`
needs an Intel Mac or a Linux host.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tuxpiper
force-pushed
the
chore/devops-cleanup
branch
from
July 15, 2026 14:17
c642bfe to
3ec822f
Compare
tuxpiper
force-pushed
the
chore/devops-fixes
branch
from
July 15, 2026 14:17
706b1fd to
40e1606
Compare
dukedanny
pushed a commit
that referenced
this pull request
Jul 17, 2026
…s, arcanist, garden) (#5053) * chore: remove dead devops tooling (travis, codeship, heroku, coveralls, arcanist, garden) GitHub Actions (.github/workflows/test-and-ship.yml) is the only CI system in use, alongside Docker + the Makefile for local dev. Everything removed here is config for tooling that is no longer wired to anything. Removed: - Travis CI: .travis.yml, .env.travis (travis-ci.org shut down in 2021) - Codeship: codeship-{services,steps}.yml, deployment.env.encrypted, and the Codeship-only docker/{build,test,release,awscli}.Dockerfile + their .run.sh scripts. CloudBees CodeShip is EOL and these still declared PHP 7.2/7.3 services. docker/release.sh is NOT part of this -- it is still used by the release-tar action. - Coveralls: .coveralls.yml + the php-coveralls dev dep (nothing has uploaded coverage since 2016). Drops the orphaned symfony/stopwatch transitive dep too. - Heroku one-click deploy: app.json, Procfile, apache_heroku.conf (Procfile invoked bin/heroku-php-apache2, which does not exist) - Phabricator Arcanist: .arcconfig, .arclint (paths referenced the pre-Laravel Kohana tree) - Garden.io: garden.yaml, garden_modules/ - librarian-puppet: .librarian/ (pointed at a puppet/modules/ tree that is gone) Orphan files with no referrer anywhere in the repo: skeleton.php (a 2014 code template), example.env (superseded by .env.example), package-lock.json (empty, and there is no package.json), mapping.json, the root pre-commit script (superseded by captainhook.json), phpspec.yml.coverage, apiary.apib (a stub; the real blueprints live in docs/api/), and tests/CaptainHook/PHPCS.php (orphaned when the pre-commit hook was dropped from captainhook.json in 2022). Also: - .gitignore / .dockerignore: drop rules for the removed tooling and the Kohana-era tree. Kept the *.aes / deployment.env / docker.env / vault.txt rules on purpose -- old working copies may still hold those secrets, and these rules are what stops them being committed or baked into an image. Added tmp/ (a Codeship-jet leftover that was untracked *and* unignored). - docs: drop the dead Travis/Coveralls/Heroku badges, and repoint the Gitter/IRC links (Gitter is sunset) at GitHub issues and ushahidi.com/support. The 2019 hackathon archive and the Code of Conduct venue list are left as-is. - resources/lang/README.md: it claimed translations are pulled from Transifex automatically. There is no such CI job, and PRs do edit the English source strings directly, so the stated policy was misleading. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: revive the inert Feature test suite, drop the dead behat Mink block (#5054) * fix: revive the inert Feature test suite, drop the dead behat Mink block Three defects found while auditing the devops config. All verified against a throwaway mariadb:10.11 stack mirroring the CI job. 1. The tests/Feature phpunit suite matched zero tests. phpunit.xml.dist selects ./tests/Feature with suffix="Test.php", but the two files there were named ExportJobAPI.php and WebhooksPostsUpdateAPI.php, so nothing ever ran. They had also rotted since the 2022 Lumen -> Laravel migration and could not simply be renamed: ExportJobAPI imported Laravel\Lumen\Testing\DatabaseTransactions (Lumen is no longer installed), WebhooksPostsUpdateAPI declared setUp()/tearDown() without the ': void' return types PHPUnit 9 requires, and both used the Lumen assertion API (seeStatusCode/seeJson/seeJsonStructure/expectsJobs), which does not exist on Laravel's TestCase. Ported both to Laravel 8 + PHPUnit 9 and renamed them so the suite collects them: - seeStatusCode/seeJson/seeJsonStructure -> assertStatus/assertJson/ assertJsonStructure on the returned TestResponse - expectsJobs(ExportPostsJob::class) -> Bus::fake() + Bus::assertDispatched() - ExportJobAPI referenced \Ushahidi\Modules\Auth\GenericUser, which does not exist; the real class is \Ushahidi\Authzn\GenericUser ExportJobAPITest now creates its own admin user in setUp() instead of assuming user id 2. Ushahidi\Authzn\Session::getUser() loads the acting user from the database, and id 2 is only an admin in the behat fixture (tests/datasets), which phpunit never loads -- and which rewrites the users table when behat does load it. Owning the user keeps the suite independent of run order. phpunit now reports 176 tests / 552 assertions, up from 172 / 534. 2. behat.yml.dist declared a Behat\MinkExtension block, but it was indented as a child of suites.default rather than at profile level, so behat silently ignored it. That was load-bearing: mink is not in composer.lock at all, so hoisting the block to where it "belongs" would break all 738 scenarios with an extension-not-found error. Removed the block rather than fix its indentation. The three contexts use no Mink. behat: 738 scenarios / 6339 steps still pass. 3. .editorconfig set indent_style = spaces. The spec only accepts 'tab' or 'space', so editors ignored the value and indent style was unenforced. Now 'space'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: make the Vagrantfile find the Homestead.yml that ships with the repo The Vagrantfile only looked for Homestead.yaml or Homestead.json, but the repo ships Homestead.yml -- and .gitignore ignores the two names it did look for. So following the setup docs ("the Vagrant and Homestead.yml files that ship with Ushahidi", then `vagrant up`) aborted with "Homestead settings file not found". Broken since Homestead.yml was re-added in 2023. Add Homestead.yml to the lookup chain, after Homestead.yaml/.json so a developer's local override still wins. Preferred over renaming the tracked file, which would land it under a gitignored name. Also point the abort message at the project directory it actually searches, rather than confDir (the vendor Homestead install). Verified statically: Vagrantfile parses (ruby -c), Homestead.yml parses as YAML and its settings resolve, and the lookup chain now selects Homestead.yml where it previously aborted. NOT boot-tested: Homestead.yml pins provider: virtualbox, and VirtualBox on Apple Silicon cannot run the x86_64 Homestead box, so `vagrant up` needs an Intel Mac or a Linux host. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dukedanny
pushed a commit
that referenced
this pull request
Jul 17, 2026
…cks CI) (#5055) * fix: vendor symm/gisconverter, whose upstream repo was deleted The project could not be installed from a clean checkout. symm/gisconverter's GitHub repository no longer exists, and the dist URL recorded in composer.lock points straight at it, so `composer install` died with: Failed to download symm/gisconverter from dist: The "https://api.github.com/ repos/symm/gisconverter/zipball/21ab697b..." file could not be downloaded (404) This broke every from-scratch build, develop included -- the last green CI run was 2026-04-21, and nothing has built from a cold cache since. It surfaced now only because that was the last time anyone pushed a branch that had to resolve dependencies without a warm vendor/ directory. The library is not droppable: it does the WKT geometry decoding behind PointRepository, BoundingBox, PostPoint, the V5 geometry query handlers and the Twitter data source. Packagist still serves the metadata but marks the package abandoned, and the only dist URL it offers is the dead one. No maintained fork exists to repoint at. So vendor it. packages/gisconverter/src is byte-identical to the v1.0.5 release (upstream ref 21ab697b7692b891dac37c64d10c9d83d4433f80), wired up with a composer path repository. symlink: false so composer copies it into vendor/ exactly as before, which keeps the docker build and the release tarball unchanged. Nothing that imports Symm\Gisconverter\... had to change. The upstream composer.json is trimmed to what we need and pinned with an explicit version, which a path repository requires. Upstream's tests, example.php and its long-dead Travis config are not copied. The BSD-3-Clause licence and copyright notice are retained as that licence requires; it is compatible with our AGPL-3.0. src/ruleset.xml excludes packages/* from phpcs, alongside vendor/*, so the vendored source stays byte-identical to upstream rather than being reformatted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: copy packages/ into the docker build before composer install The image build runs `composer install` (Dockerfile line ~23) before `COPY . .`, with only composer.json/composer.lock in the build context. symm/gisconverter is now a path-repository package, so composer needs its source at that layer and the build fails with: In PathDownloader.php line 51: Source path "packages/gisconverter" is not found for package symm/gisconverter Copy packages/ into the context before the install. This only affects the `ship` job (the Docker image build), which is push-only and skipped on PR checks -- which is why it was not caught by the test job or the pull_request run. Verified with a full `docker build .`: the image builds end to end and vendor/symm/gisconverter is present and autoloadable inside it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: remove dead devops tooling (travis, codeship, heroku, coveralls, arcanist, garden) (#5053) * chore: remove dead devops tooling (travis, codeship, heroku, coveralls, arcanist, garden) GitHub Actions (.github/workflows/test-and-ship.yml) is the only CI system in use, alongside Docker + the Makefile for local dev. Everything removed here is config for tooling that is no longer wired to anything. Removed: - Travis CI: .travis.yml, .env.travis (travis-ci.org shut down in 2021) - Codeship: codeship-{services,steps}.yml, deployment.env.encrypted, and the Codeship-only docker/{build,test,release,awscli}.Dockerfile + their .run.sh scripts. CloudBees CodeShip is EOL and these still declared PHP 7.2/7.3 services. docker/release.sh is NOT part of this -- it is still used by the release-tar action. - Coveralls: .coveralls.yml + the php-coveralls dev dep (nothing has uploaded coverage since 2016). Drops the orphaned symfony/stopwatch transitive dep too. - Heroku one-click deploy: app.json, Procfile, apache_heroku.conf (Procfile invoked bin/heroku-php-apache2, which does not exist) - Phabricator Arcanist: .arcconfig, .arclint (paths referenced the pre-Laravel Kohana tree) - Garden.io: garden.yaml, garden_modules/ - librarian-puppet: .librarian/ (pointed at a puppet/modules/ tree that is gone) Orphan files with no referrer anywhere in the repo: skeleton.php (a 2014 code template), example.env (superseded by .env.example), package-lock.json (empty, and there is no package.json), mapping.json, the root pre-commit script (superseded by captainhook.json), phpspec.yml.coverage, apiary.apib (a stub; the real blueprints live in docs/api/), and tests/CaptainHook/PHPCS.php (orphaned when the pre-commit hook was dropped from captainhook.json in 2022). Also: - .gitignore / .dockerignore: drop rules for the removed tooling and the Kohana-era tree. Kept the *.aes / deployment.env / docker.env / vault.txt rules on purpose -- old working copies may still hold those secrets, and these rules are what stops them being committed or baked into an image. Added tmp/ (a Codeship-jet leftover that was untracked *and* unignored). - docs: drop the dead Travis/Coveralls/Heroku badges, and repoint the Gitter/IRC links (Gitter is sunset) at GitHub issues and ushahidi.com/support. The 2019 hackathon archive and the Code of Conduct venue list are left as-is. - resources/lang/README.md: it claimed translations are pulled from Transifex automatically. There is no such CI job, and PRs do edit the English source strings directly, so the stated policy was misleading. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: revive the inert Feature test suite, drop the dead behat Mink block (#5054) * fix: revive the inert Feature test suite, drop the dead behat Mink block Three defects found while auditing the devops config. All verified against a throwaway mariadb:10.11 stack mirroring the CI job. 1. The tests/Feature phpunit suite matched zero tests. phpunit.xml.dist selects ./tests/Feature with suffix="Test.php", but the two files there were named ExportJobAPI.php and WebhooksPostsUpdateAPI.php, so nothing ever ran. They had also rotted since the 2022 Lumen -> Laravel migration and could not simply be renamed: ExportJobAPI imported Laravel\Lumen\Testing\DatabaseTransactions (Lumen is no longer installed), WebhooksPostsUpdateAPI declared setUp()/tearDown() without the ': void' return types PHPUnit 9 requires, and both used the Lumen assertion API (seeStatusCode/seeJson/seeJsonStructure/expectsJobs), which does not exist on Laravel's TestCase. Ported both to Laravel 8 + PHPUnit 9 and renamed them so the suite collects them: - seeStatusCode/seeJson/seeJsonStructure -> assertStatus/assertJson/ assertJsonStructure on the returned TestResponse - expectsJobs(ExportPostsJob::class) -> Bus::fake() + Bus::assertDispatched() - ExportJobAPI referenced \Ushahidi\Modules\Auth\GenericUser, which does not exist; the real class is \Ushahidi\Authzn\GenericUser ExportJobAPITest now creates its own admin user in setUp() instead of assuming user id 2. Ushahidi\Authzn\Session::getUser() loads the acting user from the database, and id 2 is only an admin in the behat fixture (tests/datasets), which phpunit never loads -- and which rewrites the users table when behat does load it. Owning the user keeps the suite independent of run order. phpunit now reports 176 tests / 552 assertions, up from 172 / 534. 2. behat.yml.dist declared a Behat\MinkExtension block, but it was indented as a child of suites.default rather than at profile level, so behat silently ignored it. That was load-bearing: mink is not in composer.lock at all, so hoisting the block to where it "belongs" would break all 738 scenarios with an extension-not-found error. Removed the block rather than fix its indentation. The three contexts use no Mink. behat: 738 scenarios / 6339 steps still pass. 3. .editorconfig set indent_style = spaces. The spec only accepts 'tab' or 'space', so editors ignored the value and indent style was unenforced. Now 'space'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: make the Vagrantfile find the Homestead.yml that ships with the repo The Vagrantfile only looked for Homestead.yaml or Homestead.json, but the repo ships Homestead.yml -- and .gitignore ignores the two names it did look for. So following the setup docs ("the Vagrant and Homestead.yml files that ship with Ushahidi", then `vagrant up`) aborted with "Homestead settings file not found". Broken since Homestead.yml was re-added in 2023. Add Homestead.yml to the lookup chain, after Homestead.yaml/.json so a developer's local override still wins. Preferred over renaming the tracked file, which would land it under a gitignored name. Also point the abort message at the project directory it actually searches, rather than confDir (the vendor Homestead install). Verified statically: Vagrantfile parses (ruby -c), Homestead.yml parses as YAML and its settings resolve, and the lookup chain now selects Homestead.yml where it previously aborted. NOT boot-tested: Homestead.yml pins provider: virtualbox, and VirtualBox on Apple Silicon cannot run the x86_64 Homestead box, so `vagrant up` needs an Intel Mac or a Linux host. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four real defects surfaced while auditing the devops config. Unlike #5053 these change behaviour, hence the separate PR.
1. The
tests/Featurephpunit suite matched zero testsphpunit.xml.distselects./tests/Featurewithsuffix="Test.php", but the two files there were namedExportJobAPI.phpandWebhooksPostsUpdateAPI.php. Nothing in that suite has run since 2022.They could not simply be renamed — both had rotted since the Lumen → Laravel migration:
ExportJobAPIimportedLaravel\Lumen\Testing\DatabaseTransactions; Lumen is no longer installedWebhooksPostsUpdateAPIdeclaredsetUp()/tearDown()without the: voidreturn types PHPUnit 9 requiresseeStatusCode/seeJson/seeJsonStructure/expectsJobs), none of which exists on Laravel'sTestCaseExportJobAPIreferenced\Ushahidi\Modules\Auth\GenericUser, which does not exist (the real class is\Ushahidi\Authzn\GenericUser)Ported both to Laravel 8 + PHPUnit 9 and renamed them so the suite collects them:
seeStatusCode/seeJson/seeJsonStructure→assertStatus/assertJson/assertJsonStructureon the returnedTestResponseexpectsJobs(ExportPostsJob::class)→Bus::fake()+Bus::assertDispatched()ExportJobAPITestnow creates its own admin user insetUp()rather than assuming user id 2.Ushahidi\Authzn\Session::getUser()loads the acting user from the database, and id 2 is only an admin in the behat fixture (tests/datasets/ushahidi/Base.yml) — which phpunit never loads, and which rewrites the users table when behat does load it. Owning the user keeps the suite independent of run order.phpunit goes from 172 → 176 tests, 534 → 552 assertions.
2.
behat.yml.distdeclared a Mink extension that Behat silently ignoredThe
extensions:block was indented as a child ofsuites.defaultinstead of at profile level, so Behat never activated it.That misindentation was load-bearing.
minkappears zero times incomposer.lock— it is not installed. Anyone "fixing" the indentation to hoistextensions:to where it looks like it belongs would instantly break all 738 scenarios with an extension-not-found error.So the block is deleted, not re-indented. The three contexts use no Mink. Behat still passes 738/738.
3.
.editorconfigused an invalid valueindent_style = spaces— the spec only acceptstaborspace, so editors silently ignored it and indent style was unenforced. Nowspace.4. The Vagrantfile could not find the
Homestead.ymlthat ships with the repoVagrantfileonly looked forHomestead.yamlorHomestead.json, but the repo shipsHomestead.yml— and.gitignoreignores the two names it did look for. Following the setup docs ("the Vagrant and Homestead.yml files that ship with Ushahidi", thenvagrant up) aborted withHomestead settings file not found. Broken sinceHomestead.ymlwas re-added in 2023.Homestead.ymlis now added to the lookup chain, afterHomestead.yaml/.jsonso a developer's local override still wins. Preferred over renaming the tracked file, which would land it under a gitignored name. The abort message now points at the directory it actually searches, rather thanconfDir(the vendor Homestead install).Vagrantfileparses (ruby -c),Homestead.ymlparses as YAML, and the lookup chain now resolvesHomestead.ymlwhere it previously aborted. ButHomestead.ymlpinsprovider: virtualbox, and VirtualBox on Apple Silicon cannot run the x86_64 Homestead box — sovagrant upneeds an Intel Mac or a Linux host to confirm end-to-end.Verification
Against a throwaway
mariadb:10.11stack mirroring the CI job:composer lint🤖 Generated with Claude Code