From a3e92a4ed42579d5fce0e2cc95a0ccd9cb8e5092 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Wed, 10 Jun 2026 17:26:40 +0200 Subject: [PATCH 01/26] Migrate to PHP 8.2, PHPUnit 10 and PHP Attributes --- composer.json | 21 ++-- phpunit-extension.xml | 67 ++++------ phpunit.xml.dist | 44 +++---- psalm.xml | 17 +-- src/Annotation/AnnotationExtension.php | 38 ++++-- src/Annotation/Attribute/Lua.php | 39 ++++++ src/Annotation/Attribute/Sql.php | 39 ++++++ src/Annotation/Processor/LuaProcessor.php | 2 + src/Annotation/Processor/SqlProcessor.php | 2 + .../Requirement/LuaConditionRequirement.php | 2 + .../TarantoolVersionRequirement.php | 13 +- src/Client/IsRequestType.php | 4 +- src/Client/MockClientBuilder2.php.bak | 103 ++++++++++++++++ src/Client/MockClientBuilderOld.php.bak | 115 ++++++++++++++++++ src/Client/TestDoubleClientBuilder.php | 58 +++------ src/Client/TestDoubleFactory.php | 21 ---- .../PreparedStatementCountContext.php | 2 + .../ExpressionContext/RequestCountContext.php | 2 + .../ExpressionContext/RequestCounter.php | 1 + tests/Annotation/AnnotationExtension.php | 5 + tests/Annotation/AnnotationExtensionTest.php | 47 ++++--- .../TarantoolVersionRequirementTest.php | 4 +- tests/Expectation/RequestExpectationsTest.php | 4 +- 23 files changed, 460 insertions(+), 190 deletions(-) create mode 100644 src/Annotation/Attribute/Lua.php create mode 100644 src/Annotation/Attribute/Sql.php create mode 100755 src/Client/MockClientBuilder2.php.bak create mode 100755 src/Client/MockClientBuilderOld.php.bak diff --git a/composer.json b/composer.json index f4c4d7d..41a23bd 100644 --- a/composer.json +++ b/composer.json @@ -11,17 +11,17 @@ } ], "require": { - "php": "^7.2.5|^8", + "php": ">=8.2", "composer/package-versions-deprecated": "1.11.99.5", "composer/semver": "^3.3", - "rybakit/phpunit-extras": "^0.2.4", - "symfony/expression-language": "^3.3|^4|^5|^6", + "rybakit/phpunit-extras": "dev-feature/php82-phpunit10-attributes-migration", + "symfony/expression-language": "^7.0", "tarantool/client": "^0.10" }, "require-dev": { - "php": "^7.2.5|^8", - "friendsofphp/php-cs-fixer": "^2.19", - "vimeo/psalm": "^3.9|^4" + "friendsofphp/php-cs-fixer": "^3", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.23|^6" }, "autoload": { "psr-4": { @@ -38,5 +38,12 @@ "*": "dist" }, "sort-packages": true - } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "repositories": [{ + "name": "local", + "type": "path", + "url": "/Users/gen/dev/php/phpunit-extras" + }] } diff --git a/phpunit-extension.xml b/phpunit-extension.xml index fef869a..24118d7 100644 --- a/phpunit-extension.xml +++ b/phpunit-extension.xml @@ -1,45 +1,26 @@ - - - - - - - - - - - - tests/Annotation/AnnotationExtensionTest.php - - - - - - src - - - - - - - - - true - - - 42 - - - foobar - - - - - + + + + + + + + + + tests/Annotation/AnnotationExtensionTest.php + + + + + src + + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c48d998..205118c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,28 +1,20 @@ - - - - - - - - - - - - tests - tests/Annotation/AnnotationExtensionTest.php - - - - - - src - - + + + + + + + + + + tests + tests/Annotation/AnnotationExtensionTest.php + + + + + src + + diff --git a/psalm.xml b/psalm.xml index 18db1fb..fbc3fcb 100644 --- a/psalm.xml +++ b/psalm.xml @@ -14,15 +14,16 @@ - + + + + + + + - - - - - - + - + diff --git a/src/Annotation/AnnotationExtension.php b/src/Annotation/AnnotationExtension.php index 2c8ae49..c39baa7 100644 --- a/src/Annotation/AnnotationExtension.php +++ b/src/Annotation/AnnotationExtension.php @@ -17,24 +17,46 @@ use PHPUnitExtras\Annotation\AnnotationExtension as BaseAnnotationExtension; use Tarantool\Client\Client; -class AnnotationExtension extends BaseAnnotationExtension +final class AnnotationExtension extends BaseAnnotationExtension { use Annotations; /** @var array|string */ - private $clientConfig; + private $clientConfig = 'tcp://127.0.0.1:3301'; /** @var Client|null */ private $client; - /** - * @param array|string $clientConfig - */ - public function __construct($clientConfig = 'tcp://127.0.0.1:3301') + + + #[\Override] + public function bootstrap(\PHPUnit\TextUI\Configuration\Configuration $configuration, \PHPUnit\Runner\Extension\Facade $facade, \PHPUnit\Runner\Extension\ParameterCollection $parameters) : void { - $this->clientConfig = $clientConfig; + $this->parseParameters($parameters); + parent::bootstrap($configuration, $facade, $parameters); + } + + protected function parseParameters(\PHPUnit\Runner\Extension\ParameterCollection $parameters) : void + { + if ($parameters->has('dsn')) { + $this->clientConfig = $parameters->get('dsn'); + } else { + $closure = \Closure::bind(function () { + /** + * @psalm-suppress InaccessibleProperty + * @var \PHPUnit\Runner\Extension\ParameterCollection $this + */ + return $this->parameters; + }, $parameters, \PHPUnit\Runner\Extension\ParameterCollection::class); + $options = $closure ? $closure() : []; + + if ($options !== []) { + $this->clientConfig = $options; + } + } } + #[\Override] protected function getClient() : Client { if ($this->client) { @@ -71,7 +93,7 @@ final protected function getClientConfig(bool $resolveEnvVars = true) private static function resolveEnvValues(string $configValue) : string { - return preg_replace_callback('/%env\((?P.+?)\)%/', static function (array $matches) : string { + return (string) preg_replace_callback('/%env\((?P.+?)\)%/', static function (array $matches) : string { if (false !== $value = getenv($matches['name'])) { return $value; } diff --git a/src/Annotation/Attribute/Lua.php b/src/Annotation/Attribute/Lua.php new file mode 100644 index 0000000..119b002 --- /dev/null +++ b/src/Annotation/Attribute/Lua.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Annotation\Attribute; + +use PHPUnitExtras\Annotation\Attribute\AnnotationAttribute; + +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Lua implements AnnotationAttribute +{ + private $code; + + public function __construct(string $code) + { + $this->code = $code; + } + + #[\Override] + public function getName() : string + { + return 'lua'; + } + + #[\Override] + public function getValue() : string + { + return $this->code; + } +} diff --git a/src/Annotation/Attribute/Sql.php b/src/Annotation/Attribute/Sql.php new file mode 100644 index 0000000..45370d9 --- /dev/null +++ b/src/Annotation/Attribute/Sql.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Annotation\Attribute; + +use PHPUnitExtras\Annotation\Attribute\AnnotationAttribute; + +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Sql implements AnnotationAttribute +{ + private $code; + + public function __construct(string $code) + { + $this->code = $code; + } + + #[\Override] + public function getName() : string + { + return 'sql'; + } + + #[\Override] + public function getValue() : string + { + return $this->code; + } +} diff --git a/src/Annotation/Processor/LuaProcessor.php b/src/Annotation/Processor/LuaProcessor.php index 375bd67..d76eca9 100644 --- a/src/Annotation/Processor/LuaProcessor.php +++ b/src/Annotation/Processor/LuaProcessor.php @@ -25,11 +25,13 @@ public function __construct(Client $client) $this->client = $client; } + #[\Override] public function getName() : string { return 'lua'; } + #[\Override] public function process(string $value) : void { $this->client->evaluate($value); diff --git a/src/Annotation/Processor/SqlProcessor.php b/src/Annotation/Processor/SqlProcessor.php index 144c6b5..3c8261e 100644 --- a/src/Annotation/Processor/SqlProcessor.php +++ b/src/Annotation/Processor/SqlProcessor.php @@ -25,11 +25,13 @@ public function __construct(Client $client) $this->client = $client; } + #[\Override] public function getName() : string { return 'sql'; } + #[\Override] public function process(string $value) : void { $this->client->executeUpdate($value); diff --git a/src/Annotation/Requirement/LuaConditionRequirement.php b/src/Annotation/Requirement/LuaConditionRequirement.php index 9b1b950..8482496 100644 --- a/src/Annotation/Requirement/LuaConditionRequirement.php +++ b/src/Annotation/Requirement/LuaConditionRequirement.php @@ -25,11 +25,13 @@ public function __construct(Client $client) $this->client = $client; } + #[\Override] public function getName() : string { return 'luaCondition'; } + #[\Override] public function check(string $value) : ?string { [$result] = $this->client->evaluate("return ($value)"); diff --git a/src/Annotation/Requirement/TarantoolVersionRequirement.php b/src/Annotation/Requirement/TarantoolVersionRequirement.php index 7d8ecb4..6487de6 100644 --- a/src/Annotation/Requirement/TarantoolVersionRequirement.php +++ b/src/Annotation/Requirement/TarantoolVersionRequirement.php @@ -29,15 +29,17 @@ public function __construct(Client $client) $this->client = $client; } + #[\Override] public function getName() : string { return 'Tarantool'; } + #[\Override] public function check(string $value) : ?string { // Replace dash with dot. - $constraints = preg_replace('/(\d+\.\d+\.\d+)-(\d+)/', '$1.$2', $value); + $constraints = (string) preg_replace('/(\d+\.\d+\.\d+)-(\d+)/', '$1.$2', $value); if (Semver::satisfies($this->getVersion(), $constraints)) { return null; @@ -52,14 +54,17 @@ private function getVersion() : string return $this->version; } - $version = $this->client->call('box.info')[0]['version']; + $version = $this->client->evaluate('return box.info.version')[0]; + if (!\is_string($version)) { + throw new \UnexpectedValueException('Tarantool version must be a string'); + } // Normalize 2.2.1-3-g878e2a42c to 2.2.1.3. - $version = preg_replace('/-(\d+)-[^-]+$/', '.$1', $version); + $version = (string) preg_replace('/-(\d+)-[^-]+$/', '.$1', $version); // Treat "entrypoint" versions as "dev", // so 2.11.0-entrypoint.8 becomes 2.11.0-dev+entrypoint.8. - $version = preg_replace('/(\d)-entrypoint/', '$1-dev+entrypoint', $version); + $version = (string) preg_replace('/(\d)-entrypoint/', '$1-dev+entrypoint', $version); return $this->version = $version; } diff --git a/src/Client/IsRequestType.php b/src/Client/IsRequestType.php index b8b4c57..68bb713 100644 --- a/src/Client/IsRequestType.php +++ b/src/Client/IsRequestType.php @@ -25,18 +25,20 @@ final class IsRequestType extends Constraint public function __construct(int $requestType) { // needed for backward compatibility with PHPUnit 7 - if (\is_callable('parent::__construct')) { + if (\method_exists(parent::class, '__construct')) { parent::__construct(); } $this->requestType = $requestType; } + #[\Override] public function toString() : string { return sprintf('is a "%s" request', strtoupper(RequestTypes::getName($this->requestType))); } + #[\Override] protected function matches($other) : bool { return $other instanceof Request && $other->getType() === $this->requestType; diff --git a/src/Client/MockClientBuilder2.php.bak b/src/Client/MockClientBuilder2.php.bak new file mode 100755 index 0000000..ef7ad90 --- /dev/null +++ b/src/Client/MockClientBuilder2.php.bak @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Client; + +use Prophecy\Argument; +use Prophecy\Prophecy\ObjectProphecy; +use Tarantool\Client\Client; +use Tarantool\Client\Connection\Connection; +use Tarantool\Client\Handler\Handler; +use Tarantool\Client\Packer\Packer; +use Tarantool\Client\Request\Request; + +final class MockClientBuilder2 +{ + private $prophesize; + private $requests; + private $packer; + + public function __construct(\Closure $prophesize) + { + $this->prophesize = $prophesize; + $this->requests = new \SplObjectStorage(); + } + + public function shouldHandle($request, ...$responses) : self + { + $this->requests->attach($request, $responses); + + return $this; + } + + public function withPacker($packer) : self + { + //$self = clone $this; + $this->packer = $packer; + + return $this; + } + + public function build() : Client + { + $handler = $this->createHandler(); + + return new Client($handler->reveal()); + } + + private function createHandler() : ObjectProphecy + { + $handler = ($this->prophesize)(Handler::class); + + $connection = $this->createConnection(); + $handler->getConnection()->willReturn($connection); + + $packer = $this->createPacker(); + $handler->getPacker()->willReturn($packer); + + $defaultResponse = DummyFactory::createEmptyResponse(); + + if (!$this->requests->count()) { + $handler->handle(Argument::type(Request::class))->willReturn($defaultResponse); + + return $handler; + } + + foreach ($this->requests as $request) { + if (!$responses = $this->requests->getInfo()) { + $handler->handle($request)->willReturn($defaultResponse); + continue; + } + + $handler->handle($request)->willReturn(...$responses) + ->shouldBeCalledTimes(\count($responses)); + } + + return $handler; + } + + private function createConnection() : ObjectProphecy + { + return ($this->prophesize)(Connection::class); + } + + private function createPacker() // : ObjectProphecy + { + if ($this->packer) { + return $this->packer; + //return $this->prophet->prophesize($this->packer); + } + + return ($this->prophesize)(Packer::class); + } +} diff --git a/src/Client/MockClientBuilderOld.php.bak b/src/Client/MockClientBuilderOld.php.bak new file mode 100755 index 0000000..7b6b7d7 --- /dev/null +++ b/src/Client/MockClientBuilderOld.php.bak @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Client; + +use Prophecy\Argument; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophet; +use Tarantool\Client\Client; +use Tarantool\Client\Connection\Connection; +use Tarantool\Client\Handler\Handler; +use Tarantool\Client\Packer\Packer; +use Tarantool\Client\Request\Request; + +final class MockClientBuilderOld +{ + /** @var Prophet */ + private $prophet; + private $requests; + private $packer; + + public function __construct() + { + $this->prophet = new Prophet(); + $this->requests = new \SplObjectStorage(); + } + + public static function buildDefault() : Client + { + return (new self())->build(); + } + + public function shouldHandle($request, ...$responses) : self + { + $this->requests->attach($request, $responses); + + return $this; + } + + public function withPacker($packer) : self + { + //$self = clone $this; + $this->packer = $packer; + + return $this; + } + + public function build() : Client + { + $handler = $this->createHandler(); + + return new Client($handler->reveal()); + } + + public function checkPredictions() : void + { + $this->prophet->checkPredictions(); + } + + private function createHandler() : ObjectProphecy + { + $handler = $this->prophet->prophesize(Handler::class); + + $connection = $this->createConnection(); + $handler->getConnection()->willReturn($connection); + + $packer = $this->createPacker(); + $handler->getPacker()->willReturn($packer); + + $defaultResponse = DummyFactory::createEmptyResponse(); + + if (!$this->requests->count()) { + $handler->handle(Argument::type(Request::class))->willReturn($defaultResponse); + + return $handler; + } + + foreach ($this->requests as $request) { + if (!$responses = $this->requests->getInfo()) { + $handler->handle($request)->willReturn($defaultResponse); + continue; + } + + $handler->handle($request)->willReturn(...$responses) + ->shouldBeCalledTimes(\count($responses)); + } + + return $handler; + } + + private function createConnection() : ObjectProphecy + { + return $this->prophet->prophesize(Connection::class); + } + + private function createPacker() // : ObjectProphecy + { + if ($this->packer) { + return $this->packer; + //return $this->prophet->prophesize($this->packer); + } + + return $this->prophet->prophesize(Packer::class); + } +} diff --git a/src/Client/TestDoubleClientBuilder.php b/src/Client/TestDoubleClientBuilder.php index 8ce18b3..1a212ab 100755 --- a/src/Client/TestDoubleClientBuilder.php +++ b/src/Client/TestDoubleClientBuilder.php @@ -49,18 +49,6 @@ public function __construct(TestCase $testCase) $this->responses = [TestDoubleFactory::createEmptyResponse()]; } - public static function buildDummy() : Client - { - /** - * @psalm-suppress InternalMethod - * @psalm-suppress PropertyNotSetInConstructor - */ - $self = new self(new class() extends TestCase { - }); - - return $self->build(); - } - /** * @param Request|Constraint|int $request * @param Request|Constraint|int ...$requests @@ -77,22 +65,6 @@ public function shouldSend($request, ...$requests) : self return $this; } - /** - * @param Request|Constraint|int $request - * @param Response ...$responses - */ - public function shouldHandle($request, ...$responses) : self - { - $this->shouldSend($request); - $this->willReceive(...$responses); - - if ($responses) { - $this->shouldBeCalledTimes = \count($responses); - } - - return $this; - } - public function willReceive(Response $response, Response ...$responses) : self { $this->responses = \func_get_args(); @@ -100,20 +72,6 @@ public function willReceive(Response $response, Response ...$responses) : self return $this; } - public function willUseConnection(Connection $connection) : self - { - $this->connection = $connection; - - return $this; - } - - public function willUsePacker(Packer $packer) : self - { - $this->packer = $packer; - - return $this; - } - public function build() : Client { /** @var Handler $handler */ @@ -137,7 +95,21 @@ private function createHandler() : MockObject : $handler->method('handle'); if ($this->requests) { - $handleMocker->withConsecutive(...array_chunk($this->requests, 1)); + $invocationCount = 0; + $requests = $this->requests; + $handleMocker->with(TestCase::callback(static function ($request) use (&$invocationCount, $requests) { + if (!isset($requests[$invocationCount])) { + $invocationCount++; + return true; + } + $expected = $requests[$invocationCount++]; + + if ($expected instanceof Constraint) { + return (bool) $expected->evaluate($request, '', true); + } + + return $expected == $request; + })); } if (1 === \count($this->responses)) { diff --git a/src/Client/TestDoubleFactory.php b/src/Client/TestDoubleFactory.php index 62798da..6b40d43 100755 --- a/src/Client/TestDoubleFactory.php +++ b/src/Client/TestDoubleFactory.php @@ -32,25 +32,4 @@ public static function createEmptyResponse() : Response { return self::createResponseFromData([null]); } - - public static function createErrorResponse(string $errorMessage = '', int $errorCode = 0) : Response - { - return self::createResponse( - [Keys::ERROR_24 => $errorMessage], - [Keys::CODE => Response::TYPE_ERROR + $errorCode] - ); - } - - public static function createErrorResponseFromStack(array $errorStack) : Response - { - $errorMessage = $errorStack[0][Keys::ERROR_MESSAGE] ?? ''; - $errorCode = $errorStack[0][Keys::ERROR_CODE] ?? 0; - - return self::createResponse([ - Keys::ERROR_24 => $errorMessage, - Keys::ERROR => [Keys::ERROR_STACK => $errorStack], - ], [ - Keys::CODE => Response::TYPE_ERROR + $errorCode, - ]); - } } diff --git a/src/Expectation/ExpressionContext/PreparedStatementCountContext.php b/src/Expectation/ExpressionContext/PreparedStatementCountContext.php index 0d8d71d..ab69daa 100644 --- a/src/Expectation/ExpressionContext/PreparedStatementCountContext.php +++ b/src/Expectation/ExpressionContext/PreparedStatementCountContext.php @@ -52,11 +52,13 @@ public static function atMost(Client $client, int $count) : self return new self($client, "new_count <= old_count + $count"); } + #[\Override] public function getExpression() : string { return $this->expression; } + #[\Override] public function getValues() : array { if (null === $this->finalValue) { diff --git a/src/Expectation/ExpressionContext/RequestCountContext.php b/src/Expectation/ExpressionContext/RequestCountContext.php index b5f821a..20d4226 100644 --- a/src/Expectation/ExpressionContext/RequestCountContext.php +++ b/src/Expectation/ExpressionContext/RequestCountContext.php @@ -75,11 +75,13 @@ public static function atMost(Client $client, RequestCounter $requestCounter, st return new self($client, $requestCounter, $requestName, "new_count <= old_count + $count"); } + #[\Override] public function getExpression() : string { return $this->expression; } + #[\Override] public function getValues() : array { if (null === $this->finalValue) { diff --git a/src/Expectation/ExpressionContext/RequestCounter.php b/src/Expectation/ExpressionContext/RequestCounter.php index c020cea..a1cfee8 100644 --- a/src/Expectation/ExpressionContext/RequestCounter.php +++ b/src/Expectation/ExpressionContext/RequestCounter.php @@ -25,6 +25,7 @@ final class RequestCounter implements Middleware */ private $requestCount = []; + #[\Override] public function process(Request $request, Handler $handler) : Response { $response = $handler->handle($request); diff --git a/tests/Annotation/AnnotationExtension.php b/tests/Annotation/AnnotationExtension.php index d2a9e83..fe084e6 100755 --- a/tests/Annotation/AnnotationExtension.php +++ b/tests/Annotation/AnnotationExtension.php @@ -27,4 +27,9 @@ protected function getClient() : Client return TestDoubleClientBuilder::buildDummy(); } + + public function doParseParameters(array $parameters) : void + { + $this->parseParameters(\PHPUnit\Runner\Extension\ParameterCollection::fromArray($parameters)); + } } diff --git a/tests/Annotation/AnnotationExtensionTest.php b/tests/Annotation/AnnotationExtensionTest.php index cf9c334..821bec1 100755 --- a/tests/Annotation/AnnotationExtensionTest.php +++ b/tests/Annotation/AnnotationExtensionTest.php @@ -14,35 +14,36 @@ namespace Tarantool\PhpUnit\Tests\Annotation; use PHPUnit\Framework\TestCase; +use Tarantool\PhpUnit\Annotation\Attribute\Lua; final class AnnotationExtensionTest extends TestCase { - /** - * @lua dummy_code_to_trigger_annotation_processing = true - */ - public function testConstructorUsesDefaultDsn() : void + private function bootstrapExtension(string $method, array $parameters = []) : AnnotationExtension { $ext = new AnnotationExtension(); + $ext->doParseParameters($parameters); + $ext->processTestAttributes(self::class, $method); + return $ext; + } + + #[Lua('dummy_code_to_trigger_annotation_processing = true')] + public function testConstructorUsesDefaultDsn() : void + { + $ext = $this->bootstrapExtension(__FUNCTION__); - $ext->executeBeforeTest(__METHOD__); self::assertSame('tcp://127.0.0.1:3301', $ext->resolvedDnsOrOptions); } - /** - * @lua dummy_code_to_trigger_annotation_processing = true - */ + #[Lua('dummy_code_to_trigger_annotation_processing = true')] public function testConstructorUsesCustomDsn() : void { $dsn = 'tcp://tnt_foobar:3302'; - $ext = new AnnotationExtension($dsn); + $ext = $this->bootstrapExtension(__FUNCTION__, ['dsn' => $dsn]); - $ext->executeBeforeTest(__METHOD__); self::assertSame($dsn, $ext->resolvedDnsOrOptions); } - /** - * @lua dummy_code_to_trigger_annotation_processing = true - */ + #[Lua('dummy_code_to_trigger_annotation_processing = true')] public function testGetClientConfigNormalizesDsnString() : void { $hostname = 'tnt_foobar'; @@ -51,15 +52,12 @@ public function testGetClientConfigNormalizesDsnString() : void $envPortName = 'tnt_phpunit_env_port_'.random_int(1, 1000); putenv("$envHostName=$hostname"); putenv("$envPortName=$port"); - $ext = new AnnotationExtension("tcp://%env($envHostName)%:%env($envPortName)%"); + $ext = $this->bootstrapExtension(__FUNCTION__, ['dsn' => "tcp://%env($envHostName)%:%env($envPortName)%"]); - $ext->executeBeforeTest(__METHOD__); self::assertSame("tcp://$hostname:$port", $ext->resolvedDnsOrOptions); } - /** - * @lua dummy_code_to_trigger_annotation_processing = true - */ + #[Lua('dummy_code_to_trigger_annotation_processing = true')] public function testGetClientConfigNormalizesOptionArray() : void { $hostname = 'tnt_foobar'; @@ -68,17 +66,16 @@ public function testGetClientConfigNormalizesOptionArray() : void $envPortName = 'tnt_phpunit_env_port_'.random_int(1, 1000); putenv("$envHostName=$hostname"); putenv("$envPortName=$port"); - $ext = new AnnotationExtension([ + $ext = $this->bootstrapExtension(__FUNCTION__, [ 'uri' => "tcp://%env($envHostName)%:%env($envPortName)%", - 'socket_timeout' => 10, - 'persistent' => true, + 'socket_timeout' => '10', + 'persistent' => '1', ]); - $ext->executeBeforeTest(__METHOD__); - self::assertSame([ + self::assertEquals([ 'uri' => "tcp://$hostname:$port", - 'socket_timeout' => 10, - 'persistent' => true, + 'socket_timeout' => '10', + 'persistent' => '1', ], $ext->resolvedDnsOrOptions); } } diff --git a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php index 57983dc..774b814 100755 --- a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php @@ -39,7 +39,7 @@ public function testCheckPassesForValidConstraints(string $serverVersion, string self::assertNull($requirement->check($constraints)); } - public function provideCheckPassesForValidConstraintsData() : iterable + public static function provideCheckPassesForValidConstraintsData() : iterable { $v2_3_1_3 = '2.3.1-3-g878e2a42c'; @@ -112,7 +112,7 @@ public function testCheckFailsForInvalidConstraints(string $serverVersion, strin self::assertSame($errorMessage, $requirement->check($constraints)); } - public function provideCheckFailsForInvalidConstraintsData() : iterable + public static function provideCheckFailsForInvalidConstraintsData() : iterable { $v2_3_1_3 = '2.3.1-3-g878e2a42c'; diff --git a/tests/Expectation/RequestExpectationsTest.php b/tests/Expectation/RequestExpectationsTest.php index 3453ec6..76ab378 100755 --- a/tests/Expectation/RequestExpectationsTest.php +++ b/tests/Expectation/RequestExpectationsTest.php @@ -33,7 +33,7 @@ protected function getClient() : Client { // increase values for eval requests // to eliminate RequestCounter's count adjustments - if ('Eval' === $this->getProvidedData()[0]) { + if ('Eval' === $this->providedData()[0]) { ++$this->oldValue; $this->newValue += 2; } @@ -47,7 +47,7 @@ protected function getClient() : Client ->build(); } - public function provideRequestNames() : iterable + public static function provideRequestNames() : iterable { return [ ['Auth'], From f030d87631ba9fff687ed67522403152e441847c Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 19:49:14 +0200 Subject: [PATCH 02/26] Fix Psalm and response shape for evaluate --- src/Annotation/AnnotationExtension.php | 2 +- src/Client/TestDoubleClientBuilder.php | 22 +++++++ .../TarantoolVersionRequirementTest.php | 19 +++---- tests/Expectation/RequestExpectationsTest.php | 57 +++++-------------- 4 files changed, 46 insertions(+), 54 deletions(-) diff --git a/src/Annotation/AnnotationExtension.php b/src/Annotation/AnnotationExtension.php index c39baa7..834f6ef 100644 --- a/src/Annotation/AnnotationExtension.php +++ b/src/Annotation/AnnotationExtension.php @@ -17,7 +17,7 @@ use PHPUnitExtras\Annotation\AnnotationExtension as BaseAnnotationExtension; use Tarantool\Client\Client; -final class AnnotationExtension extends BaseAnnotationExtension +class AnnotationExtension extends BaseAnnotationExtension { use Annotations; diff --git a/src/Client/TestDoubleClientBuilder.php b/src/Client/TestDoubleClientBuilder.php index 1a212ab..d4260bb 100755 --- a/src/Client/TestDoubleClientBuilder.php +++ b/src/Client/TestDoubleClientBuilder.php @@ -49,6 +49,13 @@ public function __construct(TestCase $testCase) $this->responses = [TestDoubleFactory::createEmptyResponse()]; } + public static function buildDummy() : Client + { + /** @psalm-suppress InternalMethod */ + $self = new self(new class('dummy') extends TestCase {}); + return $self->build(); + } + /** * @param Request|Constraint|int $request * @param Request|Constraint|int ...$requests @@ -65,6 +72,21 @@ public function shouldSend($request, ...$requests) : self return $this; } + /** + * @param Request|Constraint|int $request + */ + public function shouldHandle($request, Response ...$responses) : self + { + $this->shouldSend($request); + $this->willReceive(...$responses); + + if ($responses) { + $this->shouldBeCalledTimes = \count($responses); + } + + return $this; + } + public function willReceive(Response $response, Response ...$responses) : self { $this->responses = \func_get_args(); diff --git a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php index 774b814..14ceb75 100755 --- a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php @@ -13,8 +13,9 @@ namespace Tarantool\PhpUnit\Tests\Annotation\Requirement; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Tarantool\Client\Request\CallRequest; +use Tarantool\Client\Request\EvaluateRequest; use Tarantool\PhpUnit\Annotation\Requirement\TarantoolVersionRequirement; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -23,15 +24,13 @@ final class TarantoolVersionRequirementTest extends TestCase { use TestDoubleClient; - /** - * @dataProvider provideCheckPassesForValidConstraintsData() - */ + #[DataProvider('provideCheckPassesForValidConstraintsData')] public function testCheckPassesForValidConstraints(string $serverVersion, string $constraints) : void { $mockClient = $this->getTestDoubleClientBuilder() ->shouldHandle( - new CallRequest('box.info'), - TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) + new EvaluateRequest('return box.info.version'), + TestDoubleFactory::createResponseFromData([$serverVersion])) ->build(); $requirement = new TarantoolVersionRequirement($mockClient); @@ -95,15 +94,13 @@ public static function provideCheckPassesForValidConstraintsData() : iterable ]; } - /** - * @dataProvider provideCheckFailsForInvalidConstraintsData() - */ + #[DataProvider('provideCheckFailsForInvalidConstraintsData')] public function testCheckFailsForInvalidConstraints(string $serverVersion, string $constraints) : void { $mockClient = $this->getTestDoubleClientBuilder() ->shouldHandle( - new CallRequest('box.info'), - TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) + new EvaluateRequest('return box.info.version'), + TestDoubleFactory::createResponseFromData([$serverVersion])) ->build(); $requirement = new TarantoolVersionRequirement($mockClient); diff --git a/tests/Expectation/RequestExpectationsTest.php b/tests/Expectation/RequestExpectationsTest.php index 76ab378..160b194 100755 --- a/tests/Expectation/RequestExpectationsTest.php +++ b/tests/Expectation/RequestExpectationsTest.php @@ -13,6 +13,7 @@ namespace Tarantool\PhpUnit\Tests\Expectation; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\ExpectationFailedException; use PHPUnitExtras\TestCase; use Tarantool\Client\Client; @@ -63,9 +64,7 @@ public static function provideRequestNames() : iterable ]; } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledSucceeds(string $requestName) : void { $this->oldValue = 1; @@ -74,9 +73,7 @@ public function testExpectToBeCalledSucceeds(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledFails(string $requestName) : void { $this->oldValue = 1; @@ -87,9 +84,7 @@ public function testExpectToBeCalledFails(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtLeastSucceeds(string $requestName) : void { $this->oldValue = 1; @@ -98,9 +93,7 @@ public function testExpectToBeCalledAtLeastSucceeds(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtLeastFails(string $requestName) : void { $this->oldValue = 1; @@ -111,9 +104,7 @@ public function testExpectToBeCalledAtLeastFails(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtMostSucceeds(string $requestName) : void { $this->oldValue = 1; @@ -122,9 +113,7 @@ public function testExpectToBeCalledAtMostSucceeds(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtMostFails(string $requestName) : void { $this->oldValue = 1; @@ -135,9 +124,7 @@ public function testExpectToBeCalledAtMostFails(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledOnceSucceeds(string $requestName) : void { $this->oldValue = 1; @@ -146,9 +133,7 @@ public function testExpectToBeCalledOnceSucceeds(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledOnceFails(string $requestName) : void { $this->oldValue = 1; @@ -159,9 +144,7 @@ public function testExpectToBeCalledOnceFails(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeNeverCalledSucceeds(string $requestName) : void { $this->oldValue = 1; @@ -170,9 +153,7 @@ public function testExpectToBeNeverCalledSucceeds(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeNeverCalledFails(string $requestName) : void { $this->oldValue = 1; @@ -183,9 +164,7 @@ public function testExpectToBeNeverCalledFails(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtLeastOnceSucceeds(string $requestName) : void { $this->oldValue = 1; @@ -194,9 +173,7 @@ public function testExpectToBeCalledAtLeastOnceSucceeds(string $requestName) : v $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtLeastOnceFails(string $requestName) : void { $this->oldValue = 1; @@ -207,9 +184,7 @@ public function testExpectToBeCalledAtLeastOnceFails(string $requestName) : void $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtMostOnceSucceeds(string $requestName) : void { $this->oldValue = 1; @@ -218,9 +193,7 @@ public function testExpectToBeCalledAtMostOnceSucceeds(string $requestName) : vo $this->verifyExpectations(); } - /** - * @dataProvider provideRequestNames - */ + #[DataProvider('provideRequestNames')] public function testExpectToBeCalledAtMostOnceFails(string $requestName) : void { $this->oldValue = 1; From bb367f48a6eb0e67c0fbd692d2114110055d6522 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 19:56:05 +0200 Subject: [PATCH 03/26] Restore library API methods incorrectly removed by Psalm --- src/Client/TestDoubleClientBuilder.php | 14 ++++++++++++++ src/Client/TestDoubleFactory.php | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+) mode change 100755 => 100644 src/Client/TestDoubleFactory.php diff --git a/src/Client/TestDoubleClientBuilder.php b/src/Client/TestDoubleClientBuilder.php index d4260bb..a067baf 100755 --- a/src/Client/TestDoubleClientBuilder.php +++ b/src/Client/TestDoubleClientBuilder.php @@ -94,6 +94,20 @@ public function willReceive(Response $response, Response ...$responses) : self return $this; } + public function willUseConnection(Connection $connection) : self + { + $this->connection = $connection; + + return $this; + } + + public function willUsePacker(Packer $packer) : self + { + $this->packer = $packer; + + return $this; + } + public function build() : Client { /** @var Handler $handler */ diff --git a/src/Client/TestDoubleFactory.php b/src/Client/TestDoubleFactory.php old mode 100755 new mode 100644 index 6b40d43..62798da --- a/src/Client/TestDoubleFactory.php +++ b/src/Client/TestDoubleFactory.php @@ -32,4 +32,25 @@ public static function createEmptyResponse() : Response { return self::createResponseFromData([null]); } + + public static function createErrorResponse(string $errorMessage = '', int $errorCode = 0) : Response + { + return self::createResponse( + [Keys::ERROR_24 => $errorMessage], + [Keys::CODE => Response::TYPE_ERROR + $errorCode] + ); + } + + public static function createErrorResponseFromStack(array $errorStack) : Response + { + $errorMessage = $errorStack[0][Keys::ERROR_MESSAGE] ?? ''; + $errorCode = $errorStack[0][Keys::ERROR_CODE] ?? 0; + + return self::createResponse([ + Keys::ERROR_24 => $errorMessage, + Keys::ERROR => [Keys::ERROR_STACK => $errorStack], + ], [ + Keys::CODE => Response::TYPE_ERROR + $errorCode, + ]); + } } From ee84ea0f434d30a71c86740f1df89b67d0b8648f Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:08:10 +0200 Subject: [PATCH 04/26] Fix composer.json and phpunit xml --- composer.json | 2 +- phpunit-extension.xml | 2 +- phpunit.xml.dist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 41a23bd..429cc17 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ ], "require": { "php": ">=8.2", - "composer/package-versions-deprecated": "1.11.99.5", + "composer/package-versions-deprecated": "^1.11", "composer/semver": "^3.3", "rybakit/phpunit-extras": "dev-feature/php82-phpunit10-attributes-migration", "symfony/expression-language": "^7.0", diff --git a/phpunit-extension.xml b/phpunit-extension.xml index 24118d7..f343a3e 100644 --- a/phpunit-extension.xml +++ b/phpunit-extension.xml @@ -1,5 +1,5 @@ - + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 205118c..c6e4b88 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,5 @@ - + From d5295a9113bce7118ee1aa260a0c3ce73536d174 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:10:58 +0200 Subject: [PATCH 05/26] Use ^8.2 for PHP constraint --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 429cc17..d2d2cd5 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": ">=8.2", + "php": "^8.2", "composer/package-versions-deprecated": "^1.11", "composer/semver": "^3.3", "rybakit/phpunit-extras": "dev-feature/php82-phpunit10-attributes-migration", From 5586fdc045aeac2c33ea4eabd2855ec3a6439771 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:31:07 +0200 Subject: [PATCH 06/26] Drop unsupported PHP versions from CI matrix --- .github/workflows/qa.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index c2fe040..8ba218d 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1'] + php-versions: ['8.2', '8.3', '8.4'] runs-on: ${{ matrix.operating-system }} steps: - name: Checkout @@ -51,7 +51,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.2' coverage: none - name: Get composer cache directory @@ -81,7 +81,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: '8.2' coverage: none - name: Get composer cache directory From 9785da37f7c1e851ff6840b551a5eadb7326dec6 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:33:42 +0200 Subject: [PATCH 07/26] Update GitHub Actions to v4 --- .github/workflows/qa.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 8ba218d..0712db4 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -14,7 +14,7 @@ jobs: runs-on: ${{ matrix.operating-system }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -27,7 +27,7 @@ jobs: run: echo "::set-output name=dir::$(composer config cache-files-dir)" - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -59,7 +59,7 @@ jobs: run: echo "::set-output name=dir::$(composer config cache-files-dir)" - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -76,7 +76,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -89,7 +89,7 @@ jobs: run: echo "::set-output name=dir::$(composer config cache-files-dir)" - name: Cache composer dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} From 61e586909a4d16d4fbbc56c6a7928564d4845f77 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:34:32 +0200 Subject: [PATCH 08/26] Fix composer repositories for CI --- composer.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index d2d2cd5..641dad4 100644 --- a/composer.json +++ b/composer.json @@ -42,8 +42,7 @@ "minimum-stability": "dev", "prefer-stable": true, "repositories": [{ - "name": "local", - "type": "path", - "url": "/Users/gen/dev/php/phpunit-extras" + "type": "vcs", + "url": "https://github.com/rybakit/phpunit-extras" }] } From 52157805d576a59082a910b371e79750cfe51b35 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:38:05 +0200 Subject: [PATCH 09/26] Rename .php_cs.dist to .php-cs-fixer.dist.php --- .php_cs.dist => .php-cs-fixer.dist.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .php_cs.dist => .php-cs-fixer.dist.php (100%) diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 100% rename from .php_cs.dist rename to .php-cs-fixer.dist.php From 043ed1272a2cd451642e6d5846437e8d7e87d1c2 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:38:50 +0200 Subject: [PATCH 10/26] Remove accidental .bak files --- src/Client/MockClientBuilder2.php.bak | 103 --------------------- src/Client/MockClientBuilderOld.php.bak | 115 ------------------------ 2 files changed, 218 deletions(-) delete mode 100755 src/Client/MockClientBuilder2.php.bak delete mode 100755 src/Client/MockClientBuilderOld.php.bak diff --git a/src/Client/MockClientBuilder2.php.bak b/src/Client/MockClientBuilder2.php.bak deleted file mode 100755 index ef7ad90..0000000 --- a/src/Client/MockClientBuilder2.php.bak +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Client; - -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; -use Tarantool\Client\Client; -use Tarantool\Client\Connection\Connection; -use Tarantool\Client\Handler\Handler; -use Tarantool\Client\Packer\Packer; -use Tarantool\Client\Request\Request; - -final class MockClientBuilder2 -{ - private $prophesize; - private $requests; - private $packer; - - public function __construct(\Closure $prophesize) - { - $this->prophesize = $prophesize; - $this->requests = new \SplObjectStorage(); - } - - public function shouldHandle($request, ...$responses) : self - { - $this->requests->attach($request, $responses); - - return $this; - } - - public function withPacker($packer) : self - { - //$self = clone $this; - $this->packer = $packer; - - return $this; - } - - public function build() : Client - { - $handler = $this->createHandler(); - - return new Client($handler->reveal()); - } - - private function createHandler() : ObjectProphecy - { - $handler = ($this->prophesize)(Handler::class); - - $connection = $this->createConnection(); - $handler->getConnection()->willReturn($connection); - - $packer = $this->createPacker(); - $handler->getPacker()->willReturn($packer); - - $defaultResponse = DummyFactory::createEmptyResponse(); - - if (!$this->requests->count()) { - $handler->handle(Argument::type(Request::class))->willReturn($defaultResponse); - - return $handler; - } - - foreach ($this->requests as $request) { - if (!$responses = $this->requests->getInfo()) { - $handler->handle($request)->willReturn($defaultResponse); - continue; - } - - $handler->handle($request)->willReturn(...$responses) - ->shouldBeCalledTimes(\count($responses)); - } - - return $handler; - } - - private function createConnection() : ObjectProphecy - { - return ($this->prophesize)(Connection::class); - } - - private function createPacker() // : ObjectProphecy - { - if ($this->packer) { - return $this->packer; - //return $this->prophet->prophesize($this->packer); - } - - return ($this->prophesize)(Packer::class); - } -} diff --git a/src/Client/MockClientBuilderOld.php.bak b/src/Client/MockClientBuilderOld.php.bak deleted file mode 100755 index 7b6b7d7..0000000 --- a/src/Client/MockClientBuilderOld.php.bak +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Client; - -use Prophecy\Argument; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophet; -use Tarantool\Client\Client; -use Tarantool\Client\Connection\Connection; -use Tarantool\Client\Handler\Handler; -use Tarantool\Client\Packer\Packer; -use Tarantool\Client\Request\Request; - -final class MockClientBuilderOld -{ - /** @var Prophet */ - private $prophet; - private $requests; - private $packer; - - public function __construct() - { - $this->prophet = new Prophet(); - $this->requests = new \SplObjectStorage(); - } - - public static function buildDefault() : Client - { - return (new self())->build(); - } - - public function shouldHandle($request, ...$responses) : self - { - $this->requests->attach($request, $responses); - - return $this; - } - - public function withPacker($packer) : self - { - //$self = clone $this; - $this->packer = $packer; - - return $this; - } - - public function build() : Client - { - $handler = $this->createHandler(); - - return new Client($handler->reveal()); - } - - public function checkPredictions() : void - { - $this->prophet->checkPredictions(); - } - - private function createHandler() : ObjectProphecy - { - $handler = $this->prophet->prophesize(Handler::class); - - $connection = $this->createConnection(); - $handler->getConnection()->willReturn($connection); - - $packer = $this->createPacker(); - $handler->getPacker()->willReturn($packer); - - $defaultResponse = DummyFactory::createEmptyResponse(); - - if (!$this->requests->count()) { - $handler->handle(Argument::type(Request::class))->willReturn($defaultResponse); - - return $handler; - } - - foreach ($this->requests as $request) { - if (!$responses = $this->requests->getInfo()) { - $handler->handle($request)->willReturn($defaultResponse); - continue; - } - - $handler->handle($request)->willReturn(...$responses) - ->shouldBeCalledTimes(\count($responses)); - } - - return $handler; - } - - private function createConnection() : ObjectProphecy - { - return $this->prophet->prophesize(Connection::class); - } - - private function createPacker() // : ObjectProphecy - { - if ($this->packer) { - return $this->packer; - //return $this->prophet->prophesize($this->packer); - } - - return $this->prophet->prophesize(Packer::class); - } -} From e1b9eb63715b6b50d584683091bb0dd0666758cb Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:44:44 +0200 Subject: [PATCH 11/26] Use no-api for composer VCS repositories to prevent GitHub rate limits --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 641dad4..b5c5609 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "prefer-stable": true, "repositories": [{ "type": "vcs", - "url": "https://github.com/rybakit/phpunit-extras" + "url": "https://github.com/rybakit/phpunit-extras", + "no-api": true }] } From e73bed09289fba4681fc48a6d1b537ac5516f0b0 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sat, 25 Jul 2026 20:47:58 +0200 Subject: [PATCH 12/26] Fix code style issues for php-cs-fixer 3.0 --- .php-cs-fixer.dist.php | 2 +- src/Annotation/AnnotationExtension.php | 6 ++---- .../Requirement/LuaConditionRequirement.php | 2 +- .../Requirement/TarantoolVersionRequirement.php | 2 +- src/Client/IsRequestType.php | 4 ++-- src/Client/TestDoubleClientBuilder.php | 12 +++++++----- tests/Annotation/AnnotationExtensionTest.php | 1 + .../Requirement/LuaConditionRequirementTest.php | 2 +- .../Requirement/TarantoolVersionRequirementTest.php | 2 +- 9 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index da6bde7..992b9a0 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -15,7 +15,7 @@ file that was distributed with this source code. EOF; -return Config::create() +return (new Config()) ->setUsingCache(false) ->setRiskyAllowed(true) ->setRules([ diff --git a/src/Annotation/AnnotationExtension.php b/src/Annotation/AnnotationExtension.php index 834f6ef..e7e2352 100644 --- a/src/Annotation/AnnotationExtension.php +++ b/src/Annotation/AnnotationExtension.php @@ -27,8 +27,6 @@ class AnnotationExtension extends BaseAnnotationExtension /** @var Client|null */ private $client; - - #[\Override] public function bootstrap(\PHPUnit\TextUI\Configuration\Configuration $configuration, \PHPUnit\Runner\Extension\Facade $facade, \PHPUnit\Runner\Extension\ParameterCollection $parameters) : void { @@ -50,7 +48,7 @@ protected function parseParameters(\PHPUnit\Runner\Extension\ParameterCollection }, $parameters, \PHPUnit\Runner\Extension\ParameterCollection::class); $options = $closure ? $closure() : []; - if ($options !== []) { + if ([] !== $options) { $this->clientConfig = $options; } } @@ -98,7 +96,7 @@ private static function resolveEnvValues(string $configValue) : string return $value; } - $errorMessage = sprintf('Environment variable "%s" does not exist', $matches['name']); + $errorMessage = \sprintf('Environment variable "%s" does not exist', $matches['name']); throw new class($errorMessage) extends \RuntimeException implements Exception { }; }, $configValue); } diff --git a/src/Annotation/Requirement/LuaConditionRequirement.php b/src/Annotation/Requirement/LuaConditionRequirement.php index 8482496..976d586 100644 --- a/src/Annotation/Requirement/LuaConditionRequirement.php +++ b/src/Annotation/Requirement/LuaConditionRequirement.php @@ -40,6 +40,6 @@ public function check(string $value) : ?string return null; } - return sprintf('"%s" is not evaluated to true', $value); + return \sprintf('"%s" is not evaluated to true', $value); } } diff --git a/src/Annotation/Requirement/TarantoolVersionRequirement.php b/src/Annotation/Requirement/TarantoolVersionRequirement.php index 6487de6..5d3c2fa 100644 --- a/src/Annotation/Requirement/TarantoolVersionRequirement.php +++ b/src/Annotation/Requirement/TarantoolVersionRequirement.php @@ -45,7 +45,7 @@ public function check(string $value) : ?string return null; } - return sprintf('%s version %s is required', $this->getName(), $value); + return \sprintf('%s version %s is required', $this->getName(), $value); } private function getVersion() : string diff --git a/src/Client/IsRequestType.php b/src/Client/IsRequestType.php index 68bb713..141c510 100644 --- a/src/Client/IsRequestType.php +++ b/src/Client/IsRequestType.php @@ -25,7 +25,7 @@ final class IsRequestType extends Constraint public function __construct(int $requestType) { // needed for backward compatibility with PHPUnit 7 - if (\method_exists(parent::class, '__construct')) { + if (method_exists(parent::class, '__construct')) { parent::__construct(); } @@ -35,7 +35,7 @@ public function __construct(int $requestType) #[\Override] public function toString() : string { - return sprintf('is a "%s" request', strtoupper(RequestTypes::getName($this->requestType))); + return \sprintf('is a "%s" request', strtoupper(RequestTypes::getName($this->requestType))); } #[\Override] diff --git a/src/Client/TestDoubleClientBuilder.php b/src/Client/TestDoubleClientBuilder.php index a067baf..9ec42ef 100755 --- a/src/Client/TestDoubleClientBuilder.php +++ b/src/Client/TestDoubleClientBuilder.php @@ -41,7 +41,7 @@ final class TestDoubleClientBuilder private $packer; /** @var int|null */ - private $shouldBeCalledTimes = null; + private $shouldBeCalledTimes; public function __construct(TestCase $testCase) { @@ -53,6 +53,7 @@ public static function buildDummy() : Client { /** @psalm-suppress InternalMethod */ $self = new self(new class('dummy') extends TestCase {}); + return $self->build(); } @@ -135,16 +136,17 @@ private function createHandler() : MockObject $requests = $this->requests; $handleMocker->with(TestCase::callback(static function ($request) use (&$invocationCount, $requests) { if (!isset($requests[$invocationCount])) { - $invocationCount++; + ++$invocationCount; + return true; } $expected = $requests[$invocationCount++]; - + if ($expected instanceof Constraint) { return (bool) $expected->evaluate($request, '', true); } - - return $expected == $request; + + return $expected === $request; })); } diff --git a/tests/Annotation/AnnotationExtensionTest.php b/tests/Annotation/AnnotationExtensionTest.php index 821bec1..ca5fddc 100755 --- a/tests/Annotation/AnnotationExtensionTest.php +++ b/tests/Annotation/AnnotationExtensionTest.php @@ -23,6 +23,7 @@ private function bootstrapExtension(string $method, array $parameters = []) : An $ext = new AnnotationExtension(); $ext->doParseParameters($parameters); $ext->processTestAttributes(self::class, $method); + return $ext; } diff --git a/tests/Annotation/Requirement/LuaConditionRequirementTest.php b/tests/Annotation/Requirement/LuaConditionRequirementTest.php index 24096a2..b1f1076 100755 --- a/tests/Annotation/Requirement/LuaConditionRequirementTest.php +++ b/tests/Annotation/Requirement/LuaConditionRequirementTest.php @@ -50,7 +50,7 @@ public function testCheckFailsForFalsyExpression() : void ) ->build(); - $errorMessage = sprintf('"%s" is not evaluated to true', $luaExpression); + $errorMessage = \sprintf('"%s" is not evaluated to true', $luaExpression); $requirement = new LuaConditionRequirement($mockClient); self::assertSame($errorMessage, $requirement->check($luaExpression)); diff --git a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php index 14ceb75..262709c 100755 --- a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php @@ -104,7 +104,7 @@ public function testCheckFailsForInvalidConstraints(string $serverVersion, strin ->build(); $requirement = new TarantoolVersionRequirement($mockClient); - $errorMessage = sprintf('Tarantool version %s is required', $constraints); + $errorMessage = \sprintf('Tarantool version %s is required', $constraints); self::assertSame($errorMessage, $requirement->check($constraints)); } From 84c984e09aba179af0b257c7e4848b69e7cc3380 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sun, 26 Jul 2026 11:31:33 +0200 Subject: [PATCH 13/26] Fix strict comparison in mock builder causing tests to fail --- src/Client/TestDoubleClientBuilder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Client/TestDoubleClientBuilder.php b/src/Client/TestDoubleClientBuilder.php index 9ec42ef..5f37445 100755 --- a/src/Client/TestDoubleClientBuilder.php +++ b/src/Client/TestDoubleClientBuilder.php @@ -146,7 +146,9 @@ private function createHandler() : MockObject return (bool) $expected->evaluate($request, '', true); } - return $expected === $request; + $constraint = new \PHPUnit\Framework\Constraint\IsEqual($expected); + + return (bool) $constraint->evaluate($request, '', true); })); } From 042b8c16f71cc04247670136c1b66af912813446 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Sun, 26 Jul 2026 11:43:05 +0200 Subject: [PATCH 14/26] Revert evaluate back to call for box.info --- .../Requirement/TarantoolVersionRequirement.php | 2 +- .../Requirement/TarantoolVersionRequirementTest.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Annotation/Requirement/TarantoolVersionRequirement.php b/src/Annotation/Requirement/TarantoolVersionRequirement.php index 5d3c2fa..dcb5d63 100644 --- a/src/Annotation/Requirement/TarantoolVersionRequirement.php +++ b/src/Annotation/Requirement/TarantoolVersionRequirement.php @@ -54,7 +54,7 @@ private function getVersion() : string return $this->version; } - $version = $this->client->evaluate('return box.info.version')[0]; + $version = $this->client->call('box.info')[0]['version']; if (!\is_string($version)) { throw new \UnexpectedValueException('Tarantool version must be a string'); } diff --git a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php index 262709c..12a2808 100755 --- a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php @@ -15,7 +15,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Tarantool\Client\Request\EvaluateRequest; +use Tarantool\Client\Request\CallRequest; use Tarantool\PhpUnit\Annotation\Requirement\TarantoolVersionRequirement; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -29,8 +29,8 @@ public function testCheckPassesForValidConstraints(string $serverVersion, string { $mockClient = $this->getTestDoubleClientBuilder() ->shouldHandle( - new EvaluateRequest('return box.info.version'), - TestDoubleFactory::createResponseFromData([$serverVersion])) + new CallRequest('box.info'), + TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) ->build(); $requirement = new TarantoolVersionRequirement($mockClient); @@ -99,8 +99,8 @@ public function testCheckFailsForInvalidConstraints(string $serverVersion, strin { $mockClient = $this->getTestDoubleClientBuilder() ->shouldHandle( - new EvaluateRequest('return box.info.version'), - TestDoubleFactory::createResponseFromData([$serverVersion])) + new CallRequest('box.info'), + TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) ->build(); $requirement = new TarantoolVersionRequirement($mockClient); From ee57f20ef50b132719464eca5e9def4ae9b04693 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Mon, 28 Sep 2026 23:58:09 +0200 Subject: [PATCH 15/26] feat: migrate Tarantool integration to attributes --- .github/workflows/qa.yml | 2 +- README.md | 72 ++++++++----------- composer.json | 4 +- phpunit-extension.xml | 6 +- phpunit.xml.dist | 2 +- src/Annotation/Annotations.php | 43 ----------- src/Annotation/Attribute/Lua.php | 39 ---------- src/Annotation/Attribute/Sql.php | 39 ---------- src/Annotation/Processor/LuaProcessor.php | 39 ---------- src/Annotation/Processor/SqlProcessor.php | 39 ---------- .../Requirement/LuaConditionRequirement.php | 45 ------------ .../AttributeExtension.php} | 8 +-- src/Attribute/Attributes.php | 43 +++++++++++ src/Attribute/Lua.php | 22 ++++++ src/Attribute/Processor/LuaProcessor.php | 46 ++++++++++++ src/Attribute/Processor/SqlProcessor.php | 46 ++++++++++++ .../Requirement/LuaConditionRequirement.php | 53 ++++++++++++++ .../TarantoolVersionRequirement.php | 22 ++++-- src/Attribute/RequiresLuaCondition.php | 9 +++ src/Attribute/RequiresTarantoolVersion.php | 9 +++ src/Attribute/Sql.php | 22 ++++++ src/TestCase.php | 4 +- .../AttributeExtension.php} | 6 +- .../AttributeExtensionTest.php} | 18 ++--- .../Processor/LuaProcessorTest.php | 12 +++- .../Processor/SqlProcessorTest.php | 12 +++- .../LuaConditionRequirementTest.php | 19 +++-- .../TarantoolVersionRequirementTest.php | 19 +++-- 28 files changed, 367 insertions(+), 333 deletions(-) delete mode 100644 src/Annotation/Annotations.php delete mode 100644 src/Annotation/Attribute/Lua.php delete mode 100644 src/Annotation/Attribute/Sql.php delete mode 100644 src/Annotation/Processor/LuaProcessor.php delete mode 100644 src/Annotation/Processor/SqlProcessor.php delete mode 100644 src/Annotation/Requirement/LuaConditionRequirement.php rename src/{Annotation/AnnotationExtension.php => Attribute/AttributeExtension.php} (93%) create mode 100644 src/Attribute/Attributes.php create mode 100644 src/Attribute/Lua.php create mode 100644 src/Attribute/Processor/LuaProcessor.php create mode 100644 src/Attribute/Processor/SqlProcessor.php create mode 100644 src/Attribute/Requirement/LuaConditionRequirement.php rename src/{Annotation => Attribute}/Requirement/TarantoolVersionRequirement.php (62%) create mode 100644 src/Attribute/RequiresLuaCondition.php create mode 100644 src/Attribute/RequiresTarantoolVersion.php create mode 100644 src/Attribute/Sql.php rename tests/{Annotation/AnnotationExtension.php => Attribute/AttributeExtension.php} (79%) rename tests/{Annotation/AnnotationExtensionTest.php => Attribute/AttributeExtensionTest.php} (82%) rename tests/{Annotation => Attribute}/Processor/LuaProcessorTest.php (62%) rename tests/{Annotation => Attribute}/Processor/SqlProcessorTest.php (64%) rename tests/{Annotation => Attribute}/Requirement/LuaConditionRequirementTest.php (63%) rename tests/{Annotation => Attribute}/Requirement/TarantoolVersionRequirementTest.php (83%) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 0712db4..0cdad42 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['8.2', '8.3', '8.4'] + php-versions: ['8.2', '8.3', '8.4', '8.5'] runs-on: ${{ matrix.operating-system }} steps: - name: Checkout diff --git a/README.md b/README.md index 064dc69..3571a73 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ It is based on [rybakit/phpunit-extras](https://github.com/rybakit/phpunit-extra ## Table of contents * [Installation](#installation) - * [Annotations](#annotations) + * [Attributes](#attributes) * [Processors](#processors) * [Lua](#lua) * [Sql](#sql) * [Requirements](#requirements) - * [LuaCondition](#luacondition) - * [TarantoolVersion](#tarantoolversion) + * [RequiresLuaCondition](#requiresluacondition) + * [RequiresTarantoolVersion](#requirestarantoolversion) * [Expectations](#expectations) * [Requests](#requests) * [Prepared statements](#prepared-statements) @@ -32,10 +32,10 @@ composer require --dev tarantool/phpunit-extras ``` -## Annotations +## Attributes -Besides the annotations provided by the package `rybakit/phpunit-extras`, the library is shipped -with annotations specific to Tarantool. The easiest way to enable them is by inheriting your test classes +Besides the attributes provided by the package `rybakit/phpunit-extras`, the library is shipped +with attributes specific to Tarantool. The easiest way to enable them is by inheriting your test classes from `Tarantool\PhpUnit\TestCase`: ```php @@ -53,7 +53,7 @@ final class MyTest extends TestCase } ``` -Another option is to register an extension called `AnnotationExtension`: +Another option is to register an extension called `AttributeExtension`: ```xml - + ``` @@ -73,7 +73,7 @@ You can customize the default settings by specifying either a [DSN string](https as extension configuration values: ```xml - + tcp://127.0.0.1:3301/?socket_timeout=10 @@ -81,7 +81,7 @@ as extension configuration values: ``` or ```xml - + @@ -100,14 +100,14 @@ which might be useful if you need to share the same settings with a Tarantool instance file or any other script: ```xml - + tcp://%env(TARANTOOL_HOST)%:%env(TARANTOOL_PORT)% ``` -Once the annotations are configured, you can start using them: +Once the attributes are configured, you can start using them: ### Processors @@ -118,10 +118,10 @@ Allows executing Lua code before running a test. *Example:* ```php -/** - * @lua tube:put('kick_me') - * @lua tube:bury(0) - */ +use Tarantool\PhpUnit\Attribute\Lua; + +#[Lua("tube:put('kick_me')")] +#[Lua('tube:bury(0)')] public function testKickReleasesBuriedTask() : void { // ... @@ -135,11 +135,11 @@ Allows executing SQL statements before running a test (requires Tarantool 2.0+). *Example:* ```php -/** - * @sql DROP TABLE IF EXISTS foobar - * @sql CREATE TABLE foobar (id INTEGER PRIMARY KEY, name VARCHAR(50)) - * @sql INSERT INTO foobar VALUES (1, 'A'), (2, 'B') - */ +use Tarantool\PhpUnit\Attribute\Sql; + +#[Sql('DROP TABLE IF EXISTS foobar')] +#[Sql('CREATE TABLE foobar (id INTEGER PRIMARY KEY, name VARCHAR(50))')] +#[Sql("INSERT INTO foobar VALUES (1, 'A'), (2, 'B')")] public function testExecuteQueryFetchesAllRows() : void { // ... @@ -151,50 +151,38 @@ public function testExecuteQueryFetchesAllRows() : void Requirements allow skipping tests based on preconditions. -#### LuaCondition - -*Format:* - -``` -@requires luaCondition -``` +#### RequiresLuaCondition where `` is an arbitrary lua expression that should be evaluated to a Boolean value. *Example:* ```php -/** - * @requires luaCondition box.session.user() ~= 'guest' - */ +use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; + +#[RequiresLuaCondition("box.session.user() ~= 'guest'")] public function testChangeUserPassword() : void { // ... } ``` -#### TarantoolVersion - -*Format:* - -``` -@requires Tarantool -``` +#### RequiresTarantoolVersion where `` is a composer-like version constraint. For details on supported formats, please see the Composer [documentation](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints). *Example:* ```php -/** - * @requires Tarantool ^2.3.2 - */ +use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; + +#[RequiresTarantoolVersion('^2.3.2')] public function testPrepareCreatesPreparedStatement() : void { // ... } ``` -> *If you're interested in how to create and register your own annotations and requirements, +> *If you're interested in how to create and register your own attributes and requirements, > please refer to the `rybakit/phpunit-extras` [README](https://github.com/rybakit/phpunit-extras).* diff --git a/composer.json b/composer.json index b5c5609..bb5dd76 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "tarantool/phpunit-extras", "description": "A collection of helpers for PHPUnit to ease testing Tarantool libraries.", - "keywords": ["tarantool", "phpunit", "annotations", "assertions", "expectations", "extensions", "mocking"], + "keywords": ["tarantool", "phpunit", "attributes", "assertions", "expectations", "extensions", "mocking"], "type": "library", "license": "MIT", "authors": [ @@ -12,10 +12,8 @@ ], "require": { "php": "^8.2", - "composer/package-versions-deprecated": "^1.11", "composer/semver": "^3.3", "rybakit/phpunit-extras": "dev-feature/php82-phpunit10-attributes-migration", - "symfony/expression-language": "^7.0", "tarantool/client": "^0.10" }, "require-dev": { diff --git a/phpunit-extension.xml b/phpunit-extension.xml index f343a3e..95bf519 100644 --- a/phpunit-extension.xml +++ b/phpunit-extension.xml @@ -7,8 +7,8 @@ - - tests/Annotation/AnnotationExtensionTest.php + + tests/Attribute/AttributeExtensionTest.php @@ -17,7 +17,7 @@ - + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c6e4b88..2f26844 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -9,7 +9,7 @@ tests - tests/Annotation/AnnotationExtensionTest.php + tests/Attribute/AttributeExtensionTest.php diff --git a/src/Annotation/Annotations.php b/src/Annotation/Annotations.php deleted file mode 100644 index 93db4a6..0000000 --- a/src/Annotation/Annotations.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Annotation; - -use PHPUnitExtras\Annotation\AnnotationProcessorBuilder; -use PHPUnitExtras\Annotation\Annotations as BaseAnnotations; -use Tarantool\Client\Client; -use Tarantool\PhpUnit\Annotation\Processor\LuaProcessor; -use Tarantool\PhpUnit\Annotation\Processor\SqlProcessor; -use Tarantool\PhpUnit\Annotation\Requirement\LuaConditionRequirement; -use Tarantool\PhpUnit\Annotation\Requirement\TarantoolVersionRequirement; - -trait Annotations -{ - use BaseAnnotations { - BaseAnnotations::createAnnotationProcessorBuilder as createBaseAnnotationProcessorBuilder; - } - - protected function createAnnotationProcessorBuilder() : AnnotationProcessorBuilder - { - $client = $this->getClient(); - - return $this->createBaseAnnotationProcessorBuilder() - ->addProcessor(new LuaProcessor($client)) - ->addProcessor(new SqlProcessor($client)) - ->addRequirement(new LuaConditionRequirement($client)) - ->addRequirement(new TarantoolVersionRequirement($client)) - ; - } - - abstract protected function getClient() : Client; -} diff --git a/src/Annotation/Attribute/Lua.php b/src/Annotation/Attribute/Lua.php deleted file mode 100644 index 119b002..0000000 --- a/src/Annotation/Attribute/Lua.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Annotation\Attribute; - -use PHPUnitExtras\Annotation\Attribute\AnnotationAttribute; - -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -final class Lua implements AnnotationAttribute -{ - private $code; - - public function __construct(string $code) - { - $this->code = $code; - } - - #[\Override] - public function getName() : string - { - return 'lua'; - } - - #[\Override] - public function getValue() : string - { - return $this->code; - } -} diff --git a/src/Annotation/Attribute/Sql.php b/src/Annotation/Attribute/Sql.php deleted file mode 100644 index 45370d9..0000000 --- a/src/Annotation/Attribute/Sql.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Annotation\Attribute; - -use PHPUnitExtras\Annotation\Attribute\AnnotationAttribute; - -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -final class Sql implements AnnotationAttribute -{ - private $code; - - public function __construct(string $code) - { - $this->code = $code; - } - - #[\Override] - public function getName() : string - { - return 'sql'; - } - - #[\Override] - public function getValue() : string - { - return $this->code; - } -} diff --git a/src/Annotation/Processor/LuaProcessor.php b/src/Annotation/Processor/LuaProcessor.php deleted file mode 100644 index d76eca9..0000000 --- a/src/Annotation/Processor/LuaProcessor.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Annotation\Processor; - -use PHPUnitExtras\Annotation\Processor\Processor; -use Tarantool\Client\Client; - -final class LuaProcessor implements Processor -{ - private $client; - - public function __construct(Client $client) - { - $this->client = $client; - } - - #[\Override] - public function getName() : string - { - return 'lua'; - } - - #[\Override] - public function process(string $value) : void - { - $this->client->evaluate($value); - } -} diff --git a/src/Annotation/Processor/SqlProcessor.php b/src/Annotation/Processor/SqlProcessor.php deleted file mode 100644 index 3c8261e..0000000 --- a/src/Annotation/Processor/SqlProcessor.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Annotation\Processor; - -use PHPUnitExtras\Annotation\Processor\Processor; -use Tarantool\Client\Client; - -final class SqlProcessor implements Processor -{ - private $client; - - public function __construct(Client $client) - { - $this->client = $client; - } - - #[\Override] - public function getName() : string - { - return 'sql'; - } - - #[\Override] - public function process(string $value) : void - { - $this->client->executeUpdate($value); - } -} diff --git a/src/Annotation/Requirement/LuaConditionRequirement.php b/src/Annotation/Requirement/LuaConditionRequirement.php deleted file mode 100644 index 976d586..0000000 --- a/src/Annotation/Requirement/LuaConditionRequirement.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tarantool\PhpUnit\Annotation\Requirement; - -use PHPUnitExtras\Annotation\Requirement\Requirement; -use Tarantool\Client\Client; - -final class LuaConditionRequirement implements Requirement -{ - private $client; - - public function __construct(Client $client) - { - $this->client = $client; - } - - #[\Override] - public function getName() : string - { - return 'luaCondition'; - } - - #[\Override] - public function check(string $value) : ?string - { - [$result] = $this->client->evaluate("return ($value)"); - - if ($result) { - return null; - } - - return \sprintf('"%s" is not evaluated to true', $value); - } -} diff --git a/src/Annotation/AnnotationExtension.php b/src/Attribute/AttributeExtension.php similarity index 93% rename from src/Annotation/AnnotationExtension.php rename to src/Attribute/AttributeExtension.php index e7e2352..1bd1795 100644 --- a/src/Annotation/AnnotationExtension.php +++ b/src/Attribute/AttributeExtension.php @@ -11,15 +11,15 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Annotation; +namespace Tarantool\PhpUnit\Attribute; use PHPUnit\Exception; -use PHPUnitExtras\Annotation\AnnotationExtension as BaseAnnotationExtension; +use PHPUnitExtras\Attribute\AttributeExtension as BaseAttributeExtension; use Tarantool\Client\Client; -class AnnotationExtension extends BaseAnnotationExtension +class AttributeExtension extends BaseAttributeExtension { - use Annotations; + use Attributes; /** @var array|string */ private $clientConfig = 'tcp://127.0.0.1:3301'; diff --git a/src/Attribute/Attributes.php b/src/Attribute/Attributes.php new file mode 100644 index 0000000..1b0c098 --- /dev/null +++ b/src/Attribute/Attributes.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Attribute; + +use PHPUnitExtras\Attribute\AttributeProcessorBuilder; +use PHPUnitExtras\Attribute\Attributes as BaseAttributes; +use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\Processor\LuaProcessor; +use Tarantool\PhpUnit\Attribute\Processor\SqlProcessor; +use Tarantool\PhpUnit\Attribute\Requirement\LuaConditionRequirement; +use Tarantool\PhpUnit\Attribute\Requirement\TarantoolVersionRequirement; + +trait Attributes +{ + use BaseAttributes { + BaseAttributes::createAttributeProcessorBuilder as createBaseAttributeProcessorBuilder; + } + + protected function createAttributeProcessorBuilder() : AttributeProcessorBuilder + { + $client = $this->getClient(); + + return $this->createBaseAttributeProcessorBuilder() + ->addProcessor(new LuaProcessor($client)) + ->addProcessor(new SqlProcessor($client)) + ->addRequirement(new LuaConditionRequirement($client)) + ->addRequirement(new TarantoolVersionRequirement($client)) + ; + } + + abstract protected function getClient() : Client; +} diff --git a/src/Attribute/Lua.php b/src/Attribute/Lua.php new file mode 100644 index 0000000..79fe1d3 --- /dev/null +++ b/src/Attribute/Lua.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Attribute; + +use PHPUnitExtras\Attribute\ProcessableAttribute; + +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Lua implements ProcessableAttribute +{ + public function __construct(public readonly string $code) { } +} diff --git a/src/Attribute/Processor/LuaProcessor.php b/src/Attribute/Processor/LuaProcessor.php new file mode 100644 index 0000000..ae28c6c --- /dev/null +++ b/src/Attribute/Processor/LuaProcessor.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Attribute\Processor; + +use PHPUnitExtras\Attribute\Processor\Processor; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Target; +use Tarantool\PhpUnit\Attribute\Lua; +use Tarantool\Client\Client; + +final class LuaProcessor implements Processor +{ + private Client $client; + + public function __construct(Client $client) + { + $this->client = $client; + } + + #[\Override] + public function getAttributeClasses() : array + { + return [Lua::class]; + } + + #[\Override] + public function process(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : void + { + if (!$attribute instanceof Lua) { + throw new \InvalidArgumentException('LuaProcessor only handles Lua attributes'); + } + $this->client->evaluate($placeholderResolver->resolve($attribute->code, $target)); + } +} diff --git a/src/Attribute/Processor/SqlProcessor.php b/src/Attribute/Processor/SqlProcessor.php new file mode 100644 index 0000000..119e9eb --- /dev/null +++ b/src/Attribute/Processor/SqlProcessor.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Attribute\Processor; + +use PHPUnitExtras\Attribute\Processor\Processor; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Target; +use Tarantool\PhpUnit\Attribute\Sql; +use Tarantool\Client\Client; + +final class SqlProcessor implements Processor +{ + private Client $client; + + public function __construct(Client $client) + { + $this->client = $client; + } + + #[\Override] + public function getAttributeClasses() : array + { + return [Sql::class]; + } + + #[\Override] + public function process(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : void + { + if (!$attribute instanceof Sql) { + throw new \InvalidArgumentException('SqlProcessor only handles Sql attributes'); + } + $this->client->executeUpdate($placeholderResolver->resolve($attribute->code, $target)); + } +} diff --git a/src/Attribute/Requirement/LuaConditionRequirement.php b/src/Attribute/Requirement/LuaConditionRequirement.php new file mode 100644 index 0000000..ef36505 --- /dev/null +++ b/src/Attribute/Requirement/LuaConditionRequirement.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Attribute\Requirement; + +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Target; +use PHPUnitExtras\Attribute\Requirement\Requirement; +use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; +use Tarantool\Client\Client; + +final class LuaConditionRequirement implements Requirement +{ + private Client $client; + + public function __construct(Client $client) + { + $this->client = $client; + } + + #[\Override] + public function getAttributeClass() : string + { + return RequiresLuaCondition::class; + } + + #[\Override] + public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string + { + if (!$attribute instanceof RequiresLuaCondition) { + throw new \InvalidArgumentException('LuaConditionRequirement only handles RequiresLuaCondition attributes'); + } + $condition = $placeholderResolver->resolve($attribute->condition, $target); + [$result] = $this->client->evaluate("return ($condition)"); + + if ($result) { + return null; + } + + return \sprintf('"%s" is not evaluated to true', $condition); + } +} diff --git a/src/Annotation/Requirement/TarantoolVersionRequirement.php b/src/Attribute/Requirement/TarantoolVersionRequirement.php similarity index 62% rename from src/Annotation/Requirement/TarantoolVersionRequirement.php rename to src/Attribute/Requirement/TarantoolVersionRequirement.php index dcb5d63..9903b3a 100644 --- a/src/Annotation/Requirement/TarantoolVersionRequirement.php +++ b/src/Attribute/Requirement/TarantoolVersionRequirement.php @@ -11,15 +11,19 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Annotation\Requirement; +namespace Tarantool\PhpUnit\Attribute\Requirement; use Composer\Semver\Semver; -use PHPUnitExtras\Annotation\Requirement\Requirement; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Target; +use PHPUnitExtras\Attribute\Requirement\Requirement; +use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; use Tarantool\Client\Client; final class TarantoolVersionRequirement implements Requirement { - private $client; + private Client $client; /** @var string|null */ private $version; @@ -30,14 +34,18 @@ public function __construct(Client $client) } #[\Override] - public function getName() : string + public function getAttributeClass() : string { - return 'Tarantool'; + return RequiresTarantoolVersion::class; } #[\Override] - public function check(string $value) : ?string + public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string { + if (!$attribute instanceof RequiresTarantoolVersion) { + throw new \InvalidArgumentException('TarantoolVersionRequirement only handles RequiresTarantoolVersion attributes'); + } + $value = $placeholderResolver->resolve($attribute->constraint, $target); // Replace dash with dot. $constraints = (string) preg_replace('/(\d+\.\d+\.\d+)-(\d+)/', '$1.$2', $value); @@ -45,7 +53,7 @@ public function check(string $value) : ?string return null; } - return \sprintf('%s version %s is required', $this->getName(), $value); + return \sprintf('Tarantool version %s is required', $value); } private function getVersion() : string diff --git a/src/Attribute/RequiresLuaCondition.php b/src/Attribute/RequiresLuaCondition.php new file mode 100644 index 0000000..37ff342 --- /dev/null +++ b/src/Attribute/RequiresLuaCondition.php @@ -0,0 +1,9 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Tarantool\PhpUnit\Attribute; + +use PHPUnitExtras\Attribute\ProcessableAttribute; + +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Sql implements ProcessableAttribute +{ + public function __construct(public readonly string $code) { } +} diff --git a/src/TestCase.php b/src/TestCase.php index 941b81d..41bc960 100644 --- a/src/TestCase.php +++ b/src/TestCase.php @@ -14,13 +14,13 @@ namespace Tarantool\PhpUnit; use PHPUnitExtras\TestCase as BaseTestCase; -use Tarantool\PhpUnit\Annotation\Annotations; +use Tarantool\PhpUnit\Attribute\Attributes; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Expectation\Expectations; abstract class TestCase extends BaseTestCase { - use Annotations; + use Attributes; use Expectations; use TestDoubleClient; } diff --git a/tests/Annotation/AnnotationExtension.php b/tests/Attribute/AttributeExtension.php similarity index 79% rename from tests/Annotation/AnnotationExtension.php rename to tests/Attribute/AttributeExtension.php index fe084e6..b39b976 100755 --- a/tests/Annotation/AnnotationExtension.php +++ b/tests/Attribute/AttributeExtension.php @@ -11,13 +11,13 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation; +namespace Tarantool\PhpUnit\Tests\Attribute; use Tarantool\Client\Client; -use Tarantool\PhpUnit\Annotation\AnnotationExtension as BaseAnnotationExtension; +use Tarantool\PhpUnit\Attribute\AttributeExtension as BaseAttributeExtension; use Tarantool\PhpUnit\Client\TestDoubleClientBuilder; -final class AnnotationExtension extends BaseAnnotationExtension +final class AttributeExtension extends BaseAttributeExtension { public $resolvedDnsOrOptions; diff --git a/tests/Annotation/AnnotationExtensionTest.php b/tests/Attribute/AttributeExtensionTest.php similarity index 82% rename from tests/Annotation/AnnotationExtensionTest.php rename to tests/Attribute/AttributeExtensionTest.php index ca5fddc..ef31179 100755 --- a/tests/Annotation/AnnotationExtensionTest.php +++ b/tests/Attribute/AttributeExtensionTest.php @@ -11,23 +11,23 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation; +namespace Tarantool\PhpUnit\Tests\Attribute; use PHPUnit\Framework\TestCase; -use Tarantool\PhpUnit\Annotation\Attribute\Lua; +use Tarantool\PhpUnit\Attribute\Lua; -final class AnnotationExtensionTest extends TestCase +final class AttributeExtensionTest extends TestCase { - private function bootstrapExtension(string $method, array $parameters = []) : AnnotationExtension + private function bootstrapExtension(string $method, array $parameters = []) : AttributeExtension { - $ext = new AnnotationExtension(); + $ext = new AttributeExtension(); $ext->doParseParameters($parameters); $ext->processTestAttributes(self::class, $method); return $ext; } - #[Lua('dummy_code_to_trigger_annotation_processing = true')] + #[Lua('dummy_code_to_trigger_attribute_processing = true')] public function testConstructorUsesDefaultDsn() : void { $ext = $this->bootstrapExtension(__FUNCTION__); @@ -35,7 +35,7 @@ public function testConstructorUsesDefaultDsn() : void self::assertSame('tcp://127.0.0.1:3301', $ext->resolvedDnsOrOptions); } - #[Lua('dummy_code_to_trigger_annotation_processing = true')] + #[Lua('dummy_code_to_trigger_attribute_processing = true')] public function testConstructorUsesCustomDsn() : void { $dsn = 'tcp://tnt_foobar:3302'; @@ -44,7 +44,7 @@ public function testConstructorUsesCustomDsn() : void self::assertSame($dsn, $ext->resolvedDnsOrOptions); } - #[Lua('dummy_code_to_trigger_annotation_processing = true')] + #[Lua('dummy_code_to_trigger_attribute_processing = true')] public function testGetClientConfigNormalizesDsnString() : void { $hostname = 'tnt_foobar'; @@ -58,7 +58,7 @@ public function testGetClientConfigNormalizesDsnString() : void self::assertSame("tcp://$hostname:$port", $ext->resolvedDnsOrOptions); } - #[Lua('dummy_code_to_trigger_annotation_processing = true')] + #[Lua('dummy_code_to_trigger_attribute_processing = true')] public function testGetClientConfigNormalizesOptionArray() : void { $hostname = 'tnt_foobar'; diff --git a/tests/Annotation/Processor/LuaProcessorTest.php b/tests/Attribute/Processor/LuaProcessorTest.php similarity index 62% rename from tests/Annotation/Processor/LuaProcessorTest.php rename to tests/Attribute/Processor/LuaProcessorTest.php index 8fe890f..ff29453 100755 --- a/tests/Annotation/Processor/LuaProcessorTest.php +++ b/tests/Attribute/Processor/LuaProcessorTest.php @@ -11,11 +11,14 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation\Processor; +namespace Tarantool\PhpUnit\Tests\Attribute\Processor; use PHPUnit\Framework\TestCase; use Tarantool\Client\Request\EvaluateRequest; -use Tarantool\PhpUnit\Annotation\Processor\LuaProcessor; +use Tarantool\PhpUnit\Attribute\Processor\LuaProcessor; +use Tarantool\PhpUnit\Attribute\Lua; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -35,6 +38,9 @@ public function testProcessProcessesLuaExpression() : void ->build(); $processor = new LuaProcessor($mockClient); - $processor->process($luaExpression); + $processor->process(new Lua($luaExpression), new Target(self::class), new class implements PlaceholderResolver { + public function getName() : string { return 'identity'; } + public function resolve(string $value, Target $target) : string { return $value; } + }); } } diff --git a/tests/Annotation/Processor/SqlProcessorTest.php b/tests/Attribute/Processor/SqlProcessorTest.php similarity index 64% rename from tests/Annotation/Processor/SqlProcessorTest.php rename to tests/Attribute/Processor/SqlProcessorTest.php index 6c5a285..bc07b4a 100755 --- a/tests/Annotation/Processor/SqlProcessorTest.php +++ b/tests/Attribute/Processor/SqlProcessorTest.php @@ -11,12 +11,15 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation\Processor; +namespace Tarantool\PhpUnit\Tests\Attribute\Processor; use PHPUnit\Framework\TestCase; use Tarantool\Client\Keys; use Tarantool\Client\Request\ExecuteRequest; -use Tarantool\PhpUnit\Annotation\Processor\SqlProcessor; +use Tarantool\PhpUnit\Attribute\Processor\SqlProcessor; +use Tarantool\PhpUnit\Attribute\Sql; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -36,6 +39,9 @@ public function testProcessProcessesSqlStatement() : void ->build(); $processor = new SqlProcessor($mockClient); - $processor->process($sqlStatement); + $processor->process(new Sql($sqlStatement), new Target(self::class), new class implements PlaceholderResolver { + public function getName() : string { return 'identity'; } + public function resolve(string $value, Target $target) : string { return $value; } + }); } } diff --git a/tests/Annotation/Requirement/LuaConditionRequirementTest.php b/tests/Attribute/Requirement/LuaConditionRequirementTest.php similarity index 63% rename from tests/Annotation/Requirement/LuaConditionRequirementTest.php rename to tests/Attribute/Requirement/LuaConditionRequirementTest.php index b1f1076..ea17a69 100755 --- a/tests/Annotation/Requirement/LuaConditionRequirementTest.php +++ b/tests/Attribute/Requirement/LuaConditionRequirementTest.php @@ -11,11 +11,14 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation\Requirement; +namespace Tarantool\PhpUnit\Tests\Attribute\Requirement; use PHPUnit\Framework\TestCase; use Tarantool\Client\Request\EvaluateRequest; -use Tarantool\PhpUnit\Annotation\Requirement\LuaConditionRequirement; +use Tarantool\PhpUnit\Attribute\Requirement\LuaConditionRequirement; +use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -36,7 +39,7 @@ public function testCheckPassesForTruthyExpression() : void $requirement = new LuaConditionRequirement($mockClient); - self::assertNull($requirement->check($luaExpression)); + self::assertNull($requirement->check(new RequiresLuaCondition($luaExpression), new Target(self::class), self::resolver())); } public function testCheckFailsForFalsyExpression() : void @@ -53,6 +56,14 @@ public function testCheckFailsForFalsyExpression() : void $errorMessage = \sprintf('"%s" is not evaluated to true', $luaExpression); $requirement = new LuaConditionRequirement($mockClient); - self::assertSame($errorMessage, $requirement->check($luaExpression)); + self::assertSame($errorMessage, $requirement->check(new RequiresLuaCondition($luaExpression), new Target(self::class), self::resolver())); + } + + private static function resolver() : PlaceholderResolver + { + return new class implements PlaceholderResolver { + public function getName() : string { return 'identity'; } + public function resolve(string $value, Target $target) : string { return $value; } + }; } } diff --git a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php b/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php similarity index 83% rename from tests/Annotation/Requirement/TarantoolVersionRequirementTest.php rename to tests/Attribute/Requirement/TarantoolVersionRequirementTest.php index 12a2808..a668b31 100755 --- a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php @@ -11,12 +11,15 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation\Requirement; +namespace Tarantool\PhpUnit\Tests\Attribute\Requirement; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Tarantool\Client\Request\CallRequest; -use Tarantool\PhpUnit\Annotation\Requirement\TarantoolVersionRequirement; +use Tarantool\PhpUnit\Attribute\Requirement\TarantoolVersionRequirement; +use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -35,7 +38,7 @@ public function testCheckPassesForValidConstraints(string $serverVersion, string $requirement = new TarantoolVersionRequirement($mockClient); - self::assertNull($requirement->check($constraints)); + self::assertNull($requirement->check(new RequiresTarantoolVersion($constraints), new Target(self::class), self::resolver())); } public static function provideCheckPassesForValidConstraintsData() : iterable @@ -106,7 +109,7 @@ public function testCheckFailsForInvalidConstraints(string $serverVersion, strin $requirement = new TarantoolVersionRequirement($mockClient); $errorMessage = \sprintf('Tarantool version %s is required', $constraints); - self::assertSame($errorMessage, $requirement->check($constraints)); + self::assertSame($errorMessage, $requirement->check(new RequiresTarantoolVersion($constraints), new Target(self::class), self::resolver())); } public static function provideCheckFailsForInvalidConstraintsData() : iterable @@ -145,4 +148,12 @@ public static function provideCheckFailsForInvalidConstraintsData() : iterable [$v2_3_1_3, '< 2'], ]; } + + private static function resolver() : PlaceholderResolver + { + return new class implements PlaceholderResolver { + public function getName() : string { return 'identity'; } + public function resolve(string $value, Target $target) : string { return $value; } + }; + } } From b768a3965e68ceb0c2986f29d073cf90767aeb8c Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 00:12:10 +0200 Subject: [PATCH 16/26] style: apply php-cs-fixer formatting --- src/Attribute/Lua.php | 4 +++- src/Attribute/Processor/LuaProcessor.php | 4 ++-- src/Attribute/Processor/SqlProcessor.php | 4 ++-- .../Requirement/LuaConditionRequirement.php | 4 ++-- .../Requirement/TarantoolVersionRequirement.php | 4 ++-- src/Attribute/RequiresLuaCondition.php | 17 ++++++++++++++++- src/Attribute/RequiresTarantoolVersion.php | 17 ++++++++++++++++- src/Attribute/Sql.php | 4 +++- tests/Attribute/Processor/LuaProcessorTest.php | 17 ++++++++++++----- tests/Attribute/Processor/SqlProcessorTest.php | 15 +++++++++++---- .../Requirement/LuaConditionRequirementTest.php | 15 +++++++++++---- .../TarantoolVersionRequirementTest.php | 15 +++++++++++---- 12 files changed, 91 insertions(+), 29 deletions(-) diff --git a/src/Attribute/Lua.php b/src/Attribute/Lua.php index 79fe1d3..39a97b6 100644 --- a/src/Attribute/Lua.php +++ b/src/Attribute/Lua.php @@ -18,5 +18,7 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] final class Lua implements ProcessableAttribute { - public function __construct(public readonly string $code) { } + public function __construct(public readonly string $code) + { + } } diff --git a/src/Attribute/Processor/LuaProcessor.php b/src/Attribute/Processor/LuaProcessor.php index ae28c6c..11d1694 100644 --- a/src/Attribute/Processor/LuaProcessor.php +++ b/src/Attribute/Processor/LuaProcessor.php @@ -13,12 +13,12 @@ namespace Tarantool\PhpUnit\Attribute\Processor; -use PHPUnitExtras\Attribute\Processor\Processor; use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Processor\Processor; use PHPUnitExtras\Attribute\Target; -use Tarantool\PhpUnit\Attribute\Lua; use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\Lua; final class LuaProcessor implements Processor { diff --git a/src/Attribute/Processor/SqlProcessor.php b/src/Attribute/Processor/SqlProcessor.php index 119e9eb..029938a 100644 --- a/src/Attribute/Processor/SqlProcessor.php +++ b/src/Attribute/Processor/SqlProcessor.php @@ -13,12 +13,12 @@ namespace Tarantool\PhpUnit\Attribute\Processor; -use PHPUnitExtras\Attribute\Processor\Processor; use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Processor\Processor; use PHPUnitExtras\Attribute\Target; -use Tarantool\PhpUnit\Attribute\Sql; use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\Sql; final class SqlProcessor implements Processor { diff --git a/src/Attribute/Requirement/LuaConditionRequirement.php b/src/Attribute/Requirement/LuaConditionRequirement.php index ef36505..df01772 100644 --- a/src/Attribute/Requirement/LuaConditionRequirement.php +++ b/src/Attribute/Requirement/LuaConditionRequirement.php @@ -15,10 +15,10 @@ use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; use PHPUnitExtras\Attribute\ProcessableAttribute; -use PHPUnitExtras\Attribute\Target; use PHPUnitExtras\Attribute\Requirement\Requirement; -use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; +use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; final class LuaConditionRequirement implements Requirement { diff --git a/src/Attribute/Requirement/TarantoolVersionRequirement.php b/src/Attribute/Requirement/TarantoolVersionRequirement.php index 9903b3a..98ed13b 100644 --- a/src/Attribute/Requirement/TarantoolVersionRequirement.php +++ b/src/Attribute/Requirement/TarantoolVersionRequirement.php @@ -16,10 +16,10 @@ use Composer\Semver\Semver; use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; use PHPUnitExtras\Attribute\ProcessableAttribute; -use PHPUnitExtras\Attribute\Target; use PHPUnitExtras\Attribute\Requirement\Requirement; -use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; +use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; final class TarantoolVersionRequirement implements Requirement { diff --git a/src/Attribute/RequiresLuaCondition.php b/src/Attribute/RequiresLuaCondition.php index 37ff342..8a9c793 100644 --- a/src/Attribute/RequiresLuaCondition.php +++ b/src/Attribute/RequiresLuaCondition.php @@ -1,9 +1,24 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + declare(strict_types=1); + namespace Tarantool\PhpUnit\Attribute; + use PHPUnitExtras\Attribute\ProcessableAttribute; + #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] final class RequiresLuaCondition implements ProcessableAttribute { - public function __construct(public readonly string $condition) { } + public function __construct(public readonly string $condition) + { + } } diff --git a/src/Attribute/RequiresTarantoolVersion.php b/src/Attribute/RequiresTarantoolVersion.php index 23a0366..0ffd97a 100644 --- a/src/Attribute/RequiresTarantoolVersion.php +++ b/src/Attribute/RequiresTarantoolVersion.php @@ -1,9 +1,24 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + declare(strict_types=1); + namespace Tarantool\PhpUnit\Attribute; + use PHPUnitExtras\Attribute\ProcessableAttribute; + #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] final class RequiresTarantoolVersion implements ProcessableAttribute { - public function __construct(public readonly string $constraint) { } + public function __construct(public readonly string $constraint) + { + } } diff --git a/src/Attribute/Sql.php b/src/Attribute/Sql.php index f6f5ccf..d51be8c 100644 --- a/src/Attribute/Sql.php +++ b/src/Attribute/Sql.php @@ -18,5 +18,7 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] final class Sql implements ProcessableAttribute { - public function __construct(public readonly string $code) { } + public function __construct(public readonly string $code) + { + } } diff --git a/tests/Attribute/Processor/LuaProcessorTest.php b/tests/Attribute/Processor/LuaProcessorTest.php index ff29453..af61d98 100755 --- a/tests/Attribute/Processor/LuaProcessorTest.php +++ b/tests/Attribute/Processor/LuaProcessorTest.php @@ -14,11 +14,11 @@ namespace Tarantool\PhpUnit\Tests\Attribute\Processor; use PHPUnit\Framework\TestCase; -use Tarantool\Client\Request\EvaluateRequest; -use Tarantool\PhpUnit\Attribute\Processor\LuaProcessor; -use Tarantool\PhpUnit\Attribute\Lua; use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; use PHPUnitExtras\Attribute\Target; +use Tarantool\Client\Request\EvaluateRequest; +use Tarantool\PhpUnit\Attribute\Lua; +use Tarantool\PhpUnit\Attribute\Processor\LuaProcessor; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -39,8 +39,15 @@ public function testProcessProcessesLuaExpression() : void $processor = new LuaProcessor($mockClient); $processor->process(new Lua($luaExpression), new Target(self::class), new class implements PlaceholderResolver { - public function getName() : string { return 'identity'; } - public function resolve(string $value, Target $target) : string { return $value; } + public function getName() : string + { + return 'identity'; + } + + public function resolve(string $value, Target $target) : string + { + return $value; + } }); } } diff --git a/tests/Attribute/Processor/SqlProcessorTest.php b/tests/Attribute/Processor/SqlProcessorTest.php index bc07b4a..834035a 100755 --- a/tests/Attribute/Processor/SqlProcessorTest.php +++ b/tests/Attribute/Processor/SqlProcessorTest.php @@ -14,12 +14,12 @@ namespace Tarantool\PhpUnit\Tests\Attribute\Processor; use PHPUnit\Framework\TestCase; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Keys; use Tarantool\Client\Request\ExecuteRequest; use Tarantool\PhpUnit\Attribute\Processor\SqlProcessor; use Tarantool\PhpUnit\Attribute\Sql; -use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; -use PHPUnitExtras\Attribute\Target; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -40,8 +40,15 @@ public function testProcessProcessesSqlStatement() : void $processor = new SqlProcessor($mockClient); $processor->process(new Sql($sqlStatement), new Target(self::class), new class implements PlaceholderResolver { - public function getName() : string { return 'identity'; } - public function resolve(string $value, Target $target) : string { return $value; } + public function getName() : string + { + return 'identity'; + } + + public function resolve(string $value, Target $target) : string + { + return $value; + } }); } } diff --git a/tests/Attribute/Requirement/LuaConditionRequirementTest.php b/tests/Attribute/Requirement/LuaConditionRequirementTest.php index ea17a69..59f1ba3 100755 --- a/tests/Attribute/Requirement/LuaConditionRequirementTest.php +++ b/tests/Attribute/Requirement/LuaConditionRequirementTest.php @@ -14,11 +14,11 @@ namespace Tarantool\PhpUnit\Tests\Attribute\Requirement; use PHPUnit\Framework\TestCase; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Request\EvaluateRequest; use Tarantool\PhpUnit\Attribute\Requirement\LuaConditionRequirement; use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; -use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; -use PHPUnitExtras\Attribute\Target; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -62,8 +62,15 @@ public function testCheckFailsForFalsyExpression() : void private static function resolver() : PlaceholderResolver { return new class implements PlaceholderResolver { - public function getName() : string { return 'identity'; } - public function resolve(string $value, Target $target) : string { return $value; } + public function getName() : string + { + return 'identity'; + } + + public function resolve(string $value, Target $target) : string + { + return $value; + } }; } } diff --git a/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php b/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php index a668b31..46fb880 100755 --- a/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php @@ -15,11 +15,11 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Request\CallRequest; use Tarantool\PhpUnit\Attribute\Requirement\TarantoolVersionRequirement; use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; -use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; -use PHPUnitExtras\Attribute\Target; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -152,8 +152,15 @@ public static function provideCheckFailsForInvalidConstraintsData() : iterable private static function resolver() : PlaceholderResolver { return new class implements PlaceholderResolver { - public function getName() : string { return 'identity'; } - public function resolve(string $value, Target $target) : string { return $value; } + public function getName() : string + { + return 'identity'; + } + + public function resolve(string $value, Target $target) : string + { + return $value; + } }; } } From a5771b5368b6340084b5a136d98868cf31951991 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 00:24:43 +0200 Subject: [PATCH 17/26] fix: require expression language for expectations --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index bb5dd76..f210eaa 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "php": "^8.2", "composer/semver": "^3.3", "rybakit/phpunit-extras": "dev-feature/php82-phpunit10-attributes-migration", + "symfony/expression-language": "^7.0", "tarantool/client": "^0.10" }, "require-dev": { From 75fc1db13f2079bfb9ddf11e9669efe779213eec Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 00:30:57 +0200 Subject: [PATCH 18/26] fix: clear CI deprecations and Psalm findings --- .github/workflows/qa.yml | 24 +++++++++---------- src/Attribute/AttributeExtension.php | 1 + .../TarantoolVersionRequirement.php | 2 +- src/Client/IsRequestType.php | 7 +----- src/Client/TestDoubleClientBuilder.php | 4 +--- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 0cdad42..2ec391f 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -9,12 +9,12 @@ jobs: tests: strategy: matrix: - operating-system: [ubuntu-latest] + operating-system: [ubuntu-24.04] php-versions: ['8.2', '8.3', '8.4', '8.5'] runs-on: ${{ matrix.operating-system }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -24,10 +24,10 @@ jobs: - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer dependencies - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -43,10 +43,10 @@ jobs: static-analysis: name: Static Analysis - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -56,10 +56,10 @@ jobs: - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer dependencies - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -73,10 +73,10 @@ jobs: coding-standard: name: Coding Standard - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -86,10 +86,10 @@ jobs: - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer dependencies - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} diff --git a/src/Attribute/AttributeExtension.php b/src/Attribute/AttributeExtension.php index 1bd1795..4ddb360 100644 --- a/src/Attribute/AttributeExtension.php +++ b/src/Attribute/AttributeExtension.php @@ -17,6 +17,7 @@ use PHPUnitExtras\Attribute\AttributeExtension as BaseAttributeExtension; use Tarantool\Client\Client; +/** @psalm-suppress ClassMustBeFinal This extension is intended to be extended. */ class AttributeExtension extends BaseAttributeExtension { use Attributes; diff --git a/src/Attribute/Requirement/TarantoolVersionRequirement.php b/src/Attribute/Requirement/TarantoolVersionRequirement.php index 98ed13b..33f7eb2 100644 --- a/src/Attribute/Requirement/TarantoolVersionRequirement.php +++ b/src/Attribute/Requirement/TarantoolVersionRequirement.php @@ -58,7 +58,7 @@ public function check(ProcessableAttribute $attribute, Target $target, Placehold private function getVersion() : string { - if ($this->version) { + if (null !== $this->version) { return $this->version; } diff --git a/src/Client/IsRequestType.php b/src/Client/IsRequestType.php index 141c510..951ac6e 100644 --- a/src/Client/IsRequestType.php +++ b/src/Client/IsRequestType.php @@ -24,11 +24,6 @@ final class IsRequestType extends Constraint public function __construct(int $requestType) { - // needed for backward compatibility with PHPUnit 7 - if (method_exists(parent::class, '__construct')) { - parent::__construct(); - } - $this->requestType = $requestType; } @@ -39,7 +34,7 @@ public function toString() : string } #[\Override] - protected function matches($other) : bool + protected function matches(mixed $other) : bool { return $other instanceof Request && $other->getType() === $this->requestType; } diff --git a/src/Client/TestDoubleClientBuilder.php b/src/Client/TestDoubleClientBuilder.php index 5f37445..b74ecc1 100755 --- a/src/Client/TestDoubleClientBuilder.php +++ b/src/Client/TestDoubleClientBuilder.php @@ -131,7 +131,7 @@ private function createHandler() : MockObject ? $handler->expects(TestCase::exactly($this->shouldBeCalledTimes))->method('handle') : $handler->method('handle'); - if ($this->requests) { + if (null !== $this->requests) { $invocationCount = 0; $requests = $this->requests; $handleMocker->with(TestCase::callback(static function ($request) use (&$invocationCount, $requests) { @@ -193,8 +193,6 @@ private function createMock(string $originalClassName) : MockObject return $this->testCase->getMockBuilder($originalClassName) ->disableOriginalConstructor() ->disableOriginalClone() - ->disableArgumentCloning() - ->disallowMockingUnknownTypes() ->getMock(); } } From c043b2b18c1b7ab77cb4ab053a8af1dc60a15e71 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 00:59:21 +0200 Subject: [PATCH 19/26] refactor: rename Lua requirement attribute --- README.md | 8 ++++---- src/Attribute/Requirement/LuaConditionRequirement.php | 9 +++++---- .../{RequiresLuaCondition.php => RequiresIfLua.php} | 2 +- .../Requirement/LuaConditionRequirementTest.php | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) rename src/Attribute/{RequiresLuaCondition.php => RequiresIfLua.php} (89%) diff --git a/README.md b/README.md index 3571a73..74524e1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It is based on [rybakit/phpunit-extras](https://github.com/rybakit/phpunit-extra * [Lua](#lua) * [Sql](#sql) * [Requirements](#requirements) - * [RequiresLuaCondition](#requiresluacondition) + * [RequiresIfLua](#requiresiflua) * [RequiresTarantoolVersion](#requirestarantoolversion) * [Expectations](#expectations) * [Requests](#requests) @@ -151,15 +151,15 @@ public function testExecuteQueryFetchesAllRows() : void Requirements allow skipping tests based on preconditions. -#### RequiresLuaCondition +#### RequiresIfLua where `` is an arbitrary lua expression that should be evaluated to a Boolean value. *Example:* ```php -use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; +use Tarantool\PhpUnit\Attribute\RequiresIfLua; -#[RequiresLuaCondition("box.session.user() ~= 'guest'")] +#[RequiresIfLua("box.session.user() ~= 'guest'")] public function testChangeUserPassword() : void { // ... diff --git a/src/Attribute/Requirement/LuaConditionRequirement.php b/src/Attribute/Requirement/LuaConditionRequirement.php index df01772..cec2e65 100644 --- a/src/Attribute/Requirement/LuaConditionRequirement.php +++ b/src/Attribute/Requirement/LuaConditionRequirement.php @@ -18,7 +18,7 @@ use PHPUnitExtras\Attribute\Requirement\Requirement; use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Client; -use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; +use Tarantool\PhpUnit\Attribute\RequiresIfLua; final class LuaConditionRequirement implements Requirement { @@ -32,15 +32,16 @@ public function __construct(Client $client) #[\Override] public function getAttributeClass() : string { - return RequiresLuaCondition::class; + return RequiresIfLua::class; } #[\Override] public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string { - if (!$attribute instanceof RequiresLuaCondition) { - throw new \InvalidArgumentException('LuaConditionRequirement only handles RequiresLuaCondition attributes'); + if (!$attribute instanceof RequiresIfLua) { + throw new \InvalidArgumentException('LuaConditionRequirement only handles RequiresIfLua attributes'); } + $condition = $placeholderResolver->resolve($attribute->condition, $target); [$result] = $this->client->evaluate("return ($condition)"); diff --git a/src/Attribute/RequiresLuaCondition.php b/src/Attribute/RequiresIfLua.php similarity index 89% rename from src/Attribute/RequiresLuaCondition.php rename to src/Attribute/RequiresIfLua.php index 8a9c793..b3fca83 100644 --- a/src/Attribute/RequiresLuaCondition.php +++ b/src/Attribute/RequiresIfLua.php @@ -16,7 +16,7 @@ use PHPUnitExtras\Attribute\ProcessableAttribute; #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -final class RequiresLuaCondition implements ProcessableAttribute +final class RequiresIfLua implements ProcessableAttribute { public function __construct(public readonly string $condition) { diff --git a/tests/Attribute/Requirement/LuaConditionRequirementTest.php b/tests/Attribute/Requirement/LuaConditionRequirementTest.php index 59f1ba3..bdc5b60 100755 --- a/tests/Attribute/Requirement/LuaConditionRequirementTest.php +++ b/tests/Attribute/Requirement/LuaConditionRequirementTest.php @@ -18,7 +18,7 @@ use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Request\EvaluateRequest; use Tarantool\PhpUnit\Attribute\Requirement\LuaConditionRequirement; -use Tarantool\PhpUnit\Attribute\RequiresLuaCondition; +use Tarantool\PhpUnit\Attribute\RequiresIfLua; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -39,7 +39,7 @@ public function testCheckPassesForTruthyExpression() : void $requirement = new LuaConditionRequirement($mockClient); - self::assertNull($requirement->check(new RequiresLuaCondition($luaExpression), new Target(self::class), self::resolver())); + self::assertNull($requirement->check(new RequiresIfLua($luaExpression), new Target(self::class), self::resolver())); } public function testCheckFailsForFalsyExpression() : void @@ -56,7 +56,7 @@ public function testCheckFailsForFalsyExpression() : void $errorMessage = \sprintf('"%s" is not evaluated to true', $luaExpression); $requirement = new LuaConditionRequirement($mockClient); - self::assertSame($errorMessage, $requirement->check(new RequiresLuaCondition($luaExpression), new Target(self::class), self::resolver())); + self::assertSame($errorMessage, $requirement->check(new RequiresIfLua($luaExpression), new Target(self::class), self::resolver())); } private static function resolver() : PlaceholderResolver From b544f5ebf02af87afe0f07b13bde153c05d3b3cb Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 01:01:18 +0200 Subject: [PATCH 20/26] style: import PHPUnit extension types --- src/Attribute/AttributeExtension.php | 11 +++++++---- src/Attribute/Processor/LuaProcessor.php | 1 + src/Attribute/Processor/SqlProcessor.php | 1 + .../Requirement/TarantoolVersionRequirement.php | 1 + tests/Attribute/AttributeExtension.php | 3 ++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Attribute/AttributeExtension.php b/src/Attribute/AttributeExtension.php index 4ddb360..0cc73ab 100644 --- a/src/Attribute/AttributeExtension.php +++ b/src/Attribute/AttributeExtension.php @@ -14,6 +14,9 @@ namespace Tarantool\PhpUnit\Attribute; use PHPUnit\Exception; +use PHPUnit\Runner\Extension\Facade; +use PHPUnit\Runner\Extension\ParameterCollection; +use PHPUnit\TextUI\Configuration\Configuration; use PHPUnitExtras\Attribute\AttributeExtension as BaseAttributeExtension; use Tarantool\Client\Client; @@ -29,13 +32,13 @@ class AttributeExtension extends BaseAttributeExtension private $client; #[\Override] - public function bootstrap(\PHPUnit\TextUI\Configuration\Configuration $configuration, \PHPUnit\Runner\Extension\Facade $facade, \PHPUnit\Runner\Extension\ParameterCollection $parameters) : void + public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters) : void { $this->parseParameters($parameters); parent::bootstrap($configuration, $facade, $parameters); } - protected function parseParameters(\PHPUnit\Runner\Extension\ParameterCollection $parameters) : void + protected function parseParameters(ParameterCollection $parameters) : void { if ($parameters->has('dsn')) { $this->clientConfig = $parameters->get('dsn'); @@ -43,10 +46,10 @@ protected function parseParameters(\PHPUnit\Runner\Extension\ParameterCollection $closure = \Closure::bind(function () { /** * @psalm-suppress InaccessibleProperty - * @var \PHPUnit\Runner\Extension\ParameterCollection $this + * @var ParameterCollection $this */ return $this->parameters; - }, $parameters, \PHPUnit\Runner\Extension\ParameterCollection::class); + }, $parameters, ParameterCollection::class); $options = $closure ? $closure() : []; if ([] !== $options) { diff --git a/src/Attribute/Processor/LuaProcessor.php b/src/Attribute/Processor/LuaProcessor.php index 11d1694..2b97495 100644 --- a/src/Attribute/Processor/LuaProcessor.php +++ b/src/Attribute/Processor/LuaProcessor.php @@ -41,6 +41,7 @@ public function process(ProcessableAttribute $attribute, Target $target, Placeho if (!$attribute instanceof Lua) { throw new \InvalidArgumentException('LuaProcessor only handles Lua attributes'); } + $this->client->evaluate($placeholderResolver->resolve($attribute->code, $target)); } } diff --git a/src/Attribute/Processor/SqlProcessor.php b/src/Attribute/Processor/SqlProcessor.php index 029938a..5ef46a5 100644 --- a/src/Attribute/Processor/SqlProcessor.php +++ b/src/Attribute/Processor/SqlProcessor.php @@ -41,6 +41,7 @@ public function process(ProcessableAttribute $attribute, Target $target, Placeho if (!$attribute instanceof Sql) { throw new \InvalidArgumentException('SqlProcessor only handles Sql attributes'); } + $this->client->executeUpdate($placeholderResolver->resolve($attribute->code, $target)); } } diff --git a/src/Attribute/Requirement/TarantoolVersionRequirement.php b/src/Attribute/Requirement/TarantoolVersionRequirement.php index 33f7eb2..87e5f2f 100644 --- a/src/Attribute/Requirement/TarantoolVersionRequirement.php +++ b/src/Attribute/Requirement/TarantoolVersionRequirement.php @@ -45,6 +45,7 @@ public function check(ProcessableAttribute $attribute, Target $target, Placehold if (!$attribute instanceof RequiresTarantoolVersion) { throw new \InvalidArgumentException('TarantoolVersionRequirement only handles RequiresTarantoolVersion attributes'); } + $value = $placeholderResolver->resolve($attribute->constraint, $target); // Replace dash with dot. $constraints = (string) preg_replace('/(\d+\.\d+\.\d+)-(\d+)/', '$1.$2', $value); diff --git a/tests/Attribute/AttributeExtension.php b/tests/Attribute/AttributeExtension.php index b39b976..58ed330 100755 --- a/tests/Attribute/AttributeExtension.php +++ b/tests/Attribute/AttributeExtension.php @@ -13,6 +13,7 @@ namespace Tarantool\PhpUnit\Tests\Attribute; +use PHPUnit\Runner\Extension\ParameterCollection; use Tarantool\Client\Client; use Tarantool\PhpUnit\Attribute\AttributeExtension as BaseAttributeExtension; use Tarantool\PhpUnit\Client\TestDoubleClientBuilder; @@ -30,6 +31,6 @@ protected function getClient() : Client public function doParseParameters(array $parameters) : void { - $this->parseParameters(\PHPUnit\Runner\Extension\ParameterCollection::fromArray($parameters)); + $this->parseParameters(ParameterCollection::fromArray($parameters)); } } From 53daaa7d8a21b2c3472c117a8e5ff1efc1da8c98 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 01:10:22 +0200 Subject: [PATCH 21/26] refactor: rename Lua condition requirement --- src/Attribute/Attributes.php | 4 ++-- .../{LuaConditionRequirement.php => IfLuaRequirement.php} | 4 ++-- ...ditionRequirementTest.php => IfLuaRequirementTest.php} | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/Attribute/Requirement/{LuaConditionRequirement.php => IfLuaRequirement.php} (88%) rename tests/Attribute/Requirement/{LuaConditionRequirementTest.php => IfLuaRequirementTest.php} (89%) diff --git a/src/Attribute/Attributes.php b/src/Attribute/Attributes.php index 1b0c098..69c201b 100644 --- a/src/Attribute/Attributes.php +++ b/src/Attribute/Attributes.php @@ -18,7 +18,7 @@ use Tarantool\Client\Client; use Tarantool\PhpUnit\Attribute\Processor\LuaProcessor; use Tarantool\PhpUnit\Attribute\Processor\SqlProcessor; -use Tarantool\PhpUnit\Attribute\Requirement\LuaConditionRequirement; +use Tarantool\PhpUnit\Attribute\Requirement\IfLuaRequirement; use Tarantool\PhpUnit\Attribute\Requirement\TarantoolVersionRequirement; trait Attributes @@ -34,7 +34,7 @@ protected function createAttributeProcessorBuilder() : AttributeProcessorBuilder return $this->createBaseAttributeProcessorBuilder() ->addProcessor(new LuaProcessor($client)) ->addProcessor(new SqlProcessor($client)) - ->addRequirement(new LuaConditionRequirement($client)) + ->addRequirement(new IfLuaRequirement($client)) ->addRequirement(new TarantoolVersionRequirement($client)) ; } diff --git a/src/Attribute/Requirement/LuaConditionRequirement.php b/src/Attribute/Requirement/IfLuaRequirement.php similarity index 88% rename from src/Attribute/Requirement/LuaConditionRequirement.php rename to src/Attribute/Requirement/IfLuaRequirement.php index cec2e65..bae15e0 100644 --- a/src/Attribute/Requirement/LuaConditionRequirement.php +++ b/src/Attribute/Requirement/IfLuaRequirement.php @@ -20,7 +20,7 @@ use Tarantool\Client\Client; use Tarantool\PhpUnit\Attribute\RequiresIfLua; -final class LuaConditionRequirement implements Requirement +final class IfLuaRequirement implements Requirement { private Client $client; @@ -39,7 +39,7 @@ public function getAttributeClass() : string public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string { if (!$attribute instanceof RequiresIfLua) { - throw new \InvalidArgumentException('LuaConditionRequirement only handles RequiresIfLua attributes'); + throw new \InvalidArgumentException('IfLuaRequirement only handles RequiresIfLua attributes'); } $condition = $placeholderResolver->resolve($attribute->condition, $target); diff --git a/tests/Attribute/Requirement/LuaConditionRequirementTest.php b/tests/Attribute/Requirement/IfLuaRequirementTest.php similarity index 89% rename from tests/Attribute/Requirement/LuaConditionRequirementTest.php rename to tests/Attribute/Requirement/IfLuaRequirementTest.php index bdc5b60..cc0e6f4 100755 --- a/tests/Attribute/Requirement/LuaConditionRequirementTest.php +++ b/tests/Attribute/Requirement/IfLuaRequirementTest.php @@ -17,12 +17,12 @@ use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Request\EvaluateRequest; -use Tarantool\PhpUnit\Attribute\Requirement\LuaConditionRequirement; +use Tarantool\PhpUnit\Attribute\Requirement\IfLuaRequirement; use Tarantool\PhpUnit\Attribute\RequiresIfLua; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; -final class LuaConditionRequirementTest extends TestCase +final class IfLuaRequirementTest extends TestCase { use TestDoubleClient; @@ -37,7 +37,7 @@ public function testCheckPassesForTruthyExpression() : void ) ->build(); - $requirement = new LuaConditionRequirement($mockClient); + $requirement = new IfLuaRequirement($mockClient); self::assertNull($requirement->check(new RequiresIfLua($luaExpression), new Target(self::class), self::resolver())); } @@ -54,7 +54,7 @@ public function testCheckFailsForFalsyExpression() : void ->build(); $errorMessage = \sprintf('"%s" is not evaluated to true', $luaExpression); - $requirement = new LuaConditionRequirement($mockClient); + $requirement = new IfLuaRequirement($mockClient); self::assertSame($errorMessage, $requirement->check(new RequiresIfLua($luaExpression), new Target(self::class), self::resolver())); } From fafb9868c97e310033a6a354560d66c4af87a3b5 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 01:11:55 +0200 Subject: [PATCH 22/26] refactor: rename Tarantool version requirement --- README.md | 8 ++++---- src/Attribute/Attributes.php | 4 ++-- ...ionRequirement.php => TarantoolRequirement.php} | 10 +++++----- ...sTarantoolVersion.php => RequiresTarantool.php} | 2 +- ...rementTest.php => TarantoolRequirementTest.php} | 14 +++++++------- 5 files changed, 19 insertions(+), 19 deletions(-) rename src/Attribute/Requirement/{TarantoolVersionRequirement.php => TarantoolRequirement.php} (85%) rename src/Attribute/{RequiresTarantoolVersion.php => RequiresTarantool.php} (88%) rename tests/Attribute/Requirement/{TarantoolVersionRequirementTest.php => TarantoolRequirementTest.php} (91%) diff --git a/README.md b/README.md index 74524e1..a1ef613 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It is based on [rybakit/phpunit-extras](https://github.com/rybakit/phpunit-extra * [Sql](#sql) * [Requirements](#requirements) * [RequiresIfLua](#requiresiflua) - * [RequiresTarantoolVersion](#requirestarantoolversion) + * [RequiresTarantool](#requirestarantool) * [Expectations](#expectations) * [Requests](#requests) * [Prepared statements](#prepared-statements) @@ -166,16 +166,16 @@ public function testChangeUserPassword() : void } ``` -#### RequiresTarantoolVersion +#### RequiresTarantool where `` is a composer-like version constraint. For details on supported formats, please see the Composer [documentation](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints). *Example:* ```php -use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; +use Tarantool\PhpUnit\Attribute\RequiresTarantool; -#[RequiresTarantoolVersion('^2.3.2')] +#[RequiresTarantool('^2.3.2')] public function testPrepareCreatesPreparedStatement() : void { // ... diff --git a/src/Attribute/Attributes.php b/src/Attribute/Attributes.php index 69c201b..b553385 100644 --- a/src/Attribute/Attributes.php +++ b/src/Attribute/Attributes.php @@ -19,7 +19,7 @@ use Tarantool\PhpUnit\Attribute\Processor\LuaProcessor; use Tarantool\PhpUnit\Attribute\Processor\SqlProcessor; use Tarantool\PhpUnit\Attribute\Requirement\IfLuaRequirement; -use Tarantool\PhpUnit\Attribute\Requirement\TarantoolVersionRequirement; +use Tarantool\PhpUnit\Attribute\Requirement\TarantoolRequirement; trait Attributes { @@ -35,7 +35,7 @@ protected function createAttributeProcessorBuilder() : AttributeProcessorBuilder ->addProcessor(new LuaProcessor($client)) ->addProcessor(new SqlProcessor($client)) ->addRequirement(new IfLuaRequirement($client)) - ->addRequirement(new TarantoolVersionRequirement($client)) + ->addRequirement(new TarantoolRequirement($client)) ; } diff --git a/src/Attribute/Requirement/TarantoolVersionRequirement.php b/src/Attribute/Requirement/TarantoolRequirement.php similarity index 85% rename from src/Attribute/Requirement/TarantoolVersionRequirement.php rename to src/Attribute/Requirement/TarantoolRequirement.php index 87e5f2f..eb31de1 100644 --- a/src/Attribute/Requirement/TarantoolVersionRequirement.php +++ b/src/Attribute/Requirement/TarantoolRequirement.php @@ -19,9 +19,9 @@ use PHPUnitExtras\Attribute\Requirement\Requirement; use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Client; -use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; +use Tarantool\PhpUnit\Attribute\RequiresTarantool; -final class TarantoolVersionRequirement implements Requirement +final class TarantoolRequirement implements Requirement { private Client $client; @@ -36,14 +36,14 @@ public function __construct(Client $client) #[\Override] public function getAttributeClass() : string { - return RequiresTarantoolVersion::class; + return RequiresTarantool::class; } #[\Override] public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string { - if (!$attribute instanceof RequiresTarantoolVersion) { - throw new \InvalidArgumentException('TarantoolVersionRequirement only handles RequiresTarantoolVersion attributes'); + if (!$attribute instanceof RequiresTarantool) { + throw new \InvalidArgumentException('TarantoolRequirement only handles RequiresTarantool attributes'); } $value = $placeholderResolver->resolve($attribute->constraint, $target); diff --git a/src/Attribute/RequiresTarantoolVersion.php b/src/Attribute/RequiresTarantool.php similarity index 88% rename from src/Attribute/RequiresTarantoolVersion.php rename to src/Attribute/RequiresTarantool.php index 0ffd97a..975bbe5 100644 --- a/src/Attribute/RequiresTarantoolVersion.php +++ b/src/Attribute/RequiresTarantool.php @@ -16,7 +16,7 @@ use PHPUnitExtras\Attribute\ProcessableAttribute; #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -final class RequiresTarantoolVersion implements ProcessableAttribute +final class RequiresTarantool implements ProcessableAttribute { public function __construct(public readonly string $constraint) { diff --git a/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php b/tests/Attribute/Requirement/TarantoolRequirementTest.php similarity index 91% rename from tests/Attribute/Requirement/TarantoolVersionRequirementTest.php rename to tests/Attribute/Requirement/TarantoolRequirementTest.php index 46fb880..6095bdf 100755 --- a/tests/Attribute/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Attribute/Requirement/TarantoolRequirementTest.php @@ -18,12 +18,12 @@ use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Request\CallRequest; -use Tarantool\PhpUnit\Attribute\Requirement\TarantoolVersionRequirement; -use Tarantool\PhpUnit\Attribute\RequiresTarantoolVersion; +use Tarantool\PhpUnit\Attribute\Requirement\TarantoolRequirement; +use Tarantool\PhpUnit\Attribute\RequiresTarantool; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; -final class TarantoolVersionRequirementTest extends TestCase +final class TarantoolRequirementTest extends TestCase { use TestDoubleClient; @@ -36,9 +36,9 @@ public function testCheckPassesForValidConstraints(string $serverVersion, string TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) ->build(); - $requirement = new TarantoolVersionRequirement($mockClient); + $requirement = new TarantoolRequirement($mockClient); - self::assertNull($requirement->check(new RequiresTarantoolVersion($constraints), new Target(self::class), self::resolver())); + self::assertNull($requirement->check(new RequiresTarantool($constraints), new Target(self::class), self::resolver())); } public static function provideCheckPassesForValidConstraintsData() : iterable @@ -106,10 +106,10 @@ public function testCheckFailsForInvalidConstraints(string $serverVersion, strin TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) ->build(); - $requirement = new TarantoolVersionRequirement($mockClient); + $requirement = new TarantoolRequirement($mockClient); $errorMessage = \sprintf('Tarantool version %s is required', $constraints); - self::assertSame($errorMessage, $requirement->check(new RequiresTarantoolVersion($constraints), new Target(self::class), self::resolver())); + self::assertSame($errorMessage, $requirement->check(new RequiresTarantool($constraints), new Target(self::class), self::resolver())); } public static function provideCheckFailsForInvalidConstraintsData() : iterable From 152d2f394f23d81b1112a4a91d09279c4049016b Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 01:25:28 +0200 Subject: [PATCH 23/26] docs: update PHPUnit 10 extension configuration --- README.md | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index a1ef613..1169268 100644 --- a/README.md +++ b/README.md @@ -57,42 +57,37 @@ Another option is to register an extension called `AttributeExtension`: ```xml - + ``` By default, the extension assumes that the Tarantool server you are going to connect to is available on `127.0.0.1:3301`. You can customize the default settings by specifying either a [DSN string](https://github.com/tarantool-php/client#dsn-string) or an [array of options](https://github.com/tarantool-php/client#array-of-options) -as extension configuration values: +as extension configuration values. PHPUnit 10 passes extension parameters as strings, so use the DSN form for options that require numeric or Boolean values (such as `socket_timeout`): ```xml - - - tcp://127.0.0.1:3301/?socket_timeout=10 - - + + + + + ``` or ```xml - - - - - tcp://127.0.0.1:3301 - - - 10 - - - - + + + + + + + ``` On top of that, the configuration values can resolve environment variables, @@ -100,11 +95,11 @@ which might be useful if you need to share the same settings with a Tarantool instance file or any other script: ```xml - - - tcp://%env(TARANTOOL_HOST)%:%env(TARANTOOL_PORT)% - - + + + + + ``` Once the attributes are configured, you can start using them: From cfbd121723ebf34e92655783c2b3080ab8053520 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 20:22:19 +0200 Subject: [PATCH 24/26] Update composer.json --- composer.json | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index f210eaa..d8f2bc3 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "require": { "php": "^8.2", "composer/semver": "^3.3", - "rybakit/phpunit-extras": "dev-feature/php82-phpunit10-attributes-migration", + "rybakit/phpunit-extras": "^0.3.0", "symfony/expression-language": "^7.0", "tarantool/client": "^0.10" }, @@ -38,11 +38,5 @@ }, "sort-packages": true }, - "minimum-stability": "dev", - "prefer-stable": true, - "repositories": [{ - "type": "vcs", - "url": "https://github.com/rybakit/phpunit-extras", - "no-api": true - }] + "prefer-stable": true } From 5772b381e06c86cdc34c3f48248c5cc7065fdc39 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 20:25:04 +0200 Subject: [PATCH 25/26] fix(ci): upgrade setup-php to patched version --- .github/workflows/qa.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 2ec391f..4495048 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@v2.37.1 with: php-version: ${{ matrix.php-versions }} coverage: none @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@v2.37.1 with: php-version: '8.2' coverage: none @@ -79,7 +79,7 @@ jobs: uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@v2.37.1 with: php-version: '8.2' coverage: none From c4908fb0506de19058c6c06e90b328cb0369c945 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Tue, 29 Sep 2026 20:34:56 +0200 Subject: [PATCH 26/26] fix(ci): use valid setup-php release tag --- .github/workflows/qa.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 4495048..e09454f 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2.37.1 + uses: shivammathur/setup-php@2.37.1 with: php-version: ${{ matrix.php-versions }} coverage: none @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2.37.1 + uses: shivammathur/setup-php@2.37.1 with: php-version: '8.2' coverage: none @@ -79,7 +79,7 @@ jobs: uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2.37.1 + uses: shivammathur/setup-php@2.37.1 with: php-version: '8.2' coverage: none