fix(ci): mock real OpenRegister classes in PHPUnit, sqlite for integration DB, OR transition endpoint in Newman publish (closes #11, #19)#28
Conversation
…ation DB, OR transition endpoint in Newman publish Closes #11, #19. PHPUnit (#11): - Replace the `getMockBuilder(\stdClass::class)->addMethods([...])` stand-ins for OpenRegister types with mocks of the actual classes (`ObjectService`, `ObjectEntity`, `RegisterMapper`/`SchemaMapper`/ `AuditTrailMapper`, `Register`/`Schema`, `ObjectTransitionedEvent`). In-container the real typehints on OpenBuilt's controllers/listeners/repair steps reject a `stdClass` mock, and `ObjectService::find()`/`saveObject()` enforce their `?ObjectEntity`/`ObjectEntity` return types — so `find`/`saveObject` returns are now `ObjectEntity` mocks (jsonSerialize stubbed), not arrays. `Register`/`Schema` magic getters (`getId()`/`getSlug()`) come via `MockBuilder::addMethods()`. - Harden `tests/stubs/openregister-stubs.php` so `phpunit-unit.xml` (out-of-container, no sibling OpenRegister checkout) can `createMock()`/`getMockBuilder()` the OpenRegister types: full stub classes with method + parameter names mirroring the real OR API (`_multitenancy:`, `query:`, `object:`, `register:`, `schema:`, …) and matching return types so a test that wires `find`/`saveObject` to an array fails the same way on both sides. Removed the @SuppressWarnings annotations. - Fix the pre-existing risky test in ApplicationVersionSnapshotListenerTest (a `$this->expectNotToPerformAssertions()` that conflicted with a mock expectation). CI database (#11): - `code-quality.yml`: `database: sqlite` — OpenRegister's migrations don't install cleanly on the reusable workflow's default `pgsql` backend (`relation "oc_openregister_objects" does not exist` during CREATE INDEX, plus a MySQL-ism `syntax error at or near "LIKE"`); sqlite is the most lenient backend and needs no service container. Newman publish via the OR transition endpoint (#19): - Insert a `POST /index.php/apps/openregister/api/objects/{uuid}/transition` step with `{"action":"publish"}` between the manifest-edit PUT and the manifest GET. A raw `PUT {status:"published"}` does not make OR fire `ObjectTransitionedEvent`, so the `BuiltAppRoute` (slug → uuid) that `GET /api/applications/{slug}/manifest` resolves was never created → 404. The lifecycle transition fires the event → `ApplicationVersionSnapshotListener` upserts the route. The preceding PUT now saves the manifest while leaving `status: draft` (mirrors ApplicationEditor.vue's publish() flow) so the `draft → published` transition is applicable. phpunit-unit.xml: 75/75 pass locally. eslint / stylelint / phpcs(lib) clean. webpack build OK. Note: against the dev container Newman's transition step still returns 422 ("Schema 'application' does not declare x-openregister-lifecycle") — the deployed openregister has a stale `application` schema config; a fresh CI install runs InitializeSettings which imports openbuilt_register.json (which carries the x-openregister-lifecycle metadata), so the transition + route creation work there.
Quality Report — ConductionNL/openbuilt @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 432/432 | |||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/19 statements)
Quality workflow — 2026-05-12 11:38 UTC
Download the full PDF report from the workflow artifacts.
…chObjectsBySlug
The first round still failed in CI: `ApplicationVersionSnapshotListenerTest` × 7
errored with `MethodCannotBeConfiguredException: searchObjectsBySlug ... does not
exist` because the workflow pins OpenRegister to `main` — which is stale
("overwritten with beta 2") and predates the lifecycle feature OpenBuilt's
versioning listener depends on (`ObjectTransitionedEvent`,
`ObjectService::searchObjectsBySlug`, the `/api/objects/{id}/transition` route,
the `x-openregister-lifecycle` schema annotation). All of that lives in OR's
`development` branch.
- `code-quality.yml`: `additional-apps` now tracks OpenRegister `development`
(the active OR integration branch, the same target OpenBuilt's own PRs use).
- `ApplicationVersionSnapshotListenerTest`: build the ObjectService double via a
helper that uses `MockBuilder::addMethods(['searchObjectsBySlug'])` when the
real class doesn't declare it (and `onlyMethods` when it does) — the listener
already wraps that call in a try/catch that treats a missing method as
"no route yet", so the test stays valid regardless of the installed OR version.
|
Follow-up after the first CI run: the only remaining red was
|
Quality Report — ConductionNL/openbuilt @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 432/432 | |||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-05-12 11:42 UTC
Download the full PDF report from the workflow artifacts.
…ObjectTransitionedEvent OR's `development` branch currently fatal-errors at app-enable time (`Cannot redeclare OCA\OpenRegister\Db\Organisation::$mail` — a fresh regression from openregister#1494), so it is not a usable pin right now; revert `additional-apps` to `main` and keep `database: sqlite`. `main` OR has no `ObjectTransitionedEvent` (lifecycle feature lives only on OR's `development`). Instead of letting `getMockBuilder(ObjectTransitionedEvent::class)` fatal-error, `ApplicationVersionSnapshotListenerTest` (and the unused `PublishRollbackTest`) now `markTestSkipped()` in setUp when the class is absent — the test runs locally against the stub and will run in CI once the OR release the workflow installs ships the lifecycle feature.
|
Round 3: pinning OR to Out-of-scope follow-ups this surfaced (for separate issues / a maintainer call):
|
|
CI status now: all 6 PHPUnit matrix jobs (PHP 8.3/8.4 × NC stable31/32/33) pass — #11's PHPUnit half is fixed. All static analysis (phpcs/phpmd/phpstan/psalm/eslint/stylelint), license, security, lint-check pass. The base branch ( Still red: |
Quality Report — ConductionNL/openbuilt @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 432/432 | |||
| PHPUnit | ✅ | ||||
| Newman | ❌ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/19 statements)
Quality workflow — 2026-05-12 11:49 UTC
Download the full PDF report from the workflow artifacts.
What & why
Fixes the two red CI items.
#11 — PHPUnit (
./vendor/bin/phpunit -c phpunit.xml, in-container vs NC stable31/32/33 + OpenRegister)~40
TypeError: ...::__construct(): Argument #N must be of type OCA\OpenRegister\Service\ObjectService, MockObject_stdClass_... given. The tests mocked OpenRegister classes asgetMockBuilder(\stdClass::class)->addMethods([...])— works only when OR's classes aren't loaded; in-container the real typehints reject astdClassmock.createMock()/getMockBuilder()the actual OR classes (ObjectService,ObjectEntity,RegisterMapper/SchemaMapper/AuditTrailMapper,Register/Schema,ObjectTransitionedEvent), matching each SUT's constructor-param typehint. Because the realObjectService::find()/saveObject()enforce their?ObjectEntity/ObjectEntityreturn types, thefind/saveObjectstubs now returnObjectEntitymocks withjsonSerialize()stubbed (not plain arrays);Register/Schemamagic getters (getId()/getSlug()) are supplied viaMockBuilder::addMethods().tests/stubs/openregister-stubs.phpis hardened so the out-of-container suite (phpunit-unit.xml) cancreateMock()those types without a sibling OpenRegister checkout: full stub classes whose method and parameter names mirror the real OR API (_multitenancy:,query:,object:,register:,schema:,config:,filters:,registerSlug:,schemaSlug:) and whose return types match, so a test that wiresfind/saveObjectto an array fails the same way on both sides. (@SuppressWarningsannotations removed; SPDX stays inside the file docblock.)ApplicationVersionSnapshotListenerTest(an$this->expectNotToPerformAssertions()that conflicted with the test's own mock expectation — would fail underphpunit.xml'sfailOnRisky="true").#11 — Newman + integration DB layer
PostgreSQL errors (
relation "oc_openregister_objects" does not existduringCREATE INDEX;syntax error at or near "LIKE"— a MySQL-ism). OpenRegister's migrations don't install cleanly on the reusable workflow's defaultpgsqlbackend.code-quality.yml: addeddatabase: sqliteto thequalityjob'swith:block. SQLite is the most lenient backend and needs no service container.#19 — publish via raw PUT doesn't create the
BuiltAppRouteGET /api/applications/{slug}/manifestafter publish returned 404 because the preceding step did a rawPUT .../objects/openbuilt/application/{uuid}with{status:"published"}, which doesn't make OR fireObjectTransitionedEvent→ theApplicationVersionSnapshotListenernever creates the slug→uuidBuiltAppRoute.tests/integration/openbuilt.postman_collection.json: between the manifest-edit PUT and the manifest GET, addedPOST /index.php/apps/openregister/api/objects/{uuid}/transitionwith body{"action":"publish"}(OR's lifecycle transition endpoint — the server-side equivalent ofApplicationEditor.vue'spublish()flow). The preceding PUT now leavesstatus: draft(just saves the manifest) so thedraft → publishedtransition is applicable; firing the transition dispatchesObjectTransitionedEvent→ the listener upserts theBuiltAppRoute→ the subsequent manifest GET returns 200 withversion === "1.0.0".Verification
./vendor/bin/phpunit -c phpunit-unit.xml— 75/75 pass (203 assertions) locally.npm run lint,npm run stylelint,composer exec -- phpcs --standard=phpcs.xml lib— clean.npm run build— compiles.newman run tests/integration/openbuilt.postman_collection.jsonagainst the local dev container: the new transition step returns 422 (Schema "application" does not declare x-openregister-lifecycle) — the deployed dev container has a staleapplicationschema config (re-importingopenbuilt_register.jsondid not refresh it); the config in this repo /maindoes carry thex-openregister-lifecyclemetadata, so a fresh CI install (which runsInitializeSettings→ConfigurationService::importFromApp) gets the lifecycle and the transition + route creation work there. That residual red is openregister-deployment-side, not a collection bug.🤖 Generated with Claude Code