Skip to content

Test suite: unblock Laravel 12 & 13 (explicit 9-13 CI matrix) - #1084

Merged
shalvah merged 6 commits into
knuckleswtf:v5from
s-shiryaev:tests/laravel-9-13-matrix
Aug 13, 2026
Merged

Test suite: unblock Laravel 12 & 13 (explicit 9-13 CI matrix)#1084
shalvah merged 6 commits into
knuckleswtf:v5from
s-shiryaev:tests/laravel-9-13-matrix

Conversation

@s-shiryaev

@s-shiryaev s-shiryaev commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

While looking into what it would take to support Laravel 13's new JsonApiResource, I ran into a more basic problem: there'd be no way to prove such a thing worked, because the test suite cannot resolve to anything above Laravel 11. That seemed worth fixing first, so this PR is only about the test infrastructure — the suite now genuinely runs on Laravel 9 through 13.

No behaviour change — src/ and camel/ are untouched. Every failure found on Laravel 12/13 turned out to be a stale expectation in the tests or a testbench default, not a regression in the package.

The problem

composer.json already advertises laravel/framework: … || ^13.0, orchestra/testbench: … || ^11.0, pest: … || ^4.0 and phpunit: … || ^12.0. But a single dev dependency held the whole tree down:

dms/phpunit-arraysubset-asserts (caps phpunit at ^10)
  → phpunit ≤10 → pest ≤2 → collision ≤7/8.5 → orchestra/workbench ≤9 → testbench ≤9 → Laravel 11

So every highest CI job resolved to Laravel 11 at best, and the declared support for 12 and 13 was never exercised.

The matrix compounded this: it varied only PHP (8.1–8.4) and let the resolver pick the framework. That is not a Laravel matrix — on PHP 8.1 highest lands on Laravel 10, on PHP 8.2 it jumps straight to 12, so Laravel 11 would have dropped out of coverage entirely once the blocker was removed, silently.

Constraints I worked within

require declares php: >=8.1, and I'm not changing that here. On PHP 8.1 the dev tree resolves to Pest 1.23 / PHPUnit 9.6 — with Laravel 9 and with Laravel 10 alike — and PHPUnit 9 does not read attributes. Pest 2 can't be forced there either: the resolver rejects the whole 2.x range on PHP 8.1 (the latest release requires PHP ^8.2, a band in the middle pulls brianium/paratest ^7.4 which needs ≥8.2, and the rest hit PHPUnit conflicts). At the other end, PHPUnit 12 no longer reads docblock metadata at all.

That single fact — one supported PHP version forcing PHPUnit 9 while another forces PHPUnit 12 — is the source of most of the compromises below. They're all listed under Future cleanup below so they can be removed when the floors move.

Keeping Laravel 9 turned out to be nearly free, incidentally: it resolves from the main manifest to the same pest 1.23 / phpunit 9.6 pair as PHP 8.1 + Laravel 10, and the suite is green on it. It costs one matrix row and no extra concessions.

Changes

Dropping dms/phpunit-arraysubset-asserts

Replaced by tests/ArraySubsetAsserts.php, a ~40-line trait reproducing the package's semantics. All 127 call sites stay exactly as they are; only the import changes, in the 19 files that carry it.

The subtlety worth flagging in review: the obvious implementation (array_replace_recursive() then assertEquals()) is stricter than the original. dms compares with ==, where [] == null holds; assertEquals([], null) fails on the type mismatch. The difference surfaces in exactly one test (GetFromFormRequestTest, an object-typed field with an example). The trait keeps the loose comparison and only reaches assertEquals() to render a diff once the arrays are known to differ. It is excluded from Pint, because strict_comparison would rewrite that comparison and quietly change what the assertions accept.

PHPUnit 9 → 12 compatibility

  • 264 /** @test */ annotations across 39 files replaced by the test_* method prefix. This is the version-neutral form — the one laravel/framework itself uses — and needs no duplication: /** @test */ is invisible to PHPUnit 12, #[Test] is invisible to PHPUnit 9.
  • Data providers are the one place with no neutral form: @dataProvider works up to PHPUnit 11, #[DataProvider] from 10. The two overlap only on 10 and 11, so both markups sit side by side on all 8 providers, plus the one @testWith block in UseResponseAttributesTest (its two rows become two #[TestWith] attributes). Verified that PHPUnit 11 does not duplicate the data set or warn — the attribute wins. (The providers were already static, so nothing to change there.)
  • Domain annotations in tests/Fixtures and the test controllers — @group, @bodyParam, @response — are input to Scribe's own extractor, not PHPUnit metadata, and are deliberately left alone. An automated annotation→attribute conversion breaks 7 tests by rewriting @group.
  • phpunit.xml migrated with --migrate-configuration: schema 12.5, <source> instead of <coverage>, backupStaticProperties, cacheDirectory, obsolete convert*ToExceptions removed. Verified that PHPUnit 9 still accepts this file and still discovers the tests, so no second config is needed for the lower rows.
  • tearDown() overrides in BehavioursTest and OutputTest now call parent::tearDown(). This also clears 32 risky tests ("Test code or tested code did not remove its own error handlers", flagged by default from PHPUnit 10): without the parent call, Testbench never got to restore the handlers it installed.

