From 389afdcacda1274d11dd83842b77a110702ad1f8 Mon Sep 17 00:00:00 2001 From: silverbackdan Date: Wed, 11 Nov 2026 14:55:52 +0000 Subject: [PATCH 1/2] Backdate set-up positions so a write under test changes a stored value (#585) Doctrine ORM 3.8 (doctrine/orm#12753) compares the database values of object fields, so a position write whose only change is modifiedAt in the same second as set-up is no longer an update and is neither purged nor published. --- CHANGELOG.md | 1 + CLAUDE.md | 1 + tests/Functional/Component/ComponentSiteTrait.php | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51644832..2507b823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Every change that reaches `main` adds a line under **Unreleased**. When a versio ### Tooling - The Symfony NEXT CI leg keeps PHPUnit and ParaTest on their stable releases, so it tests the next Symfony rather than unreleased test tools ([#584](https://github.com/components-web-app/api-components-bundle/pull/584)) +- The component position tests' set-up backdates the positions' `modifiedAt`, so a write under test always changes a stored value on Doctrine ORM 3.8, which skips an update whose only change is a `datetime` in the same second ([#585](https://github.com/components-web-app/api-components-bundle/issues/585)) ## [2.0.0-alpha.40](https://github.com/components-web-app/api-components-bundle/compare/2.0.0-alpha.39...2.0.0-alpha.40) - 2026-10-31 diff --git a/CLAUDE.md b/CLAUDE.md index 994d1824..2d0c7fbc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,6 +94,7 @@ API behaviour gets a functional test in `tests/Functional//Test.php - **The test app runs the Varnish xkey purger (space-joined), production runs Souin (`', '`, chunked at 1500).** Never assert an exact purge or tag header; use `CacheTagsTrait::purgedCacheTags()` / `responseCacheTags()` and their assertions, which split on `/[,\s]+/`. - **The test app imports routes with no prefix.** A test that needs `/_api` boots the kernel with `routePrefix`, which gets its own cache directory and container class. - **Test doubles with static switches** (`HubStub`, `MockClientCallback`, `UnreachableDatabaseMiddleware`, `RejectedDeleteMiddleware`, `QueryCountingMiddleware`, `BeforeTransactionCallback`, `SwitchableMailer`, `KernelHttpClient`) are reset before and after every test by `FunctionalTestCase::resetTestDoubles()`, with the `EMAIL_LINK_DEFAULT_ORIGIN` and `ORPHANED_RESOURCES_NOTIFY_RECIPIENTS` env vars; a new switch goes there. Static because the kernel can reboot between requests. `QueryCountingMiddleware::count()` is how a functional test pins a query count; the profiler collects no Doctrine queries with `debug: false`. Logs are asserted through the `app.monolog.test_handler` Monolog `TestHandler`, because the harness runs with `debug: false` and the profiler's logger collects nothing. +- **From Doctrine ORM 3.8 a write whose only change is `modifiedAt` in the same second as the stored value is not an update** (doctrine/orm#12753 compares database values, and `modifiedAt` is a `datetime`): nothing is flushed, purged or published. A test that expects a write to propagate a resource whose other fields keep their values must set it up with an earlier `modifiedAt`, as `ComponentSiteTrait::createGroupWithPositions()` does. - **Newly covering a large declarative file with a unit test costs MSI**, because its mutants start counting (functional coverage does not count). Answer with assertions on the wiring, not exclusions. - **Every CI test step runs `vendor/bin/paratest`** over `phpunit/phpunit` from Composer, `--processes="$TEST_PROCESSES"` (4, a runner's vCPUs), after warming each worker's cache, **except the coverage leg, which runs `vendor/bin/phpunit` in one process** for Codecov. Keep `brianium/paratest` on a version that supports the installed PHPUnit major. - **PHPStan baseline:** 65 findings, none of them `method.notFound` (#324 worked through all 23). Fixing a baselined finding means regenerating with `vendor/bin/phpstan analyse --memory-limit=1G --generate-baseline phpstan-baseline.neon`; a stale entry fails the job. Narrow with `instanceof` or a precise parameter type, never a `@var` override. `src/Resources/skeleton` is excluded. diff --git a/tests/Functional/Component/ComponentSiteTrait.php b/tests/Functional/Component/ComponentSiteTrait.php index 9a9f1ab1..0fec40c9 100644 --- a/tests/Functional/Component/ComponentSiteTrait.php +++ b/tests/Functional/Component/ComponentSiteTrait.php @@ -70,6 +70,12 @@ protected static function createGroupWithPositions(string $positions, string $re array_push($entities, $component, $position); } static::persistAndFlush(...$entities); + static::getEntityManager() + ->createQuery(\sprintf('UPDATE %s p SET p.modifiedAt = :earlier WHERE p.componentGroup = :group', ComponentPosition::class)) + ->setParameter('earlier', new \DateTime('-1 hour')) + ->setParameter('group', $group) + ->execute(); + static::rebootKernel(); $byValue = []; foreach ($created as [, $position]) { From 424eac7a7628ef5a05df29b161db013fe848341b Mon Sep 17 00:00:00 2001 From: silverbackdan Date: Wed, 11 Nov 2026 14:56:13 +0000 Subject: [PATCH 2/2] Link changelog entry to #586 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2507b823..3a012f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Every change that reaches `main` adds a line under **Unreleased**. When a versio ### Tooling - The Symfony NEXT CI leg keeps PHPUnit and ParaTest on their stable releases, so it tests the next Symfony rather than unreleased test tools ([#584](https://github.com/components-web-app/api-components-bundle/pull/584)) -- The component position tests' set-up backdates the positions' `modifiedAt`, so a write under test always changes a stored value on Doctrine ORM 3.8, which skips an update whose only change is a `datetime` in the same second ([#585](https://github.com/components-web-app/api-components-bundle/issues/585)) +- The component position tests' set-up backdates the positions' `modifiedAt`, so a write under test always changes a stored value on Doctrine ORM 3.8, which skips an update whose only change is a `datetime` in the same second ([#586](https://github.com/components-web-app/api-components-bundle/pull/586)) ## [2.0.0-alpha.40](https://github.com/components-web-app/api-components-bundle/compare/2.0.0-alpha.39...2.0.0-alpha.40) - 2026-10-31