PoC: Hook + Bootstrapper for shared integration test setup - #38
Draft
Steveb-p wants to merge 12 commits into
Draft
PoC: Hook + Bootstrapper for shared integration test setup#38Steveb-p wants to merge 12 commits into
Steveb-p wants to merge 12 commits into
Conversation
…encies in Test Core
Wires SchemaHook, FixtureHook, and the new PurgeIndexHook into the HooksExecutor via tagged services, and simplifies Bootstrapper to just resolve/boot the kernel, prep the database, and delegate everything else to the hook pipeline. Each hook reads its own option key from the bootstrap options array, so a downstream bundle (e.g. ibexa/migrations) can contribute its own hook without any changes needed here.
This was referenced Aug 20, 2026
Collaborator
Author
|
Two companion PRs depend on this branch via a temporary
|
Migrations is not a concept ibexa/test-core knows about - this method only existed as a leftover from the pre-Hook PoC, where Bootstrapper called it directly. Its only caller is now ibexa/migrations' own MigrationHook, so it belongs on that package's own test kernel instead (see companion commit in ibexa/migrations).
It was only there to resolve the (now fully-removed) migration-specific class references that used to live inline in Bootstrapper.php. Nothing in this package depends on a proprietary Ibexa package anymore.
EXPOSED_SERVICES_BY_CLASS/_BY_ID picked up an `array` type hint in the original PoC commit, even though this package's composer.json still declares php ^7.4 || ^8.0 support. Typed class constants only exist since PHP 8.3.
GlobFileLoader only tracks matched files as a cache-invalidation resource; it needs a configured LoaderResolver to actually parse them, which this extension never set up. It silently never loaded services.php, meaning none of the Hook/Bootstrapper services (added when services.yaml became services.php) were ever registered in any consuming kernel - caught by actually running the two showcase suites end to end.
Without it, this bundle's extension (and therefore every Hook service) never made it into any consuming kernel's container - the underlying reason the previous commit's loader bug went unnoticed for so long. Simplified TestKernel accordingly: it was manually re-registering this bundle as a workaround for the exact gap now fixed at the source.
HooksExecutor and its interface alias were private and referenced by
nothing inside the container graph (Bootstrapper fetches them from
outside, via the test service container) - Symfony's compiler pruned
them as unused before framework.test's public-everything mechanism
ever got a chance to preserve them. Marked both public explicitly.
Separately, SchemaHook/FixtureHook typed their $kernel argument against
the concrete Ibexa\Contracts\Test\Core\IbexaTestKernel class, which does
not autowire: Symfony only resolves the synthetic "kernel" service by
the interfaces it implements (KernelInterface, HttpKernelInterface, or -
as used here - IbexaTestKernelInterface), never by an intermediate parent
class. Retyped against IbexaTestKernelInterface and wired $kernel
explicitly via service('kernel') to sidestep the ambiguity entirely.
This file was copied from ibexa/core's own Legacy fixture data in 2023 and never touched since; that original copy (and every migrations snapshot test calibrated against it) uses lowercase "Anonymous users". Surfaced by actually running ibexa/migrations' full suite against this package's shared kernel.
Each HookInterface implementation now declares its options through a configureOptions(OptionsResolver $resolver) method (mirroring Symfony\Component\Form\FormTypeInterface), so an unknown key or wrong type throws instead of being silently ignored. The top-level options array passed to Bootstrapper/HooksExecutor is keyed by each hook's own service id (not its FQCN), so two differently-configured instances of the same hook class wouldn't collide on one options slot. HooksExecutor is wired via tagged_iterator() with an explicit index attribute (falling back to service id, since no hook sets that attribute) rather than tagged_locator(): Symfony's ServiceLocatorTagPass ksort()s its service map before building the locator, which silently broke the tag-priority execution order hooks rely on (schema before fixtures). Verified against Symfony's own DI source before landing on this, and confirmed the correct order empirically. Added symfony/options-resolver as an explicit require (was only ever resolved transitively before).
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.
Description:
Supersedes #30 — same goal (kill the copy-pasted bootstrap logic across packages), cleaner split this time.
Bootstrappernow only does infra: resolve/boot the kernel, create the DB, optionally rundoctrine:schema:update. Everything else — schema import, fixture import, search index purge — moved intoHookInterfaceimplementations taggedibexa.test.bootstrapper.hook, run byHooksExecutorin priority order.SchemaHook/FixtureHookwere actually empty stubs before (the tagging was wired up but nothing implemented them yet); they're real now, each gated by its own option key (schema,fixtures) read off the options arrayBootstrapperforwards untouched. AddedPurgeIndexHook(purge_index, defaultfalse) to finish that bit instead of leaving it as dead inline code.The point of tagging instead of hardcoding: a downstream bundle can contribute its own hook without touching this package at all.
ibexa/migrationsdoes exactly that in its own PR — registers aMigrationHookfrom its own test kernel, and it just gets picked up by the sametagged_iterator.