Skip to content

Commit 7e34755

Browse files
committed
Add dama/doctrine-test-bundle for faster Behat test isolation
Replaces per-scenario schema drop/create with DAMA's transaction-rollback approach. EnableForeignKeys middleware registered at priority 101 (above DAMA's 100) so PRAGMA foreign_keys=ON fires before the outer BEGIN TRANSACTION, keeping SQLite ON DELETE SET NULL cascades functional.
1 parent 4a893c6 commit 7e34755

6 files changed

Lines changed: 38 additions & 16 deletions

File tree

CLAUDE.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ Companion project: **CWA Nuxt Module** (`@cwa/nuxt`) — the frontend that consu
2626

2727
```bash
2828
# Unit tests
29-
bin/phpunit
29+
php -d memory_limit=256M vendor/bin/phpunit
3030

3131
# Integration tests (Behat)
32-
bin/behat
32+
php -d memory_limit=256M vendor/bin/behat
3333

3434
# Database setup for tests
35-
bin/console -e test doctrine:database:create
36-
bin/console -e test doctrine:migrations:migrate --no-interaction
37-
bin/console -e test doctrine:schema:validate
35+
php tests/Functional/app/bin/console -e test doctrine:database:create
36+
php tests/Functional/app/bin/console -e test doctrine:migrations:migrate --no-interaction
37+
php tests/Functional/app/bin/console -e test doctrine:schema:validate
3838
```
3939

4040
Behat features live in `features/`. PHPUnit tests in `tests/`. Behat coverage is more extensive than unit — prefer adding Behat scenarios for new API behaviour, unit tests for pure logic.
@@ -164,6 +164,14 @@ This means:
164164
5. **`/_/resource_manifest/{id}` unified endpoint**`ResourceManifest` DTO (`src/ApiResource/ResourceManifest.php`) with `ResourceManifestStateProvider` resolving route paths (starts with `/`) or UUIDs (Page then AbstractPageData). `ResourceManifestVoter` delegates access control to `RouteVoter` or `AbstractRoutableVoter`. `ResourceManifestNormalizer` produces `{ "resource_iris": string[][] }` using the shared `ManifestDepthGroupTrait`.
165165
6. **`ManifestDepthGroupTrait`** (`src/Serializer/Normalizer/Trait/ManifestDepthGroupTrait.php`) — `buildDepthGroups`, `collectCurrentDepth`, `shouldSkipIri` extracted and shared between `RouteNormalizer` and `ResourceManifestNormalizer`
166166

167+
### Outstanding — `parentPage` in standard Page read group
168+
169+
**Requirement (discovered 2026-06-15):** The Nuxt module's admin parent-page picker must filter out descendants of the current page to prevent circular parent chains (e.g. A → B → A). The picker is populated from `GET /_/pages` (via `useParentPageLoader`). To detect descendants client-side, each page in that collection response must include its own `parentPage` IRI.
170+
171+
Currently `parentPage` is only in `Route:manifest:read`. It needs to be added to whatever serialization group drives the `/_/pages` collection read (e.g. `Page:read` or a shared `AbstractPage:read` group). This satisfies the principle of least exposure — there is a concrete consumer (the admin picker descendant-filter).
172+
173+
A Behat test should cover: `GET /_/pages` response includes `parentPage` for a page that has one set.
174+
167175
### Design decisions
168176

169177
- **No `$nested` boolean** — parent = nested, full stop. The presence of `$parentPage`/`$parentPageData` is the complete signal.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"behat/behat": "^3.13",
6666
"behat/mink": "*",
6767
"behatch/contexts": "dev-master",
68+
"dama/doctrine-test-bundle": "^8.6",
6869
"doctrine/doctrine-fixtures-bundle": "^4.3",
6970
"friends-of-behat/mink-browserkit-driver": "^1.6",
7071
"friends-of-behat/mink-extension": "^2.7",

