Skip to content

[Testing] Allow a test case to resolve package versions from a standalone composer.json - #8264

Merged
TomasVotruba merged 1 commit into
mainfrom
test-case-composer-json-versions
Aug 2, 2026
Merged

[Testing] Allow a test case to resolve package versions from a standalone composer.json#8264
TomasVotruba merged 1 commit into
mainfrom
test-case-composer-json-versions

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

A rule that implements ComposerPackageConstraintInterface is filtered out unless the bonded package is installed — RectorNodeTraverser::prepareNodeVisitors() applies ComposerPackageConstraintFilter unconditionally, so this hits the rule's own test run too. Every fixture then reports no change and the test fails.

That leaves an extension unable to test a rule bonded to a package it cannot add to require-dev. Concretely, in rector-symfony RedirectToRouteRector bonds to symfony/framework-bundle, which does not resolve against that repo's dev constraints at any major.

A test case can now name a standalone composer.json to read the versions from:

final class RedirectToRouteRectorTest extends AbstractRectorTestCase
{
    // ...

    protected function provideComposerJsonFilePath(): ?string
    {
        return __DIR__ . '/config/composer.json';
    }
}
{
    "require": {
        "symfony/framework-bundle": "^2.6"
    }
}

The require and require-dev constraints become the resolved versions, each one taking the lowest version it allows — ^2.6 resolves to 2.6.0.0. No vendor directory is read in this mode. An open constraint such as * has no lowest version and stays unresolved, same as a package that is not listed at all.

Notes

  • InstalledPackageResolver gets an optional second constructor argument. Default behaviour is untouched: without it, versions still come from vendor/composer/installed.json with the composer.json constraints applied on top.
  • The lower-bound logic is extracted from matchConstraintVersion() into resolveConstraintLowestVersion() and shared by both modes.
  • changeComposerJsonFilePath() exists because ComposerPackageConstraintFilter keeps the first resolver it is handed for the whole process. The test case binds the resolver as a singleton once, then re-points it per test case, so a custom composer.json cannot leak into the next one. Re-binding the container entry instead would leave the filter holding a stale instance.

Verified against rector-symfony: with the rule bonded and this branch in place, its suite is green at 584 tests, including the 7 RedirectToRouteRector cases that fail without it.

tests/Composer/InstalledPackageResolverTest covers the new mode and the re-point. The 32 failures in the full suite are present on main as well and are unrelated.

…lone composer.json

A rule that implements ComposerPackageConstraintInterface is filtered out
unless the bonded package is installed, which includes its own test run.
Extensions cannot always add the package to require-dev, so there was no
way to test such a rule.

AbstractRectorTestCase::provideComposerJsonFilePath() now points at a
standalone "composer.json"; its "require" and "require-dev" constraints
become the resolved versions, using the lowest version each one allows.
@TomasVotruba
TomasVotruba merged commit 4f594ba into main Aug 2, 2026
64 checks passed
@TomasVotruba
TomasVotruba deleted the test-case-composer-json-versions branch August 2, 2026 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant