fix(me): pass the two ADR-084 arguments the /me factory stopped supplying - #531
Merged
Conversation
added 3 commits
August 16, 2026 13:25
…ying
`GET /api/softwarecatalog/api/me` returns 500 for every user on
`development` right now:
Too few arguments to function
OCA\SoftwareCatalog\Controller\ContactpersonenController::__construct(),
11 passed in .../lib/AppInfo/Application.php on line 730
and exactly 13 expected
ADR-084 (#519) added `$objectService` and `$organisationService` to
`ContactpersonenController::__construct()`. That controller is not
autowired: `Application.php` registers it with a hand-written factory
closure, added deliberately "for /me endpoint", and a hand-written
factory does not gain a constructor argument automatically.
Nothing we run catches that. `php -l` passes, phpcs/phpmd/psalm/phpstan
pass, and the controller's own unit tests pass because they construct the
controller themselves and never go through the factory. The first thing
that notices is a request. It was found by an end-to-end run, as the only
5xx URL in 176 requests — and it accounts for 42 of that suite's 47
failures.
The fix supplies both arguments from the same container entries the file
already uses twice (`ObjectServiceInterface::class` via the alias
registered at the top of `register()`, and OpenRegister's
`OrganisationService`).
`tests/Unit/AppInfo/CompositionRootArgumentsTest.php` closes the hole for
every hand-written factory in the composition root, not just this one. It
reads `Application.php`'s own source with PHP's tokeniser, extracts every
`new <Class>(...)`, and reflects the target constructor. It refuses to
report a vacuous pass: it asserts a floor on the number of call sites
parsed and on the number of classes reflected, and it keeps the set of
targets it cannot reflect explicit, because unreflectable is "unchecked",
not "clean".
… file's own namespace The first version of the parser resolved `use ... as Alias` after taking the short name, and did not resolve an unqualified name against the file's own namespace at all. Both mistakes push a call site into the "could not reflect" bucket, and unreflectable reads as unchecked, which reads as clean. With both fixed, the set of factory targets this test cannot reflect in softwarecatalog is EMPTY — all 40 call sites in the composition root are actually checked — so the declaration is now asserted exactly rather than as a subset.
… declares
The unit suite does not run on `development`. It dies with
Fatal error: Declaration of OCA\OpenRegister\Db\ObjectEntity::getUuid()
must be compatible with
OCA\OpenRegister\Contract\ObjectEntityInterface::getUuid(): ?string
in tests/Stubs/Db/ObjectEntity.php on line 151
ADR-084 made the stub `implements ObjectEntityInterface`, but its five
abstract declarations kept their untyped, docblock-only signatures. PHP
refuses to declare a class whose abstract method is less specific than
the interface it satisfies, so the failure is a class-declaration fatal,
not a test failure: it aborts the run rather than reporting anything.
Under `tests/bootstrap.php` — the config CI uses — every stub is
`require_once`d at bootstrap, so the suite dies before test one and the
job reports zero tests.
One anonymous subclass (MergeOrganisatieServiceTest) needed the same four
return types for the same reason.
`getId()` and `setObject()` are deliberately left untyped: neither is on
the contract, so nothing constrains them.
This does not fix a single test. It makes the suite measurable:
before: 50 tests, then a fatal (0 under tests/bootstrap.php)
after: Tests: 702, Assertions: 2656, Errors: 50, Failures: 12,
Warnings: 3, Deprecations: 1, Skipped: 25
The 62 problems now visible are the rest of the ADR-084 fallout and are
NOT addressed here — 38 are test helpers still declaring a return type of
the concrete `OCA\OpenRegister\Service\ObjectService` while the code they
feed now takes `ObjectServiceInterface`, and 8 are the deleted
`$container` constructor parameter shifting every positional argument
after it. They need their own change.
Contributor
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 704/704 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 11:41 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 704/704 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 12:03 UTC
Download the full PDF report from the workflow artifacts.
`development` gained #528 (`fix/stub-return-types-match-the-contract`) while this branch was open. It fixes `tests/Stubs/Db/ObjectEntity.php` with the same four return types and the same reasoning as this branch's `b711474c`, and it goes further: it also repairs the test helpers and the two services whose failures that class-load fatal was hiding. Conflict in `tests/Stubs/Db/ObjectEntity.php` resolved by taking `development`'s version wholesale. The two are equivalent in effect — `getUuid(): ?string`, `getObject(): array`, `getRegister(): ?string`, `getSchema(): ?string`, with `getId()` and `setObject()` left untyped because neither is on the contract — and #528's comment sits better. `tests/Unit/Service/MergeOrganisatieServiceTest.php` auto-merged to `development`'s content for the same reason. Both files are now byte-identical to `origin/development`. What remains on this branch is only the composition root itself, which #528 did NOT touch: `Application.php:730` still passes 11 arguments to a 13-parameter constructor, so `/api/me` still returns 500 for every user.
Contributor
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 704/704 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 12:16 UTC
Download the full PDF report from the workflow artifacts.
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.
The defect
GET /api/softwarecatalog/api/mereturns 500 for every user ondevelopmentright now:ADR-084 (#519) added
$objectServiceand$organisationServicetoContactpersonenController::__construct(). That controller is not autowired —Application.php:726registers it with a hand-written factory closure, added deliberately "for /me endpoint" — and a hand-written factory does not gain a constructor argument automatically.Nothing we run catches this.
php -lpasses. phpcs, phpmd, psalm and phpstan pass. The controller's own unit tests pass, because they build the controller themselves and never go through the factory. The first thing that notices is a request. It was found by an end-to-end run as the only 5xx URL in 176 requests, and it accounts for 42 of that suite's 47 failures.The fix
Two arguments, resolved from the same container entries this file already uses twice —
ObjectServiceInterface::class(via the alias registered at the top ofregister()) and OpenRegister'sOrganisationService.The test
tests/Unit/AppInfo/CompositionRootArgumentsTest.phpcloses the hole for every hand-written factory in the composition root, not only this one. It readsApplication.php's own source with PHP's tokeniser (so comments and strings cannot be mistaken for code), extracts everynew <Class>(...)call site, and compares it against the target constructor by reflection.A unit test of the controller cannot catch a factory arity mismatch, so this test targets the composition root itself.
Shown to fail against the unfixed composition root (
git stashof the one-hunk fix, same command, same container):With the fix:
OK (4 tests, 14 assertions).It refuses to report a vacuous pass (a check that did not run looks exactly like one that passed):
testTheParserFindsTheFactoriesItIsSupposedToCheckasserts a floor of 30 parsed call sites and thatContactpersonenControlleris among them;testUnresolvableFactoryTargetsAreDeclaredkeeps the set of targets that cannot be reflected in a unit-test run explicit — an unloadable class is "I could not tell", not a pass, and a new one appearing there is a factory this file is not checking.Before / after
lib/AppInfo/Application.phpnew <Class>(...)call sites scanned, both sidesCompositionRootArgumentsTestInstrument: a static composition-root checker (call sites from
Application.php, constructors by parsing the repo's ownlib/tree), output redirected to a file and line-counted — never piped, so no exit code is read off the last stage of a pipe. Run identically on both sides; the only difference is the one hunk.Quality legs, on the changed files
phpcs --standard=phpcs.xml—lib/AppInfo/Application.phpclean. (phpcs.xmlscopeslibonly;tests/is outside it.)phpstan analyse lib/AppInfo/Application.php tests/Unit/AppInfo/CompositionRootArgumentsTest.php—[OK] No errors, 2 files. Positive control in the same container: the same command onlib/Controller/ContactpersonenController.php+lib/Service/OrganizationSyncService.phpreports 12 errors, so the instrument can say no here.php -lclean on both files.Container:
hermiq-llm-runner:gates(PHP 8.3.33) — the host is PHP 8.2, andphp:8.3-cliships neitherpython3norgit.Parity
Base
7cf3afa6. This PR does not touch any file involved in the inherited ADR-084 red set (4 PHP Quality legs + 6 PHPUnit cells) other than adding two arguments to one factory, which strictly reduces the failure surface.E2Eon this base is separately blocked by the seed defect in #529.Not done here, deliberately
The remaining ADR-084 fallout in this repo (~37 phpstan / 6 psalm / 3 phpmd findings and the 6 PHPUnit cells) is service-layer and call-site work, and it is a different change from the composition root. Keeping this PR to the runtime fatal makes it safe to merge immediately, which is the point — it is live right now.
Rebased onto
developmentafter #528 — and the stub work here is now theirs, deliberatelyWhile this PR was open, #528 (
fix/stub-return-types-match-the-contract) landed ondevelopment. It fixestests/Stubs/Db/ObjectEntity.phpwith the same four return types and the same reasoning I had used here, and it goes further — it also repairs the test helpers and the two services whose failures the class-load fatal was hiding.origin/developmentis merged in. The one conflict, intests/Stubs/Db/ObjectEntity.php, is resolved wholesale in favour ofdevelopment:getUuid(): ?string,getObject(): array,getRegister(): ?string,getSchema(): ?string, and both leavegetId()andsetObject()untyped because neither is on the contract — equivalent in effect;tests/Unit/Service/MergeOrganisatieServiceTest.phpauto-merged todevelopment's content for the same reason. Both files are now byte-identical toorigin/development— my contribution to them is zero, and that is the correct outcome.What remains on this branch is two files, and #528 did not touch either:
The fatal is still on
development— re-verified after the merge, not assumedvs this branch:
SCANNED … calls=40and no findings. Same command, same 40 call sites, both sides.The test still fails first — re-confirmed on the merged tree
A test that no longer fails first is not evidence, so the positive control was re-run after the merge (
git checkout origin/development -- lib/AppInfo/Application.phpon the merged worktree):Restore the one hunk and it is
OK (4 tests, 14 assertions).Parity on the merged tree, measured on both sides
Unit suite (
--testsuite "Unit Tests",tests/bootstrap-unit.php,hermiq-llm-runner:gatesPHP 8.3.33),origin/developmentextracted read-only vs this branch, samevendor/:origin/development(89a7ea1b)502acbdc)Identical failure set. The four extra tests are this PR's, and all four pass.
phpstan analyse lib/AppInfo/Application.php tests/Unit/AppInfo/CompositionRootArgumentsTest.php→[OK] No errors(2 files).phpcs --standard=phpcs.xml lib/AppInfo/Application.php→ rc 0.