Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil committed Feb 4, 2025
1 parent 1c3f73a commit f69aa88
Show file tree
Hide file tree
Showing 17 changed files with 330 additions and 1 deletion.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"autoload": {
"psr-4": {
"Zenstruck\\Foundry\\": "src/",
"Zenstruck\\Foundry\\PHPStan\\": "utils/phpstan/src",
"Zenstruck\\Foundry\\Psalm\\": "utils/psalm"
},
"files": [
Expand All @@ -64,7 +65,8 @@
"psr-4": {
"Zenstruck\\Foundry\\Tests\\": ["tests/"],
"App\\": "tests/Fixture/Maker/tmp/src",
"App\\Tests\\": "tests/Fixture/Maker/tmp/tests"
"App\\Tests\\": "tests/Fixture/Maker/tmp/tests",
"Zenstruck\\Foundry\\Tests\\PHPStan\\": "utils/phpstan/tests"
},
"exclude-from-classmap": ["tests/Fixture/Maker/expected"]
},
Expand Down
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ parameters:
- src
- tests
- stubs/phpstan
- utils/phpstan

banned_code:
non_ignorable: false
Expand All @@ -27,6 +28,10 @@ parameters:
- identifier: missingType.iterableValue
path: tests/

# prevent PHPStan to force to type data providers
- identifier: missingType.iterableValue
path: utils/phpstan/tests

# We support both PHPUnit versions (this method changed in PHPUnit 10)
- identifier: function.impossibleType
path: src/Test/Factories.php
Expand Down
4 changes: 4 additions & 0 deletions phpunit-10.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
<testsuite name="reset-database">
<directory>tests/Integration/ResetDatabase</directory>
</testsuite>
<testsuite name="phpstan">
<directory>utils/phpstan/tests</directory>
<exclude>utils/phpstan/tests/data/</exclude>
</testsuite>
</testsuites>
<source ignoreSuppressionOfDeprecations="true">
<include>
Expand Down
1 change: 1 addition & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Symfony\Component\Filesystem\Filesystem;

require \dirname(__DIR__).'/vendor/autoload.php';
require \dirname(__DIR__).'/bin/tools/phpstan/vendor/autoload.php';

$fs = new Filesystem();

Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\PHPStan\FactoriesTraitMissing;

use phpDocumentor\Reflection\ProjectFactory;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\TypeWithClassName;
use PHPUnit\Framework\TestCase;
use Zenstruck\Foundry\Factory;
use Zenstruck\Foundry\Test\Factories;

/**
* @template T of Node\Expr\MethodCall|Node\Expr\StaticCall
* @implements Rule<T>
*/
abstract class AbstractFactoriesTraitMissingInTestCaseRule implements Rule
{
/**
* @param T $node
*/
public function processNode(Node $node, Scope $scope): array
{
$scopeClassReflection = $scope->getClassReflection();

if (
!$scopeClassReflection?->is(TestCase::class)
|| $scopeClassReflection->hasMethod('_bootFoundry')
|| !$this->isFoundryCall($node, $scope)
) {
return [];
}

return [
RuleErrorBuilder::message(sprintf('You must use the trait "%s" in order to use Foundry in a test.', Factories::class))
->identifier('foundry.FactoriesTraitMissing')
->build(),
];
}

/**
* @param T $node
*/
abstract protected function isFoundryCall(Node $node, Scope $scope): bool;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\PHPStan\FactoriesTraitMissing;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Type\TypeWithClassName;
use Zenstruck\Foundry\Factory;

/**
* @extends AbstractFactoriesTraitMissingInTestCaseRule<Node\Expr\MethodCall>
*/
final class FactoriesTraitMissingInTestCaseWithMethodCallRule extends AbstractFactoriesTraitMissingInTestCaseRule
{
public function getNodeType(): string
{
return Node\Expr\MethodCall::class;
}

protected function isFoundryCall(Node $node, Scope $scope): bool
{
$reflection = $scope->getType($node->var)->getObjectClassReflections()[0] ?? null;

return (bool) $reflection?->is(Factory::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\PHPStan\FactoriesTraitMissing;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use Zenstruck\Foundry\Factory;

/**
* @extends AbstractFactoriesTraitMissingInTestCaseRule<Node\Expr\StaticCall>
*/
final class FactoriesTraitMissingInTestCaseWithStaticMethodCallRule extends AbstractFactoriesTraitMissingInTestCaseRule
{
public function getNodeType(): string
{
return Node\Expr\StaticCall::class;
}

protected function isFoundryCall(Node $node, Scope $scope): bool
{
if (!$node->class instanceof Node\Name) {
return false;
}

$reflection = $scope->resolveTypeByName($node->class)->getObjectClassReflections()[0] ?? null;

return (bool)$reflection?->is(Factory::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Zenstruck\Foundry\Tests\PHPStan;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Zenstruck\Foundry\PHPStan\FactoriesTraitMissing\FactoriesTraitMissingInTestCaseWithMethodCallRule;

/**
* @extends RuleTestCase<FactoriesTraitMissingInTestCaseWithMethodCallRule>
*/
final class FactoriesTraitMissingInTestCaseWithMethodCallRuleTest extends RuleTestCase
{
protected function getRule(): Rule
{
return new FactoriesTraitMissingInTestCaseWithMethodCallRule();
}

/**
* @dataProvider ruleProvider
*/
public function test_rule(string $file, array $errors): void
{
$this->analyse([__DIR__."/data/FactoriesTraitMissing/{$file}"], $errors,);

Check failure on line 35 in utils/phpstan/tests/FactoriesTraitMissingInTestCaseWithMethodCallRuleTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Parameter #2 $expectedErrors of method PHPStan\Testing\RuleTestCase<Zenstruck\Foundry\PHPStan\FactoriesTraitMissing\FactoriesTraitMissingInTestCaseWithMethodCallRule>::analyse() expects list<array{0: string, 1: int, 2?: string|null}>, array given.
}

public static function ruleProvider(): iterable
{
$errorMessage = 'You must use the trait "Zenstruck\Foundry\Test\Factories" in order to use Foundry in a test.';

yield ['NotUsingFactoriesTraitInTest.php', [[$errorMessage, 15]]];
yield ['UsingFactoriesTraitInTest.php', []];
yield ['NotInTest.php', []];
yield ['WithAbstractClassUsingFactoriesTraitTest.php', []];
yield ['WithTraitWithFactoriesTraitTest.php', []];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Zenstruck\Foundry\Tests\PHPStan;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Zenstruck\Foundry\PHPStan\FactoriesTraitMissing\FactoriesTraitMissingInTestCaseWithStaticMethodCallRule;

/**
* @extends RuleTestCase<FactoriesTraitMissingInTestCaseWithStaticMethodCallRule>
*/
final class FactoriesTraitMissingInTestCaseWithStaticMethodCallRuleTest extends RuleTestCase
{
protected function getRule(): Rule
{
return new FactoriesTraitMissingInTestCaseWithStaticMethodCallRule();
}

/**
* @dataProvider ruleProvider
*/
public function test_rule(string $file, array $errors): void
{
$this->analyse([__DIR__ . "/data/FactoriesTraitMissing/{$file}"], $errors);

Check failure on line 35 in utils/phpstan/tests/FactoriesTraitMissingInTestCaseWithStaticMethodCallRuleTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Parameter #2 $expectedErrors of method PHPStan\Testing\RuleTestCase<Zenstruck\Foundry\PHPStan\FactoriesTraitMissing\FactoriesTraitMissingInTestCaseWithStaticMethodCallRule>::analyse() expects list<array{0: string, 1: int, 2?: string|null}>, array given.
}

public static function ruleProvider(): iterable
{
$errorMessage = 'You must use the trait "Zenstruck\Foundry\Test\Factories" in order to use Foundry in a test.';

yield ['NotUsingFactoriesTraitInTest.php', [[$errorMessage, 14], [$errorMessage, 15]]];
yield ['UsingFactoriesTraitInTest.php', []];
yield ['NotInTest.php', []];
yield ['WithAbstractClassUsingFactoriesTraitTest.php', []];
yield ['WithTraitWithFactoriesTraitTest.php', []];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Tests\PHPStan\data\FactoriesTraitMissing;

use PHPUnit\Framework\TestCase;
use Zenstruck\Foundry\Test\Factories;

abstract class AbstractClassUsingFactoriesTraitTestCase extends TestCase
{
use Factories;
}
16 changes: 16 additions & 0 deletions utils/phpstan/tests/data/FactoriesTraitMissing/NotInTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Tests\PHPStan\data\FactoriesTraitMissing;

use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;

final class NotInTest
{
public function someMethod(): void
{
ContactFactory::createOne();
ContactFactory::new()->create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Tests\PHPStan\data\FactoriesTraitMissing;

use PHPUnit\Framework\TestCase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;

final class NotUsingFactoriesTraitInTest extends TestCase
{
public function testSomething(): void
{
ContactFactory::createOne();
ContactFactory::new()->create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Tests\PHPStan\data\FactoriesTraitMissing;

use Zenstruck\Foundry\Test\Factories;

trait TraitWithFactoriesTrait
{
use Factories;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Tests\PHPStan\data\FactoriesTraitMissing;

use PHPUnit\Framework\TestCase;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;

final class UsingFactoriesTraitInTest extends TestCase
{
use Factories;

public function testSomething(): void
{
ContactFactory::createOne();
ContactFactory::new()->create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Tests\PHPStan\data\FactoriesTraitMissing;

use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;

final class WithAbstractClassUsingFactoriesTraitTest extends AbstractClassUsingFactoriesTraitTestCase
{
public function testSomething(): void
{
ContactFactory::createOne();
ContactFactory::new()->create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\Tests\PHPStan\data\FactoriesTraitMissing;

use PHPUnit\Framework\TestCase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;

final class WithTraitWithFactoriesTraitTest extends TestCase
{
use TraitWithFactoriesTrait;

public function testSomething(): void
{
ContactFactory::createOne();
ContactFactory::new()->create();
}
}

0 comments on commit f69aa88

Please sign in to comment.