CI matrix (.github/workflows/run-tests.yml)

The framework is now pinned per job with composer update --with="laravel/framework:…" — preferred over composer require --no-update because it doesn't mutate composer.json in the working copy:

PHP Laravel deps
8.1 ^9.41 lowest
8.1 ^9.41 highest
8.1 ^10.0 highest
8.2 ^11.0 highest
8.2 ^12.0 highest
8.3 ^12.0 highest
8.3 ^13.0 highest
8.4 ^13.0 highest

The if:-guarded duplicate install/execute steps collapse into one.

About the ^9.41 floor: composer.json allows ^9.21, but the suite itself uses Rule::enum(), which only exists from 9.41.0. That's a pre-existing gap between what the package claims and what the tests can actually cover — I've pinned to what runs rather than silently widening or narrowing declared support.

Removing composer.lowest.json / composer.lowest.lock

composer update --prefer-lowest --prefer-stable --with="laravel/framework:^9.41" does the same job without a second manifest. The old one had already drifted: erusev/parsedown 1.7.4 where the real manifest has parsedown/parsedown ^1.7, a stray spatie/data-transfer-object (previously removed), and no pint, phpstan or ray. The committed lockfile bought nothing either — CI ran composer update over it anyway, and composer.lock is gitignored. That's ~8.5k lines of lockfile deleted.

Version-neutral assertions

Three distinct causes behind 27 failures on Laravel 12 and 9 on Laravel 13:

  • 18 failures, Laravel 12 only. The testbench 10 skeleton ships filesystems.disks.local.serve = true (testbench 11 ships it false). When on, Laravel registers a GET storage/{path} route, Scribe documents it, and every group count and byte-for-byte output comparison shifts. BaseLaravelTest now pins it to false so the suite doesn't depend on the testbench default.
  • 5 failures, Laravel 12 and 13. OutputTest expected storage/app/scribe/ in the console output, but the testbench 10+ skeleton points the local disk at storage/app/private. storageOutputPath() now derives the expected path from the disk instead of hardcoding it — hardcoding the new path would just break Laravel 10 in the other direction.
  • 4 failures, Laravel 12.65 and 13. current_page_url was added to the paginator's meta during the 12.x branch (11.55 doesn't have it). TestHelpers::paginationMeta() detects it from the paginator itself rather than from a version number, since it landed in a patch release.

symfony/polyfill-mbstring added to require

The one production-facing change. The codebase already calls mb_trim() / mb_ltrim() / mb_rtrim(), native only from PHP 8.4; on 8.1–8.3 they come from the polyfill (v1.31.0+). It was previously present only transitively, so the lowest-deps job was one dependency change away from a fatal error.

Docs

AGENTS.md and .github/copilot-instructions.md described PHP-CS-Fixer (the project actually uses Pint), the @test annotation, and the old PHP-only matrix. All three updated.

Out of scope

The suite now reports Tests: 329, Assertions: 988, Deprecations: 6 on Laravel 13 / PHPUnit 12 — no failures, no risky tests. The remaining 6 deprecations all come from mpociot/reflection-docblock (Tag.php, implicitly nullable parameters). That dependency is unmaintained and will keep emitting these on PHP 8.4+, so it needs a fix or a replacement rather than a patch here. Separate issue.

Future cleanup

Everything below exists only to support the lower end of the supported range. When the floors move:

When PHP 8.1 is dropped (PHPUnit 9 goes with it — this is the big one):

  • Remove the duplicated @dataProvider / @testWith docblocks; the #[DataProvider] / #[TestWith] attributes are then sufficient.
  • Pest and PHPUnit lower bounds in composer.json (^1.21, ^9.0) can be raised.
  • The test_* prefix can stay as is — it's neutral, not a workaround.

When Laravel 9 is dropped:

  • The ^9.41 CI floor and its explanatory comment go with the Laravel 9 rows.

When the floor reaches Laravel 12.65 / 13 (min testbench 11):

  • The filesystems.disks.local.serve pin in BaseLaravelTest becomes unnecessary.
  • TestHelpers::paginationMeta() can go — current_page_url is always present, and the meta blocks can be inlined again.

s-shiryaev and others added 6 commits August 10, 2026 23:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Scribe’s test infrastructure to validate Laravel 9–13 across PHPUnit 9–12 without changing package behavior.

Changes:

  • Adds an explicit Laravel/PHP CI matrix and removes obsolete lowest-dependency manifests.
  • Modernizes PHPUnit discovery, providers, configuration, and array-subset assertions.
  • Normalizes version-dependent Testbench and paginator expectations.

