chore(deps): upgrade phpunit 12 -> 13#284
Conversation
There was a problem hiding this comment.
Pull request overview
This PR upgrades the test toolchain to PHPUnit 13 (and the required Paratest version), updating PHPUnit configuration files accordingly and refreshing generated artifacts impacted by the dependency graph change.
Changes:
- Bump
phpunit/phpunitto^13.0andbrianium/paratestto^7.22(plus transitive PHPUnit ecosystem updates incomposer.lock). - Update PHPUnit XML schema references and Symfony PHPUnit Bridge version pins to 13.1 in all PHPUnit configs.
- Regenerate generated files impacted by the update (
phpstan-baseline.neon,config/reference.php).
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| composer.json | Raises PHPUnit and Paratest version constraints to allow PHPUnit 13. |
| composer.lock | Updates locked dependency set for PHPUnit 13 / Paratest 7.22 and related transitive packages. |
| phpunit.xml.dist | Updates PHPUnit schema URL and Symfony PHPUnit Bridge version pin. |
| config/testing/phpunit.xml.verbose | Updates PHPUnit schema URL and Symfony PHPUnit Bridge version pin for verbose config. |
| config/testing/phpunit-performance.xml | Updates PHPUnit schema URL and Symfony PHPUnit Bridge version pin for performance config. |
| config/testing/paratest.xml | Updates PHPUnit schema URL and Symfony PHPUnit Bridge version pin for Paratest config. |
| phpstan-baseline.neon | Refreshes baseline to account for new phpstan-phpunit/PHPUnit 13 typing diagnostics. |
| config/reference.php | Regenerated Symfony reference config/types file due to dependency tree update. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #284 +/- ##
============================================
- Coverage 81.47% 81.41% -0.07%
Complexity 2584 2584
============================================
Files 172 172
Lines 7116 7085 -31
============================================
- Hits 5798 5768 -30
+ Misses 1318 1317 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request upgrades PHPUnit from version 12 to 13 and Paratest to version 7.22. The changes include updates to composer dependencies and the synchronization of various PHPUnit configuration files with the new version schema. Feedback on the PR highlights that the upgrade introduced a significant number of PHPStan errors related to mock objects; the reviewer suggests using targeted ignore rules in the main configuration file rather than expanding the baseline to avoid increasing technical debt.
… rule Per gemini-code-assist review on PR #284: rather than adding 121 baseline entries (242 errors total) for the PHPUnit 13 + phpstan-phpunit stub mismatch on the legacy `->method('x')->with(...)->willReturn(...)` mock chain, switch to two targeted message-restricted ignoreErrors rules in phpstan.neon. Comment in-place documents the deprecation timeline (12.5.11 introduced, 14 removes) and that a dedicated test-modernization PR will migrate ~143 call sites before the eventual PHPUnit 14 bump. Net: phpstan-baseline.neon goes from 121 → 40 entries (242 → 80 errors). The remaining 80 are not new — they're the same set PR #285 is clearing in source; this PR will rebase against that once it merges and the baseline should converge to zero plus the two scoped rules above. Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Bumps phpunit/phpunit ^12.3 -> ^13.0 (resolves to 13.1.8) and the required ecosystem packages: - brianium/paratest ^7.11 -> ^7.22 (paratest 7.21+ requires phpunit ^13) - phpstan/phpstan-phpunit stays at 2.0.16 (no upper bound; supports 13) - phpunit/php-code-coverage 12 -> 14 (transitively) Updates the phpunit XML schema reference to 13.1 and bumps the symfony phpunit-bridge SYMFONY_PHPUNIT_VERSION env var from 12.3 to 13.1 across all phpunit/paratest configuration files. Regenerates phpstan-baseline.neon to absorb 121 new "InvocationStubber::with()" errors raised by phpstan-phpunit's stricter return-type definitions for mock builder calls in PHPUnit 13. The underlying issue is the runtime deprecation "Using with*() without expects() is deprecated and will no longer be possible in PHPUnit 14" - migrating those 121 mock setups to the expects()-based API is intentionally scoped out of this dependency bump and is a follow-up before PHPUnit 14. config/reference.php is regenerated by the symfony framework-bundle post-install script triggered by the dependency tree update. PHPUnit 13 removals (Assert::isType(), assertContainsOnly(), containsOnly(), #[RunClassInSeparateProcess], any() matcher) are not used by the codebase, so no test migrations are required. Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
… rule Per gemini-code-assist review on PR #284: rather than adding 121 baseline entries (242 errors total) for the PHPUnit 13 + phpstan-phpunit stub mismatch on the legacy `->method('x')->with(...)->willReturn(...)` mock chain, switch to two targeted message-restricted ignoreErrors rules in phpstan.neon. Comment in-place documents the deprecation timeline (12.5.11 introduced, 14 removes) and that a dedicated test-modernization PR will migrate ~143 call sites before the eventual PHPUnit 14 bump. Net: phpstan-baseline.neon goes from 121 → 40 entries (242 → 80 errors). The remaining 80 are not new — they're the same set PR #285 is clearing in source; this PR will rebase against that once it merges and the baseline should converge to zero plus the two scoped rules above. Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
1d416ae to
f6d936a
Compare
|
…rvice stdClass narrowing Resurfaced after rebasing onto main, where #284 added two ignoreErrors patterns for InvocationStubber::with()/willReturn() that this branch's mock-to-stub sweep already eliminated, and where #285's stronger Jira return narrowing made four runtime assertions tautological. - JiraHttpClientService::getOAuthConsumerKey: drop ?? '' on ticketSystem->getLogin() — the entity setter is now string-typed, so the coalesce branch is unreachable. - JiraTicketServiceTest: drop assertInstanceOf(stdClass::class, ...) in createTicket / searchTickets / getTicket / addComment tests — the service signatures already declare : stdClass. - phpstan.neon: drop the InvocationStubber::with() and willReturn() on mixed ignore patterns — every with()-on-stub call site was converted to createStub(...)->method(...)->willReturn(...) in this branch's earlier mock-to-stub sweep. Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
…ne cleanup, fully-green PHPUnit (#288) ## Description Comprehensive PHP modernization pass against the timetracker code base. Brings the suite to a fully-green PHPUnit 12 state, eliminates 16 entries from the PHPStan baseline by fixing the underlying type issues, adds `#[\SensitiveParameter]` to every credential-bearing method, and aligns the Rector and PHP-CS-Fixer configs with the project's actual PHP 8.5 / Symfony 8 / PER-CS targets. No production behaviour changes — every fix preserves runtime semantics; only types, mocks and tooling configs move. **Rebased onto `main` after #284 (PHPUnit 13 upgrade) and #285 (baseline cleanup) merged.** All 5 commits carry `Signed-off-by:` (DCO). ## Type of Change - [x] Code refactoring - [x] Performance improvement (test suite — fewer mock allocations, earlier PHPUnit 13 readiness) - [ ] Breaking change ## Changes Made ### Production hardening - **`#[\SensitiveParameter]` (PHP 8.2+)** — annotated 14 password / token / OAuth-secret parameters across `LdapClientService::setUserPass`, `ModernLdapService::{authenticate,validateInput}`, `JiraOAuthApiService::{getFetchAccessTokenClient,getClient,fetchOAuthAccessToken,storeToken,getOAuthAuthUrl}`, `TokenEncryptionService::{encryptToken,decryptToken,rotateToken}`, plus `TicketSystem::setPassword` and `UserTicketsystem::set{AccessToken,TokenSecret}`. Prevents secrets from leaking into stack traces / Sentry / Monolog output. - **Entity setter typing** — `TicketSystem` `set{Name,Url,Login,Password,TicketUrl,BookTime}`, `UserTicketsystem::set{AccessToken,TokenSecret}`, plus `User::$id` (`?int`). Replaces the legacy untyped Doctrine getters/setters that bypassed strict typing at call sites. - **Jira service return narrowing** — `JiraTicketService::{createTicket,searchTickets,getTicket,updateTicket,addComment}` previously declared `: mixed`/`: object`; now return `: stdClass` (the truthful runtime type from `json_decode($body, false)`). - **`JiraWorkLogPayloadDto`** (`final readonly`, namespace `App\DTO\Jira`) replaces `array $data`/`: mixed` in `JiraWorkLogService::{prepareWorkLogData,createWorkLog,updateWorkLog}` with a typed payload + serializer; removes the now-dead `is_object()` guard in `updateEntryWorkLog`. - **`OptimizedEntryRepository::findByFilterArrayOptimized`** — promoted `array $filter` to a precise PHPDoc array-shape (`{user_id?: int, customer_id?: int, ...}`) so PHPStan can verify each downstream `ArrayTypeHelper::getInt` call. - **Two genuine bugs unmasked by tightened types** — removed dead `null === $ticketUrl` branch in `Entry::getTicketSystemIssueLink` and dead `?? ''` on `JiraHttpClientService::getOAuthConsumerKey` (the entity setter is now string-typed, so the coalesce branch is unreachable). - **Deprecation cleanup** — `ExportService::getEntries{Ticket,Worklog}Url` no longer uses null as an array-offset (cache key now `getId() ?? ''`); `TimeCalculationService::formatDuration` swaps an implicit float `%` int for `(int) floor(fmod(...))`. ### PHPUnit 12/13 cleanup | Before | After | |---|---| | 1 error | **0** | | 14 warnings | **0** | | 6 deprecations | **0** | | 495 notices (1698 occurrences) | **0** | | 10 risky | **0** | - **`createMock` → `createStub`** for 1698 sites across 29 test files where no expectations are set (PHPUnit 12 distinguishes mocks from stubs). 19 classes received `#[AllowMockObjectsWithoutExpectations]` at class level instead, where the same fixture is reused with mixed semantics across many tests. - **Stripped 67 deprecated `->with(...)`** calls from stub-style chains (deprecated in 12, hard error in 14). Removes the two `InvocationStubber::with()` / `willReturn() on mixed` ignore patterns from `phpstan.neon` introduced by #284 — every `with()`-on-stub call site is now properly migrated. - Converted `$this->createStub(...)` → `self::createStub(...)` to satisfy PHPStan's static-call requirement. - **Performance test cleanup** — replaced naive `unlink + rmdir` with `Symfony\Filesystem::remove()`; clears 14 `Is a directory` / `Directory not empty` warnings. - **Exception-handler restoration** — added `tearDown { restore_exception_handler(); parent::tearDown(); }` to `EntryRepositoryExportTest`, balancing Symfony's `bootKernel()` registration; clears 4 risky markers. - **Real assertion** — `EntryRepositoryFullIntegrationTest::testFindOverlappingEntriesExcludesSpecifiedId` now uses a single `assertNotContains` instead of an empty-result-tolerant foreach. - **JiraOAuthApiService early-return tests** — dropped the `expectNotToPerformAssertions()` calls; the existing `mock->method(...)` calls are themselves the verifications PHPUnit was counting. - Deleted redundant `EntryTest::testGetTicketSystemIssueLinkReturnsTicketWhenNullTicketUrl` (sibling empty-string test already covers the same intent under the now-non-nullable `getTicketUrl(): string` contract). - Removed 4 tautological `assertInstanceOf(stdClass::class, $result)` calls in `JiraTicketServiceTest` since the service now declares `: stdClass`. ### Tooling alignment - **Rector** — bumped `LevelSetList::UP_TO_PHP_84` → `UP_TO_PHP_85`, dropped `SymfonySetList::SYMFONY_73` (per-version sets are `@deprecated` upstream) in favour of `->withComposerBased(symfony: true)` which auto-detects the installed Symfony 8 line, and added `->withAttributesSets(symfony: true)` so future annotation→attribute migrations run automatically. Applied the one finding the bump surfaced (`array_values($x)[0]` → `array_first($x)` in `SaveContractAction.php`). - **PHP-CS-Fixer** — bumped `@PHP84Migration` → `@PHP85Migration`. Codebase was already 8.5-clean: zero diff from the bump itself. - **Stale config cleanup** — deleted 4 unreferenced files (root `rector.php` placeholder, `config/quality/{phpstan.dist.neon,phpstan-baseline.neon,phpstan-phpat.neon}`) plus 90 orphaned PHPStan cache files under `config/quality/var/` (already in `.gitignore` line 62, so they shouldn't have been tracked). Verified no hits in CI/Makefile/composer for the deleted files. ## Testing - [x] **`make test`** (Docker) — `Tests: 1922, Assertions: 5501, Skipped: 1`, 0 errors / 0 failures / 0 warnings / 0 deprecations / 0 notices / 0 risky (the 1 skip is intentional and pre-existing). - [x] **`composer check:all`** — PHPStan level 10 + bleedingEdge `[OK] No errors` (311 files); PHPat `[OK] No errors`; PHP-CS-Fixer clean; Twig lint clean. - [x] **`bin/rector … --dry-run`** — clean. - [x] **`composer security-check`** — no advisories. - [x] **`npm run build`** — webpack compiled successfully, no warnings, 2106 assets emitted. - [x] **`make e2e`** (Playwright on ephemeral compose stack) — **118 passed, 1 failed, 2 skipped**. The single failing test (`settings.spec.ts:142 › should save show_empty_line setting`) is a parallel-state flake: it passes 3/3 in isolation; the failure is a race when the full suite runs concurrently. Not a regression caused by this branch. - [x] Manual testing completed. ## Code Quality - [x] Code follows project coding standards (`@PER-CS:risky` + `@PHP85Migration`) - [x] Self-review completed - [ ] Documentation updated — none needed; all changes are internal types and tooling - [x] No breaking changes — public API is unchanged; the `: stdClass` narrowing on `JiraTicketService` is a covariant tightening (callers already accessed `$result->key`/`$result->total` style on `stdClass`) ## Migration Notes - **`composer rector` cannot be safely tested via `composer rector -- --dry-run`** — composer doesn't forward flags through that script, so the dry-run flag is silently dropped and rector runs in apply-mode. Use `bin/rector process src --config=config/quality/rector.php --dry-run` directly when previewing. - **Tests must run inside the Docker `app-dev` container** (per the existing `make test` target). The host typically lacks `pdo_mysql` and the `db_unittest` hostname only resolves on the compose network. - **All 5 commits carry `Signed-off-by:` (DCO)**. The branch was rebased onto `main` after #284 and #285 merged. ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/netresearch/timetracker/blob/main/CONTRIBUTING.md) guidelines - [x] I agree to follow the [Code of Conduct](https://github.com/netresearch/timetracker/blob/main/CODE_OF_CONDUCT.md)



Summary
phpunit/phpunitfrom^12.3to^13.0(resolves to 13.1.8)brianium/paratestfrom^7.11to^7.22(paratest 7.21+ requires phpunit ^13)phpstan/phpstan-phpunitstays at 2.0.16 (no upper bound on phpunit; already supports 13)phpunit/php-code-coverage12 -> 14 (transitive)phpunit.xml.distand the three configs underconfig/testing/SYMFONY_PHPUNIT_VERSIONenv var from12.3to13.1everywhere it is setphpstan-baseline.neonto absorb new errors from the upgradedphpstan-phpunitextensionconfig/reference.phpis regenerated by symfony framework-bundle's post-install hook (triggered by the dependency tree update)Breaking changes addressed
The PHPUnit 13 changelog removals were checked against the codebase; none of the removed APIs are used:
Assert::isType()- not usedassertContainsOnly()/assertNotContainsOnly()- not used (assertContainsOnlyInstancesOf(), which we do use, is unaffected)containsOnly()- not used#[RunClassInSeparateProcess]attribute - not usedany()matcher (hard-deprecated) - not usedSo no production or test code changes were required to keep the suite green.
phpstan baseline regeneration
phpstan/phpstan-phpunitreflects PHPUnit 13's stricter return types:MockBuilder::method()->with(...)without an explicitexpects(...)now returnsInvocationStubber, which has nowith()method. This raises 121 newmethod.notFoundand 121 newmethod.nonObjecterrors across the test suite.The underlying runtime deprecation is "Using
with*()withoutexpects()is deprecated and will no longer be possible in PHPUnit 14" (introduced in PHPUnit 12.5.11). Migrating those 121 call sites to theexpects()-based mock setup API is intentionally scoped out of this dependency bump - it's a substantial test refactor that should land as its own PR before the PHPUnit 14 upgrade. The baseline regeneration is consistent with the regeneration that landed in #282.Test plan
make testpasses locally with phpunit 13.1.8 (1923 tests, 5676 assertions, 0 errors, 0 failures; LDAP integration tests required theldap-devcontainer to be reachable on the compose network)docker compose run --rm app-dev php -d memory_limit=1G bin/phpstan analyze --no-progressreports[OK] No errorscomposer update --ignore-platform-req=php phpunit/phpunit phpstan/phpstan-phpunit brianium/paratest symfony/phpunit-bridge --with-all-dependenciescompletes cleanly inside the dev containerFollow-ups (not in this PR)
with*()withoutexpects()to the new mock builder API before bumping to PHPUnit 14src/Service/ExportService.php(null array offsets) andsrc/Service/Util/TimeCalculationService.php(implicit float-to-int) surfaced by--display-deprecations; not new in this upgrade