diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index c2fe040..e09454f 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -9,25 +9,25 @@ jobs: tests: strategy: matrix: - operating-system: [ubuntu-latest] - php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1'] + 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@v2 + uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@2.37.1 with: php-version: ${{ matrix.php-versions }} coverage: none - 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@v1 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -43,23 +43,23 @@ jobs: static-analysis: name: Static Analysis - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@2.37.1 with: - php-version: '8.0' + php-version: '8.2' coverage: none - 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@v1 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -73,23 +73,23 @@ jobs: coding-standard: name: Coding Standard - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v7 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@2.37.1 with: - php-version: 8.0 + php-version: '8.2' coverage: none - 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@v2 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 98% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index da6bde7..992b9a0 100644 --- a/.php_cs.dist +++ 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/README.md b/README.md index 064dc69..1169268 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) + * [RequiresIfLua](#requiresiflua) + * [RequiresTarantool](#requirestarantool) * [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,46 +53,41 @@ final class MyTest extends TestCase } ``` -Another option is to register an extension called `AnnotationExtension`: +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,14 +95,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 +113,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 +130,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 +146,38 @@ public function testExecuteQueryFetchesAllRows() : void Requirements allow skipping tests based on preconditions. -#### LuaCondition - -*Format:* - -``` -@requires luaCondition -``` +#### RequiresIfLua 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\RequiresIfLua; + +#[RequiresIfLua("box.session.user() ~= 'guest'")] public function testChangeUserPassword() : void { // ... } ``` -#### TarantoolVersion - -*Format:* - -``` -@requires Tarantool -``` +#### 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 -/** - * @requires Tarantool ^2.3.2 - */ +use Tarantool\PhpUnit\Attribute\RequiresTarantool; + +#[RequiresTarantool('^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 f4c4d7d..d8f2bc3 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": [ @@ -11,17 +11,16 @@ } ], "require": { - "php": "^7.2.5|^8", - "composer/package-versions-deprecated": "1.11.99.5", + "php": "^8.2", "composer/semver": "^3.3", - "rybakit/phpunit-extras": "^0.2.4", - "symfony/expression-language": "^3.3|^4|^5|^6", + "rybakit/phpunit-extras": "^0.3.0", + "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 +37,6 @@ "*": "dist" }, "sort-packages": true - } + }, + "prefer-stable": true } diff --git a/phpunit-extension.xml b/phpunit-extension.xml index fef869a..95bf519 100644 --- a/phpunit-extension.xml +++ b/phpunit-extension.xml @@ -1,45 +1,26 @@ - - - - - - - - - - - - tests/Annotation/AnnotationExtensionTest.php - - - - - - src - - - - - - - - - true - - - 42 - - - foobar - - - - - + + + + + + + + + + tests/Attribute/AttributeExtensionTest.php + + + + + src + + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c48d998..2f26844 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,28 +1,20 @@ - - - - - - - - - - - - tests - tests/Annotation/AnnotationExtensionTest.php - - - - - - src - - + + + + + + + + + + tests + tests/Attribute/AttributeExtensionTest.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/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/Processor/LuaProcessor.php b/src/Annotation/Processor/LuaProcessor.php deleted file mode 100644 index 375bd67..0000000 --- a/src/Annotation/Processor/LuaProcessor.php +++ /dev/null @@ -1,37 +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; - } - - public function getName() : string - { - return 'lua'; - } - - 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 144c6b5..0000000 --- a/src/Annotation/Processor/SqlProcessor.php +++ /dev/null @@ -1,37 +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; - } - - public function getName() : string - { - return 'sql'; - } - - 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 9b1b950..0000000 --- a/src/Annotation/Requirement/LuaConditionRequirement.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\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; - } - - public function getName() : string - { - return 'luaCondition'; - } - - 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/Requirement/TarantoolVersionRequirement.php b/src/Annotation/Requirement/TarantoolVersionRequirement.php deleted file mode 100644 index 7d8ecb4..0000000 --- a/src/Annotation/Requirement/TarantoolVersionRequirement.php +++ /dev/null @@ -1,66 +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 Composer\Semver\Semver; -use PHPUnitExtras\Annotation\Requirement\Requirement; -use Tarantool\Client\Client; - -final class TarantoolVersionRequirement implements Requirement -{ - private $client; - - /** @var string|null */ - private $version; - - public function __construct(Client $client) - { - $this->client = $client; - } - - public function getName() : string - { - return 'Tarantool'; - } - - public function check(string $value) : ?string - { - // Replace dash with dot. - $constraints = preg_replace('/(\d+\.\d+\.\d+)-(\d+)/', '$1.$2', $value); - - if (Semver::satisfies($this->getVersion(), $constraints)) { - return null; - } - - return sprintf('%s version %s is required', $this->getName(), $value); - } - - private function getVersion() : string - { - if ($this->version) { - return $this->version; - } - - $version = $this->client->call('box.info')[0]['version']; - - // Normalize 2.2.1-3-g878e2a42c to 2.2.1.3. - $version = 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); - - return $this->version = $version; - } -} diff --git a/src/Annotation/AnnotationExtension.php b/src/Attribute/AttributeExtension.php similarity index 51% rename from src/Annotation/AnnotationExtension.php rename to src/Attribute/AttributeExtension.php index 2c8ae49..0cc73ab 100644 --- a/src/Annotation/AnnotationExtension.php +++ b/src/Attribute/AttributeExtension.php @@ -11,30 +11,54 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Annotation; +namespace Tarantool\PhpUnit\Attribute; use PHPUnit\Exception; -use PHPUnitExtras\Annotation\AnnotationExtension as BaseAnnotationExtension; +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; -class AnnotationExtension extends BaseAnnotationExtension +/** @psalm-suppress ClassMustBeFinal This extension is intended to be extended. */ +class AttributeExtension extends BaseAttributeExtension { - use Annotations; + use Attributes; /** @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(Configuration $configuration, Facade $facade, ParameterCollection $parameters) : void { - $this->clientConfig = $clientConfig; + $this->parseParameters($parameters); + parent::bootstrap($configuration, $facade, $parameters); + } + + protected function parseParameters(ParameterCollection $parameters) : void + { + if ($parameters->has('dsn')) { + $this->clientConfig = $parameters->get('dsn'); + } else { + $closure = \Closure::bind(function () { + /** + * @psalm-suppress InaccessibleProperty + * @var ParameterCollection $this + */ + return $this->parameters; + }, $parameters, ParameterCollection::class); + $options = $closure ? $closure() : []; + + if ([] !== $options) { + $this->clientConfig = $options; + } + } } + #[\Override] protected function getClient() : Client { if ($this->client) { @@ -71,12 +95,12 @@ 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; } - $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/Attribute/Attributes.php b/src/Attribute/Attributes.php new file mode 100644 index 0000000..b553385 --- /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\IfLuaRequirement; +use Tarantool\PhpUnit\Attribute\Requirement\TarantoolRequirement; + +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 IfLuaRequirement($client)) + ->addRequirement(new TarantoolRequirement($client)) + ; + } + + abstract protected function getClient() : Client; +} diff --git a/src/Attribute/Lua.php b/src/Attribute/Lua.php new file mode 100644 index 0000000..39a97b6 --- /dev/null +++ b/src/Attribute/Lua.php @@ -0,0 +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 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..2b97495 --- /dev/null +++ b/src/Attribute/Processor/LuaProcessor.php @@ -0,0 +1,47 @@ + + * + * 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\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Processor\Processor; +use PHPUnitExtras\Attribute\Target; +use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\Lua; + +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..5ef46a5 --- /dev/null +++ b/src/Attribute/Processor/SqlProcessor.php @@ -0,0 +1,47 @@ + + * + * 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\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Processor\Processor; +use PHPUnitExtras\Attribute\Target; +use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\Sql; + +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/IfLuaRequirement.php b/src/Attribute/Requirement/IfLuaRequirement.php new file mode 100644 index 0000000..bae15e0 --- /dev/null +++ b/src/Attribute/Requirement/IfLuaRequirement.php @@ -0,0 +1,54 @@ + + * + * 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\Requirement\Requirement; +use PHPUnitExtras\Attribute\Target; +use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\RequiresIfLua; + +final class IfLuaRequirement implements Requirement +{ + private Client $client; + + public function __construct(Client $client) + { + $this->client = $client; + } + + #[\Override] + public function getAttributeClass() : string + { + return RequiresIfLua::class; + } + + #[\Override] + public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string + { + if (!$attribute instanceof RequiresIfLua) { + throw new \InvalidArgumentException('IfLuaRequirement only handles RequiresIfLua 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/Attribute/Requirement/TarantoolRequirement.php b/src/Attribute/Requirement/TarantoolRequirement.php new file mode 100644 index 0000000..eb31de1 --- /dev/null +++ b/src/Attribute/Requirement/TarantoolRequirement.php @@ -0,0 +1,80 @@ + + * + * 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 Composer\Semver\Semver; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\ProcessableAttribute; +use PHPUnitExtras\Attribute\Requirement\Requirement; +use PHPUnitExtras\Attribute\Target; +use Tarantool\Client\Client; +use Tarantool\PhpUnit\Attribute\RequiresTarantool; + +final class TarantoolRequirement implements Requirement +{ + private Client $client; + + /** @var string|null */ + private $version; + + public function __construct(Client $client) + { + $this->client = $client; + } + + #[\Override] + public function getAttributeClass() : string + { + return RequiresTarantool::class; + } + + #[\Override] + public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string + { + if (!$attribute instanceof RequiresTarantool) { + throw new \InvalidArgumentException('TarantoolRequirement only handles RequiresTarantool attributes'); + } + + $value = $placeholderResolver->resolve($attribute->constraint, $target); + // Replace dash with dot. + $constraints = (string) preg_replace('/(\d+\.\d+\.\d+)-(\d+)/', '$1.$2', $value); + + if (Semver::satisfies($this->getVersion(), $constraints)) { + return null; + } + + return \sprintf('Tarantool version %s is required', $value); + } + + private function getVersion() : string + { + if (null !== $this->version) { + return $this->version; + } + + $version = $this->client->call('box.info')[0]['version']; + 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 = (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 = (string) preg_replace('/(\d)-entrypoint/', '$1-dev+entrypoint', $version); + + return $this->version = $version; + } +} diff --git a/src/Attribute/RequiresIfLua.php b/src/Attribute/RequiresIfLua.php new file mode 100644 index 0000000..b3fca83 --- /dev/null +++ b/src/Attribute/RequiresIfLua.php @@ -0,0 +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 RequiresIfLua implements ProcessableAttribute +{ + public function __construct(public readonly string $condition) + { + } +} diff --git a/src/Attribute/RequiresTarantool.php b/src/Attribute/RequiresTarantool.php new file mode 100644 index 0000000..975bbe5 --- /dev/null +++ b/src/Attribute/RequiresTarantool.php @@ -0,0 +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 RequiresTarantool implements ProcessableAttribute +{ + public function __construct(public readonly string $constraint) + { + } +} diff --git a/src/Attribute/Sql.php b/src/Attribute/Sql.php new file mode 100644 index 0000000..d51be8c --- /dev/null +++ b/src/Attribute/Sql.php @@ -0,0 +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 Sql implements ProcessableAttribute +{ + public function __construct(public readonly string $code) + { + } +} diff --git a/src/Client/IsRequestType.php b/src/Client/IsRequestType.php index b8b4c57..951ac6e 100644 --- a/src/Client/IsRequestType.php +++ b/src/Client/IsRequestType.php @@ -24,20 +24,17 @@ final class IsRequestType extends Constraint public function __construct(int $requestType) { - // needed for backward compatibility with PHPUnit 7 - if (\is_callable('parent::__construct')) { - parent::__construct(); - } - $this->requestType = $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))); } - protected function matches($other) : bool + #[\Override] + 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 8ce18b3..b74ecc1 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) { @@ -51,12 +51,8 @@ public function __construct(TestCase $testCase) public static function buildDummy() : Client { - /** - * @psalm-suppress InternalMethod - * @psalm-suppress PropertyNotSetInConstructor - */ - $self = new self(new class() extends TestCase { - }); + /** @psalm-suppress InternalMethod */ + $self = new self(new class('dummy') extends TestCase {}); return $self->build(); } @@ -79,9 +75,8 @@ public function shouldSend($request, ...$requests) : self /** * @param Request|Constraint|int $request - * @param Response ...$responses */ - public function shouldHandle($request, ...$responses) : self + public function shouldHandle($request, Response ...$responses) : self { $this->shouldSend($request); $this->willReceive(...$responses); @@ -136,8 +131,25 @@ private function createHandler() : MockObject ? $handler->expects(TestCase::exactly($this->shouldBeCalledTimes))->method('handle') : $handler->method('handle'); - if ($this->requests) { - $handleMocker->withConsecutive(...array_chunk($this->requests, 1)); + if (null !== $this->requests) { + $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); + } + + $constraint = new \PHPUnit\Framework\Constraint\IsEqual($expected); + + return (bool) $constraint->evaluate($request, '', true); + })); } if (1 === \count($this->responses)) { @@ -181,8 +193,6 @@ private function createMock(string $originalClassName) : MockObject return $this->testCase->getMockBuilder($originalClassName) ->disableOriginalConstructor() ->disableOriginalClone() - ->disableArgumentCloning() - ->disallowMockingUnknownTypes() ->getMock(); } } diff --git a/src/Client/TestDoubleFactory.php b/src/Client/TestDoubleFactory.php old mode 100755 new mode 100644 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/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 59% rename from tests/Annotation/AnnotationExtension.php rename to tests/Attribute/AttributeExtension.php index d2a9e83..58ed330 100755 --- a/tests/Annotation/AnnotationExtension.php +++ b/tests/Attribute/AttributeExtension.php @@ -11,13 +11,14 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation; +namespace Tarantool\PhpUnit\Tests\Attribute; +use PHPUnit\Runner\Extension\ParameterCollection; 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; @@ -27,4 +28,9 @@ protected function getClient() : Client return TestDoubleClientBuilder::buildDummy(); } + + public function doParseParameters(array $parameters) : void + { + $this->parseParameters(ParameterCollection::fromArray($parameters)); + } } diff --git a/tests/Annotation/AnnotationExtensionTest.php b/tests/Attribute/AttributeExtensionTest.php similarity index 58% rename from tests/Annotation/AnnotationExtensionTest.php rename to tests/Attribute/AttributeExtensionTest.php index cf9c334..ef31179 100755 --- a/tests/Annotation/AnnotationExtensionTest.php +++ b/tests/Attribute/AttributeExtensionTest.php @@ -11,38 +11,40 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation; +namespace Tarantool\PhpUnit\Tests\Attribute; use PHPUnit\Framework\TestCase; +use Tarantool\PhpUnit\Attribute\Lua; -final class AnnotationExtensionTest extends TestCase +final class AttributeExtensionTest extends TestCase { - /** - * @lua dummy_code_to_trigger_annotation_processing = true - */ + private function bootstrapExtension(string $method, array $parameters = []) : AttributeExtension + { + $ext = new AttributeExtension(); + $ext->doParseParameters($parameters); + $ext->processTestAttributes(self::class, $method); + + return $ext; + } + + #[Lua('dummy_code_to_trigger_attribute_processing = true')] public function testConstructorUsesDefaultDsn() : void { - $ext = new AnnotationExtension(); + $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_attribute_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_attribute_processing = true')] public function testGetClientConfigNormalizesDsnString() : void { $hostname = 'tnt_foobar'; @@ -51,15 +53,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_attribute_processing = true')] public function testGetClientConfigNormalizesOptionArray() : void { $hostname = 'tnt_foobar'; @@ -68,17 +67,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/Processor/LuaProcessorTest.php b/tests/Attribute/Processor/LuaProcessorTest.php similarity index 59% rename from tests/Annotation/Processor/LuaProcessorTest.php rename to tests/Attribute/Processor/LuaProcessorTest.php index 8fe890f..af61d98 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 PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Request\EvaluateRequest; -use Tarantool\PhpUnit\Annotation\Processor\LuaProcessor; +use Tarantool\PhpUnit\Attribute\Lua; +use Tarantool\PhpUnit\Attribute\Processor\LuaProcessor; use Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -35,6 +38,16 @@ 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 61% rename from tests/Annotation/Processor/SqlProcessorTest.php rename to tests/Attribute/Processor/SqlProcessorTest.php index 6c5a285..834035a 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 PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; 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 Tarantool\PhpUnit\Client\TestDoubleClient; use Tarantool\PhpUnit\Client\TestDoubleFactory; @@ -36,6 +39,16 @@ 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/IfLuaRequirementTest.php similarity index 51% rename from tests/Annotation/Requirement/LuaConditionRequirementTest.php rename to tests/Attribute/Requirement/IfLuaRequirementTest.php index 24096a2..cc0e6f4 100755 --- a/tests/Annotation/Requirement/LuaConditionRequirementTest.php +++ b/tests/Attribute/Requirement/IfLuaRequirementTest.php @@ -11,15 +11,18 @@ declare(strict_types=1); -namespace Tarantool\PhpUnit\Tests\Annotation\Requirement; +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\Annotation\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; @@ -34,9 +37,9 @@ public function testCheckPassesForTruthyExpression() : void ) ->build(); - $requirement = new LuaConditionRequirement($mockClient); + $requirement = new IfLuaRequirement($mockClient); - self::assertNull($requirement->check($luaExpression)); + self::assertNull($requirement->check(new RequiresIfLua($luaExpression), new Target(self::class), self::resolver())); } public function testCheckFailsForFalsyExpression() : void @@ -50,9 +53,24 @@ public function testCheckFailsForFalsyExpression() : void ) ->build(); - $errorMessage = sprintf('"%s" is not evaluated to true', $luaExpression); - $requirement = new LuaConditionRequirement($mockClient); + $errorMessage = \sprintf('"%s" is not evaluated to true', $luaExpression); + $requirement = new IfLuaRequirement($mockClient); - self::assertSame($errorMessage, $requirement->check($luaExpression)); + self::assertSame($errorMessage, $requirement->check(new RequiresIfLua($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/TarantoolRequirementTest.php similarity index 71% rename from tests/Annotation/Requirement/TarantoolVersionRequirementTest.php rename to tests/Attribute/Requirement/TarantoolRequirementTest.php index 57983dc..6095bdf 100755 --- a/tests/Annotation/Requirement/TarantoolVersionRequirementTest.php +++ b/tests/Attribute/Requirement/TarantoolRequirementTest.php @@ -11,21 +11,23 @@ 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 PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; use Tarantool\Client\Request\CallRequest; -use Tarantool\PhpUnit\Annotation\Requirement\TarantoolVersionRequirement; +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; - /** - * @dataProvider provideCheckPassesForValidConstraintsData() - */ + #[DataProvider('provideCheckPassesForValidConstraintsData')] public function testCheckPassesForValidConstraints(string $serverVersion, string $constraints) : void { $mockClient = $this->getTestDoubleClientBuilder() @@ -34,12 +36,12 @@ public function testCheckPassesForValidConstraints(string $serverVersion, string TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) ->build(); - $requirement = new TarantoolVersionRequirement($mockClient); + $requirement = new TarantoolRequirement($mockClient); - self::assertNull($requirement->check($constraints)); + self::assertNull($requirement->check(new RequiresTarantool($constraints), new Target(self::class), self::resolver())); } - public function provideCheckPassesForValidConstraintsData() : iterable + public static function provideCheckPassesForValidConstraintsData() : iterable { $v2_3_1_3 = '2.3.1-3-g878e2a42c'; @@ -95,9 +97,7 @@ public function provideCheckPassesForValidConstraintsData() : iterable ]; } - /** - * @dataProvider provideCheckFailsForInvalidConstraintsData() - */ + #[DataProvider('provideCheckFailsForInvalidConstraintsData')] public function testCheckFailsForInvalidConstraints(string $serverVersion, string $constraints) : void { $mockClient = $this->getTestDoubleClientBuilder() @@ -106,13 +106,13 @@ public function testCheckFailsForInvalidConstraints(string $serverVersion, strin TestDoubleFactory::createResponseFromData([['version' => $serverVersion]])) ->build(); - $requirement = new TarantoolVersionRequirement($mockClient); - $errorMessage = sprintf('Tarantool version %s is required', $constraints); + $requirement = new TarantoolRequirement($mockClient); + $errorMessage = \sprintf('Tarantool version %s is required', $constraints); - self::assertSame($errorMessage, $requirement->check($constraints)); + self::assertSame($errorMessage, $requirement->check(new RequiresTarantool($constraints), new Target(self::class), self::resolver())); } - public function provideCheckFailsForInvalidConstraintsData() : iterable + public static function provideCheckFailsForInvalidConstraintsData() : iterable { $v2_3_1_3 = '2.3.1-3-g878e2a42c'; @@ -148,4 +148,19 @@ public 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; + } + }; + } } diff --git a/tests/Expectation/RequestExpectationsTest.php b/tests/Expectation/RequestExpectationsTest.php index 3453ec6..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; @@ -33,7 +34,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 +48,7 @@ protected function getClient() : Client ->build(); } - public function provideRequestNames() : iterable + public static function provideRequestNames() : iterable { return [ ['Auth'], @@ -63,9 +64,7 @@ public 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;