Reviewed changes

Copilot reviewed 52 out of 54 changed files in this pull request and generated no comments.

Show a summary per file
File Description
.github/copilot-instructions.md Documents the new CI matrix.
.github/workflows/lint.yml Improves Composer cache isolation.
.github/workflows/run-tests.yml Adds explicit Laravel 9–13 jobs.
.gitattributes Removes deleted lowest-manifest exports.
.gitignore Ignores the PHPUnit cache directory.
AGENTS.md Updates testing and Pint guidance.
composer.json Removes DMS asserts and adds the mbstring polyfill.
composer.lowest.json Removes the obsolete lowest manifest.
composer.lowest.lock Removes the obsolete lowest lockfile.
phpunit.xml Migrates configuration to PHPUnit 12.
pint.json Preserves the subset trait’s loose comparison.
tests/ArraySubsetAsserts.php Implements compatible subset assertions.
tests/BaseLaravelTest.php Stabilizes Testbench filesystem defaults.
tests/BaseUnitTest.php Uses the local assertion trait.
tests/TestHelpers.php Adds version-neutral pagination metadata.
tests/GenerateDocumentation/BehavioursTest.php Modernizes discovery and teardown.
tests/GenerateDocumentation/OutputTest.php Adapts output paths and teardown.
tests/Strategies/BodyParameters/GetFromBodyParamAttributeTest.php Migrates test discovery and assertions.
tests/Strategies/BodyParameters/GetFromBodyParamTagTest.php Migrates test discovery and assertions.
tests/Strategies/GetFromFormRequestTest.php Migrates test discovery and assertions.
tests/Strategies/GetFromInlineValidatorTest.php Migrates test discovery and assertions.
tests/Strategies/Headers/GetFromHeaderAttributeTest.php Migrates test discovery and assertions.
tests/Strategies/Headers/GetFromHeaderTagTest.php Migrates test discovery and assertions.
tests/Strategies/Metadata/GetFromDocBlocksTest.php Migrates test discovery and assertions.
tests/Strategies/Metadata/GetFromMetadataAttributesTest.php Migrates test discovery and assertions.
tests/Strategies/QueryParameters/GetFromQueryParamAttributeTest.php Migrates test discovery and assertions.
tests/Strategies/QueryParameters/GetFromQueryParamTagTest.php Migrates test discovery and assertions.
tests/Strategies/ResponseFields/GetFromResponseFieldAttributesTest.php Migrates test discovery and assertions.
tests/Strategies/ResponseFields/GetFromResponseFieldTagTest.php Migrates test discovery and assertions.
tests/Strategies/Responses/ResponseCallsTest.php Migrates test discovery.
tests/Strategies/Responses/UseApiResourceTagsTest.php Handles version-dependent pagination metadata.
tests/Strategies/Responses/UseResponseAttributesTest.php Adds PHPUnit-compatible inline datasets.
tests/Strategies/Responses/UseResponseFileTagTest.php Adds compatible data-provider metadata.
tests/Strategies/Responses/UseResponseTagTest.php Adds compatible data-provider metadata.
tests/Strategies/Responses/UseTransformerTagsTest.php Adds compatible data-provider metadata.
tests/Strategies/UrlParameters/GetFromLaravelAPITest.php Migrates test discovery and assertions.
tests/Strategies/UrlParameters/GetFromUrlParamAttributeTest.php Migrates test discovery and assertions.
tests/Strategies/UrlParameters/GetFromUrlParamTagTest.php Migrates test discovery and assertions.
tests/Unit/AfterExtractingHookTest.php Migrates test discovery.
tests/Unit/AnnotationParserTest.php Adds compatible data-provider metadata.
tests/Unit/ConfigDifferTest.php Migrates test discovery.
tests/Unit/ExtractedEndpointDataTest.php Migrates test discovery.
tests/Unit/ExtractorStrategiesInvocationTest.php Modernizes discovery and providers.
tests/Unit/ExtractorTest.php Modernizes discovery and providers.
tests/Unit/HtmlWriterTest.php Migrates test discovery.
tests/Unit/OpenAPISpecWriterTest.php Migrates test discovery.
tests/Unit/OutputEndpointDataTest.php Migrates test discovery.
tests/Unit/PathConfigurationTest.php Migrates test discovery.
tests/Unit/PostmanCollectionWriterTest.php Migrates test discovery.
tests/Unit/RouteMatcherTest.php Migrates test discovery.
tests/Unit/RoutePatternMatcherTest.php Migrates test discovery.
tests/Unit/UtilsTest.php Migrates test discovery.
tests/Unit/ValidationRuleParsingTest.php Modernizes discovery and providers.
tests/Unit/WritingUtilsTest.php Migrates test discovery.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@shalvah
shalvah merged commit 97d820e into knuckleswtf:v5 Aug 13, 2026
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants