Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .phpstan/ForbidTestCoverageAttributesRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\AI\PHPStan;

use PhpParser\Node;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

/**
* PHPStan rule that forbids usage of test coverage attributes in tests.
*
* This rule enforces that Large, Small, Medium, CoversClass and UsesClass attributes
* should not be used in test files.
*
* @author Oskar Stark <oskarstark@googlemail.com>
*
* @implements Rule<Node>
*/
final class ForbidTestCoverageAttributesRule implements Rule
{
private const FORBIDDEN_ATTRIBUTES = [
'Large',
'Small',
'Medium',
'CoversClass',
'UsesClass',
];

public function getNodeType(): string
{
return Node::class;
}

public function processNode(Node $node, Scope $scope): array
{
// Only check test files
if (!str_ends_with($scope->getFile(), 'Test.php')) {
return [];
}

$errors = [];

if ($node instanceof Class_ || $node instanceof ClassMethod) {
foreach ($node->attrGroups as $attrGroup) {
$errors = array_merge($errors, $this->checkAttributeGroup($attrGroup));
}
}

return $errors;
}

/**
* @return array<\PHPStan\Rules\RuleError>
*/
private function checkAttributeGroup(AttributeGroup $attrGroup): array
{
$errors = [];

foreach ($attrGroup->attrs as $attr) {
$attributeName = $attr->name->toString();

// Handle both fully qualified and short names
$shortName = $attributeName;
if (str_contains($attributeName, '\\')) {
$shortName = substr($attributeName, strrpos($attributeName, '\\') + 1);
}

if (\in_array($shortName, self::FORBIDDEN_ATTRIBUTES, true)) {
$errors[] = RuleErrorBuilder::message(
\sprintf('Usage of #[%s] attribute is forbidden in test files. Remove the attribute.', $shortName)
)
->line($attr->getLine())
->identifier('symfonyAi.forbidTestCoverageAttributes')
->tip(\sprintf('Remove the #[%s] attribute from the test.', $shortName))
->build();
}
}

return $errors;
}
}
1 change: 1 addition & 0 deletions .phpstan/extension.neon
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
rules:
- Symfony\AI\PHPStan\ForbidDeclareStrictTypesRule
- Symfony\AI\PHPStan\ForbidNativeExceptionRule
- Symfony\AI\PHPStan\ForbidTestCoverageAttributesRule
4 changes: 0 additions & 4 deletions src/mcp-sdk/tests/Message/ErrorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@

namespace Symfony\AI\McpSdk\Tests\Message;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
use Symfony\AI\McpSdk\Message\Error;

#[Small]
#[CoversClass(Error::class)]
final class ErrorTest extends TestCase
{
public function testWithIntegerId()
Expand Down
4 changes: 0 additions & 4 deletions src/mcp-sdk/tests/Message/FactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,12 @@

namespace Symfony\AI\McpSdk\Tests\Message;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
use Symfony\AI\McpSdk\Exception\InvalidInputMessageException;
use Symfony\AI\McpSdk\Message\Factory;
use Symfony\AI\McpSdk\Message\Notification;
use Symfony\AI\McpSdk\Message\Request;

#[Small]
#[CoversClass(Factory::class)]
final class FactoryTest extends TestCase
{
private Factory $factory;
Expand Down
4 changes: 0 additions & 4 deletions src/mcp-sdk/tests/Message/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@

namespace Symfony\AI\McpSdk\Tests\Message;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
use Symfony\AI\McpSdk\Message\Response;

#[Small]
#[CoversClass(Response::class)]
final class ResponseTest extends TestCase
{
public function testWithIntegerId()
Expand Down
4 changes: 0 additions & 4 deletions src/mcp-sdk/tests/Server/JsonRpcHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

namespace Symfony\AI\McpSdk\Tests\Server;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
Expand All @@ -22,8 +20,6 @@
use Symfony\AI\McpSdk\Server\NotificationHandlerInterface;
use Symfony\AI\McpSdk\Server\RequestHandlerInterface;

#[Small]
#[CoversClass(JsonRpcHandler::class)]
class JsonRpcHandlerTest extends TestCase
{
#[TestDox('Make sure a single notification can be handled by multiple handlers.')]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,12 @@

namespace Symfony\AI\McpSdk\Tests\Server\RequestHandler;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
use Symfony\AI\McpSdk\Capability\Prompt\MetadataInterface;
use Symfony\AI\McpSdk\Capability\PromptChain;
use Symfony\AI\McpSdk\Message\Request;
use Symfony\AI\McpSdk\Server\RequestHandler\PromptListHandler;

#[Small]
#[CoversClass(PromptListHandler::class)]
class PromptListHandlerTest extends TestCase
{
public function testHandleEmpty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@

namespace Symfony\AI\McpSdk\Tests\Server\RequestHandler;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
use Symfony\AI\McpSdk\Capability\Resource\CollectionInterface;
use Symfony\AI\McpSdk\Capability\Resource\MetadataInterface;
use Symfony\AI\McpSdk\Message\Request;
use Symfony\AI\McpSdk\Server\RequestHandler\ResourceListHandler;

#[Small]
#[CoversClass(ResourceListHandler::class)]
class ResourceListHandlerTest extends TestCase
{
public function testHandleEmpty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,14 @@

namespace Symfony\AI\McpSdk\Tests\Server\RequestHandler;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
use Symfony\AI\McpSdk\Capability\Tool\CollectionInterface;
use Symfony\AI\McpSdk\Capability\Tool\MetadataInterface;
use Symfony\AI\McpSdk\Capability\Tool\ToolAnnotationsInterface;
use Symfony\AI\McpSdk\Message\Request;
use Symfony\AI\McpSdk\Server\RequestHandler\ToolListHandler;

#[Small]
#[CoversClass(ToolListHandler::class)]
class ToolListHandlerTest extends TestCase
{
public function testHandleEmpty()
Expand Down
4 changes: 0 additions & 4 deletions src/mcp-sdk/tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@

namespace Symfony\AI\McpSdk\Tests;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\MockObject\Stub\Exception;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\AI\McpSdk\Server;
use Symfony\AI\McpSdk\Server\JsonRpcHandler;
use Symfony\AI\McpSdk\Tests\Fixtures\InMemoryTransport;

#[Small]
#[CoversClass(Server::class)]
class ServerTest extends TestCase
{
public function testJsonExceptions()
Expand Down
2 changes: 0 additions & 2 deletions src/platform/tests/Bridge/OpenAi/DallE/Base64ImageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@

namespace Symfony\AI\Platform\Tests\Bridge\OpenAi\DallE;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Symfony\AI\Platform\Bridge\OpenAi\DallE\Base64Image;

#[CoversClass(Base64Image::class)]
final class Base64ImageTest extends TestCase
{
public function testItCreatesBase64Image()
Expand Down
2 changes: 0 additions & 2 deletions src/platform/tests/Bridge/OpenAi/DallE/ImageResultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@

namespace Symfony\AI\Platform\Tests\Bridge\OpenAi\DallE;

use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
use Symfony\AI\Platform\Bridge\OpenAi\DallE\Base64Image;
use Symfony\AI\Platform\Bridge\OpenAi\DallE\ImageResult;
use Symfony\AI\Platform\Bridge\OpenAi\DallE\UrlImage;

#[UsesClass(Base64Image::class)]
final class ImageResultTest extends TestCase
{
public function testItCreatesImagesResult()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace Symfony\AI\Platform\Tests\Bridge\OpenAi\DallE;

use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
use Symfony\AI\Platform\Bridge\OpenAi\DallE\Base64Image;
use Symfony\AI\Platform\Bridge\OpenAi\DallE\ImageResult;
Expand All @@ -20,7 +19,6 @@
use Symfony\AI\Platform\Result\RawHttpResult;
use Symfony\Contracts\HttpClient\ResponseInterface as HttpResponse;

#[UsesClass(Base64Image::class)]
final class ResultConverterTest extends TestCase
{
public function testItIsConvertingTheResponse()
Expand Down
Loading