Skip to content

fix: revive the inert Feature test suite, drop the dead behat Mink block - #5054

Merged
dukedanny merged 2 commits into
chore/devops-cleanupfrom
chore/devops-fixes
Jul 17, 2026
Merged

fix: revive the inert Feature test suite, drop the dead behat Mink block#5054
dukedanny merged 2 commits into
chore/devops-cleanupfrom
chore/devops-fixes

Conversation

@tuxpiper

@tuxpiper tuxpiper commented Jul 13, 2026

Copy link
Copy Markdown
Member

Stacked on #5053 — base is chore/devops-cleanup, so the diff here is just the fixes. Retarget to develop once #5053 merges.

Four real defects surfaced while auditing the devops config. Unlike #5053 these change behaviour, hence the separate PR.

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. Nothing in that suite has run since 2022.

They could not simply be renamed — both had rotted since the Lumen → Laravel migration:

  • ExportJobAPI imported Laravel\Lumen\Testing\DatabaseTransactions; Lumen is no longer installed
  • WebhooksPostsUpdateAPI declared setUp()/tearDown() without the : void return types PHPUnit 9 requires
  • both used the Lumen assertion API (seeStatusCode / seeJson / seeJsonStructure / expectsJobs), none of which exists on Laravel's TestCase
  • ExportJobAPI referenced \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/seeJsonStructureassertStatus/assertJson/assertJsonStructure on the returned TestResponse
  • expectsJobs(ExportPostsJob::class)Bus::fake() + Bus::assertDispatched()

ExportJobAPITest now creates its own admin user in setUp() 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.dist declared a Mink extension that Behat silently ignored

The extensions: block was indented as a child of suites.default instead of at profile level, so Behat never activated it.

That misindentation was load-bearing. mink appears zero times in composer.lock — it is not installed. Anyone "fixing" the indentation to hoist extensions: 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. .editorconfig used an invalid value

indent_style = spaces — the spec only accepts tab or space, so editors silently ignored it and indent style was unenforced. Now space.

4. The Vagrantfile could not find the Homestead.yml that ships with the repo

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. 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.

Homestead.yml is now added 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. The abort message now points at the directory it actually searches, rather than confDir (the vendor Homestead install).

⚠️ Statically validated, not boot-tested. Vagrantfile parses (ruby -c), Homestead.yml parses as YAML, and the lookup chain now resolves Homestead.yml where it previously aborted. But 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 to confirm end-to-end.

Verification

Against a throwaway mariadb:10.11 stack mirroring the CI job:

Check Result
composer lint ✅ all 3 rulesets
phpunit 176 tests, 552 assertions (was 172/534)
phpspec ✅ 10 specs, 42 examples
behat 738 scenarios, 6339 steps

🤖 Generated with Claude Code

@tuxpiper
tuxpiper force-pushed the chore/devops-cleanup branch from 3751a70 to c642bfe Compare July 13, 2026 17:13
@tuxpiper
tuxpiper force-pushed the chore/devops-fixes branch from 1a757b5 to 706b1fd Compare July 13, 2026 17:13
tuxpiper and others added 2 commits July 15, 2026 16:15
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
tuxpiper force-pushed the chore/devops-cleanup branch from c642bfe to 3ec822f Compare July 15, 2026 14:17
@tuxpiper
tuxpiper force-pushed the chore/devops-fixes branch from 706b1fd to 40e1606 Compare July 15, 2026 14:17
@dukedanny
dukedanny merged commit 1888f40 into chore/devops-cleanup Jul 17, 2026
4 checks passed
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>
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