features/bootstrap/DoctrineContext.php

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
use Behat\Mink\Exception\ExpectationException;
2020
use Behat\MinkExtension\Context\MinkContext;
2121
use Behatch\Context\RestContext as BehatchRestContext;
22-
use Doctrine\ORM\Tools\SchemaTool;
22+
use DAMA\DoctrineTestBundle\Doctrine\DBAL\StaticDriver;
2323
use Doctrine\Persistence\ManagerRegistry;
2424
use Doctrine\Persistence\ObjectManager;
2525
use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
@@ -67,9 +67,7 @@ final class DoctrineContext implements Context
6767
private IriConverterInterface $iriConverter;
6868
private TimestampedDataPersister $timestampedHelper;
6969
private ObjectManager $manager;
70-
private SchemaTool $schemaTool;
7170
private UserPasswordHasherInterface $passwordHasher;
72-
private array $classes;
7371
private JWTEncoderInterface $jwtEncoder;
7472
private JsonContext $jsonContext;
7573

@@ -87,18 +85,19 @@ public function __construct(ManagerRegistry $doctrine, JWTTokenManagerInterface
8785
$this->iriConverter = $iriConverter;
8886
$this->timestampedHelper = $timestampedHelper;
8987
$this->manager = $doctrine->getManager();
90-
$this->schemaTool = new SchemaTool($this->manager);
91-
$this->classes = $this->manager->getMetadataFactory()->getAllMetadata();
9288
$this->passwordHasher = $passwordHasher;
9389
$this->jwtEncoder = $jwtEncoder;
9490
}
9591

9692
/**
9793
* @BeforeSuite
9894
*/
99-
public static function clearAppCache(): void
95+
public static function prepareTestSuite(): void
10096
{
10197
exec('php tests/Functional/app/bin/console cache:clear --env=test --no-warmup');
98+
exec('php tests/Functional/app/bin/console doctrine:database:drop --force --env=test 2>/dev/null; true');
99+
exec('php tests/Functional/app/bin/console doctrine:database:create --env=test');
100+
exec('php tests/Functional/app/bin/console doctrine:schema:create --env=test');
102101
}
103102

104103
/**
@@ -115,11 +114,20 @@ public function gatherContexts(BeforeScenarioScope $scope): void
115114
/**
116115
* @BeforeScenario
117116
*/
118-
public function createDatabase(): void
117+
public function setupDatabase(): void
119118
{
120-
$this->schemaTool->dropSchema($this->classes);
121-
$this->doctrine->getManager()->clear();
122-
$this->schemaTool->createSchema($this->classes);
119+
StaticDriver::setKeepStaticConnections(true);
120+
StaticDriver::beginTransaction();
121+
$this->manager->clear();
122+
}
123+
124+
/**
125+
* @AfterScenario
126+
*/
127+
public function rollbackDatabase(): void
128+
{
129+
StaticDriver::rollBack();
130+
$this->manager->clear();
123131
}
124132

125133
private function login(array $roles = [], $useAuthHeader = false): void

tests/Functional/app/config/bundles.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
Silverback\ApiComponentsBundle\SilverbackApiComponentsBundle::class => ['all' => true],
2626
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
2727
Cocur\Slugify\Bridge\Symfony\CocurSlugifyBundle::class => ['all' => true],
28+
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
2829
FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true],
2930
Silverback\ApiComponentsBundle\Tests\Functional\TestBundle\TestBundle::class => ['test' => true],
3031
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],

tests/Functional/app/config/packages/doctrine.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ doctrine:
22
dbal:
33
driver: 'pdo_sqlite'
44
path: '%kernel.cache_dir%/../../db.sqlite'
5-
charset: 'UTF8'
5+
charset: 'UTF8'
66
orm:
77
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
88
auto_mapping: false

tests/Functional/app/config/services_test.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ services:
33
autowire: true
44
autoconfigure: true
55

6+
Doctrine\DBAL\Driver\AbstractSQLiteDriver\Middleware\EnableForeignKeys:
7+
tags:
8+
- { name: doctrine.middleware, priority: 101 }
9+
610
Silverback\ApiComponentsBundle\Features\Bootstrap\:
711
resource: '../../../../features/bootstrap/*'
812

0 commit comments

Comments
 (0)