From 700044ab8d283822495c191f8827023e45409098 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 22:59:44 +0200 Subject: [PATCH 01/20] feat: upgrade client and coverage workflow --- .gitattributes | 1 - .github/workflows/qa.yml | 49 +++-- .scrutinizer.yml | 22 -- README.md | 4 +- composer.json | 11 +- dockerfile.sh | 2 +- phpunit.xml.dist | 6 - tests/Integration/QueueTest.php | 190 +++++++----------- tests/Integration/TestCase.php | 14 +- tests/Integration/Ttl.php | 64 +++--- tests/Integration/TubePlaceholderResolver.php | 9 +- tests/PhpUnitCompat.php | 27 --- tests/Unit/TaskTest.php | 12 +- 13 files changed, 158 insertions(+), 253 deletions(-) delete mode 100644 .scrutinizer.yml delete mode 100644 tests/PhpUnitCompat.php diff --git a/.gitattributes b/.gitattributes index 021bd70..74ff349 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,7 +3,6 @@ tests export-ignore .gitattributes export-ignore .gitignore export-ignore .php_cs.dist export-ignore -.scrutinizer.yml export-ignore dockerfile.sh export-ignore phpunit.xml.dist export-ignore psalm.xml export-ignore diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index c9a5989..71954c6 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -8,6 +8,9 @@ on: jobs: tests: + permissions: + actions: read + contents: read env: TNT_IMAGE: tarantool/tarantool:2.11 PHP_IMAGE: php:8.2-cli @@ -15,16 +18,10 @@ jobs: matrix: operating-system: [ubuntu-latest] env: - - PHP_IMAGE: php:7.2-cli - - - PHP_IMAGE: php:7.3-cli + - PHP_IMAGE: php:8.2-cli QA: 1 - - PHP_IMAGE: php:7.4-cli - COVERAGE_FILE: coverage.clover - - - PHP_IMAGE: php:8.0-cli - - PHP_IMAGE: php:8.1-cli + - COVERAGE_FILE: coverage.xml - TNT_IMAGE: tarantool/tarantool:1.7 - TNT_IMAGE: tarantool/tarantool:1.9 @@ -38,6 +35,7 @@ jobs: - TNT_IMAGE: tarantool/tarantool:2.8 - TNT_IMAGE: tarantool/tarantool:2.10 + name: ${{ matrix.env.PHP_IMAGE || 'php:8.2-cli' }}, ${{ matrix.env.TNT_IMAGE || 'tarantool/tarantool:2.11' }}${{ matrix.env.QA && ', qa' || '' }}${{ matrix.env.COVERAGE_FILE && ', coverage' || '' }} runs-on: ${{ matrix.operating-system }} steps: - name: Checkout @@ -62,9 +60,36 @@ jobs: docker run --net=tarantool-php --rm -v $PWD:/queue -w /queue queue php vendor/bin/php-cs-fixer fix --dry-run --diff --verbose . docker run --net=tarantool-php --rm -v $PWD:/queue -w /queue queue php vendor/bin/psalm - - name: Upload code coverage to Scrutinizer + - name: Upload code coverage report env: ${{ matrix.env }} if: ${{ env.COVERAGE_FILE }} - run: | - curl -sSOL https://scrutinizer-ci.com/ocular.phar - docker run --rm -v $(pwd):/queue -w /queue queue php ocular.phar code-coverage:upload --format=php-clover "$COVERAGE_FILE" + uses: actions/upload-artifact@v7 + with: + name: coverage-${{ strategy.job-index }} + path: ${{ env.COVERAGE_FILE }} + + coverage: + needs: tests + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + statuses: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Download coverage reports + uses: actions/download-artifact@v8 + with: + pattern: coverage-* + path: coverage-reports + + - name: Add combined code coverage report to workflow summary + uses: getsentry/coverage-action@0.3.8 + with: + token: ${{ secrets.GITHUB_TOKEN }} + directory: coverage-reports + coverage-format: cobertura + enable-tests: false + name: full-test-matrix diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index c1a456c..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,22 +0,0 @@ -checks: - php: true - -tools: - php_mess_detector: true - php_cpd: true - php_loc: - excluded_dirs: [vendor] - php_pdepend: - excluded_dirs: [vendor] - external_code_coverage: - runs: 1 - -build: - environment: - php: - version: 7.4 - nodes: - analysis: - tests: - override: - - php-scrutinizer-run diff --git a/README.md b/README.md index fe127b7..2a9be2f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # Tarantool Queue [![Quality Assurance](https://github.com/tarantool-php/queue/workflows/QA/badge.svg)](https://github.com/tarantool-php/queue/actions?query=workflow%3AQA) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/tarantool-php/queue/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/tarantool-php/queue/?branch=master) -[![Code Coverage](https://scrutinizer-ci.com/g/tarantool-php/queue/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/tarantool-php/queue/?branch=master) [![Mentioned in Awesome PHP](https://awesome.re/mentioned-badge.svg)](https://github.com/ziadoz/awesome-php) [![Telegram](https://img.shields.io/badge/Telegram-join%20chat-blue.svg)](https://t.me/tarantool_php) @@ -28,6 +26,8 @@ Lua modules, called [LuaRocks](https://luarocks.org/). This package provides PHP ## Installation +This library requires PHP 8.2 or newer. + The recommended way to install the library is through [Composer](http://getcomposer.org): ```bash diff --git a/composer.json b/composer.json index cd247b6..f7bff94 100644 --- a/composer.json +++ b/composer.json @@ -11,13 +11,14 @@ } ], "require": { - "php": "^7.2.5|^8", - "tarantool/client": "^0.10" + "php": "^8.2", + "tarantool/client": "^0.11" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.19", - "tarantool/phpunit-extras": "^0.2", - "vimeo/psalm": "^3.9|^4" + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^10.5", + "tarantool/phpunit-extras": "^0.3.2", + "vimeo/psalm": "^5.23|^6" }, "autoload": { "psr-4": { diff --git a/dockerfile.sh b/dockerfile.sh index 0f8601e..1e940e5 100755 --- a/dockerfile.sh +++ b/dockerfile.sh @@ -25,5 +25,5 @@ ENV PATH=~/.composer/vendor/bin:\$PATH ENV TNT_LISTEN_URI=$TNT_LISTEN_URI CMD if [ ! -f composer.lock ]; then composer install; fi && \\ - vendor/bin/phpunit ${COVERAGE_FILE:+ --coverage-text --coverage-clover=}$COVERAGE_FILE + vendor/bin/phpunit ${COVERAGE_FILE:+ --coverage-text --coverage-cobertura=}$COVERAGE_FILE " diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c3102ae..82595ab 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -23,10 +23,4 @@ tests/Integration - - - - src - - diff --git a/tests/Integration/QueueTest.php b/tests/Integration/QueueTest.php index 5034f05..b48583f 100644 --- a/tests/Integration/QueueTest.php +++ b/tests/Integration/QueueTest.php @@ -1,6 +1,5 @@ @@ -13,16 +12,17 @@ namespace Tarantool\Queue\Tests\Integration; +use PHPUnit\Framework\Attributes\DataProvider; +use Tarantool\PhpUnit\Attribute\Lua; use Tarantool\Queue\States; abstract class QueueTest extends TestCase { /** - * @dataProvider provideTaskData - * @lua create_tube('%tube_name%', '%tube_type%') - * * @param mixed $data */ + #[DataProvider('provideTaskData')] + #[Lua("create_tube('%tube_name%', '%tube_type%')")] public function testPut($data) : void { $task = $this->queue->put($data); @@ -44,10 +44,8 @@ public function provideTaskData() : iterable ]; } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('peek_0') - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('peek_0')")] public function testPeek() : void { $task = $this->queue->peek(0); @@ -55,10 +53,8 @@ public function testPeek() : void self::assertTask($task, 0, States::READY, 'peek_0'); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('take') - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('take')")] public function testTake() : void { $task = $this->queue->take(); @@ -66,9 +62,7 @@ public function testTake() : void self::assertTask($task, 0, States::TAKEN, 'take'); } - /** - * @lua create_tube('%tube_name%', '%tube_type%') - */ + #[Lua("create_tube('%tube_name%', '%tube_type%')")] public function testTakeNone() : void { $time = time(); @@ -78,11 +72,9 @@ public function testTakeNone() : void self::assertNull($task); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('release_0') - * @lua tube:take() - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('release_0')")] + #[Lua('tube:take()')] public function testRelease() : void { $task = $this->queue->release(0); @@ -90,11 +82,9 @@ public function testRelease() : void self::assertTask($task, 0, States::READY, 'release_0'); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('ack_0') - * @lua tube:take() - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('ack_0')")] + #[Lua('tube:take()')] public function testAck() : void { $task = $this->queue->ack(0); @@ -102,10 +92,8 @@ public function testAck() : void self::assertTask($task, 0, States::DONE, 'ack_0'); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('bury_0') - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('bury_0')")] public function testBury() : void { $task = $this->queue->bury(0); @@ -113,11 +101,9 @@ public function testBury() : void self::assertTask($task, 0, States::BURIED, 'bury_0'); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('kick_1') - * @lua tube:bury(0) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('kick_1')")] + #[Lua('tube:bury(0)')] public function testKickOne() : void { $count = $this->queue->kick(1); @@ -125,15 +111,13 @@ public function testKickOne() : void self::assertSame(1, $count); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('kick_1') - * @lua tube:put('kick_2') - * @lua tube:put('kick_3') - * @lua tube:bury(0) - * @lua tube:bury(1) - * @lua tube:bury(2) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('kick_1')")] + #[Lua("tube:put('kick_2')")] + #[Lua("tube:put('kick_3')")] + #[Lua('tube:bury(0)')] + #[Lua('tube:bury(1)')] + #[Lua('tube:bury(2)')] public function testKickMany() : void { $count = $this->queue->kick(3); @@ -141,10 +125,8 @@ public function testKickMany() : void self::assertSame(3, $count); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('delete_0') - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('delete_0')")] public function testDelete() : void { $task = $this->queue->delete(0); @@ -152,11 +134,9 @@ public function testDelete() : void self::assertTask($task, 0, States::DONE, 'delete_0'); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('truncate_0') - * @lua tube:put('truncate_1') - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('truncate_0')")] + #[Lua("tube:put('truncate_1')")] public function testTruncate() : void { self::assertSame(2, $this->queue->stats('tasks.total')); @@ -166,9 +146,7 @@ public function testTruncate() : void self::assertSame(0, $this->queue->stats('tasks.total')); } - /** - * @lua create_tube('%tube_name%', '%tube_type%') - */ + #[Lua("create_tube('%tube_name%', '%tube_type%')")] public function testTruncateEmpty() : void { self::assertSame(0, $this->queue->stats('tasks.total')); @@ -178,23 +156,21 @@ public function testTruncateEmpty() : void self::assertSame(0, $this->queue->stats('tasks.total')); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('stat_0') - * @lua tube:put('stat_1') - * @lua tube:put('stat_2') - * @lua tube:put('stat_3') - * @lua tube:put('stat_4') - * @lua tube:delete(4) - * @lua tube:take(.001) - * @lua tube:release(0) - * @lua tube:take(.001) - * @lua tube:ack(0) - * @lua tube:bury(1) - * @lua tube:bury(2) - * @lua tube:kick(1) - * @lua tube:take(.001) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('stat_0')")] + #[Lua("tube:put('stat_1')")] + #[Lua("tube:put('stat_2')")] + #[Lua("tube:put('stat_3')")] + #[Lua("tube:put('stat_4')")] + #[Lua('tube:delete(4)')] + #[Lua('tube:take(.001)')] + #[Lua('tube:release(0)')] + #[Lua('tube:take(.001)')] + #[Lua('tube:ack(0)')] + #[Lua('tube:bury(1)')] + #[Lua('tube:bury(2)')] + #[Lua('tube:kick(1)')] + #[Lua('tube:take(.001)')] public function testStats() : void { $stats = $this->queue->stats(); @@ -226,9 +202,7 @@ public function testStats() : void ], $stats); } - /** - * @lua create_tube('%tube_name%', '%tube_type%') - */ + #[Lua("create_tube('%tube_name%', '%tube_type%')")] public function testEmptyStats() : void { $stats = $this->queue->stats(); @@ -260,31 +234,29 @@ public function testEmptyStats() : void ], $stats); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('stat_0') - * @lua tube:put('stat_1') - * @lua tube:put('stat_2') - * @lua tube:put('stat_3') - * @lua tube:put('stat_4') - * @lua tube:put('stat_5') - * @lua tube:put('stat_6') - * @lua tube:put('stat_7') - * @lua tube:put('stat_8') - * @lua tube:put('stat_9') - * @lua tube:take(.001) - * @lua tube:release(0) - * @lua tube:take(.001) - * @lua tube:ack(0) - * @lua tube:bury(1) - * @lua tube:bury(2) - * @lua tube:bury(3) - * @lua tube:kick(1) - * @lua tube:delete(6) - * @lua tube:delete(7) - * @lua tube:delete(8) - * @lua tube:take(.001) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('stat_0')")] + #[Lua("tube:put('stat_1')")] + #[Lua("tube:put('stat_2')")] + #[Lua("tube:put('stat_3')")] + #[Lua("tube:put('stat_4')")] + #[Lua("tube:put('stat_5')")] + #[Lua("tube:put('stat_6')")] + #[Lua("tube:put('stat_7')")] + #[Lua("tube:put('stat_8')")] + #[Lua("tube:put('stat_9')")] + #[Lua('tube:take(.001)')] + #[Lua('tube:release(0)')] + #[Lua('tube:take(.001)')] + #[Lua('tube:ack(0)')] + #[Lua('tube:bury(1)')] + #[Lua('tube:bury(2)')] + #[Lua('tube:bury(3)')] + #[Lua('tube:kick(1)')] + #[Lua('tube:delete(6)')] + #[Lua('tube:delete(7)')] + #[Lua('tube:delete(8)')] + #[Lua('tube:take(.001)')] public function testStatsPath() : void { self::assertEquals([ @@ -327,10 +299,8 @@ public function testStatsPath() : void self::assertSame(3, $this->queue->stats('calls.bury')); } - /** - * @dataProvider provideStatsInvalidPathData - * @lua create_tube('%tube_name%', '%tube_type%') - */ + #[DataProvider('provideStatsInvalidPathData')] + #[Lua("create_tube('%tube_name%', '%tube_type%')")] public function testStatsInvalidPath(?string $path) : void { $this->expectException(\InvalidArgumentException::class); @@ -354,10 +324,8 @@ public function provideStatsInvalidPathData() : iterable ]; } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube.pow = function(self, base, exp) return math.pow(base, exp) end - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua('tube.pow = function(self, base, exp) return math.pow(base, exp) end')] public function testCall() : void { $result = $this->queue->call('pow', 2, 8); @@ -365,9 +333,7 @@ public function testCall() : void self::assertSame(256, $result[0]); } - /** - * @dataProvider provideFailureCallbackData - */ + #[DataProvider('provideFailureCallbackData')] public function testThrowException(string $methodName, array $args) : void { $this->expectException(\Throwable::class); diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php index 09c7e7c..42acf03 100644 --- a/tests/Integration/TestCase.php +++ b/tests/Integration/TestCase.php @@ -13,17 +13,15 @@ namespace Tarantool\Queue\Tests\Integration; -use PHPUnitExtras\Annotation\AnnotationProcessorBuilder; +use PHPUnit\Framework\Attributes\Before; +use PHPUnitExtras\Attribute\AttributeProcessorBuilder; use Tarantool\Client\Client; use Tarantool\PhpUnit\TestCase as BaseTestCase; use Tarantool\Queue\Queue; use Tarantool\Queue\Task; -use Tarantool\Queue\Tests\PhpUnitCompat; abstract class TestCase extends BaseTestCase { - use PhpUnitCompat; - /** @var Client|null */ private $client; @@ -71,15 +69,13 @@ final protected function getClient() : Client return $this->client = Client::fromDsn($dsn); } - final protected function createAnnotationProcessorBuilder() : AnnotationProcessorBuilder + final protected function createAttributeProcessorBuilder() : AttributeProcessorBuilder { - return parent::createAnnotationProcessorBuilder() + return parent::createAttributeProcessorBuilder() ->addPlaceholderResolver(new TubePlaceholderResolver($this)); } - /** - * @before - */ + #[Before] final protected function initQueue() : void { $this->queue = new Queue($this->getClient(), $this->getQueueName()); diff --git a/tests/Integration/Ttl.php b/tests/Integration/Ttl.php index ef2850a..c741f58 100644 --- a/tests/Integration/Ttl.php +++ b/tests/Integration/Ttl.php @@ -1,6 +1,5 @@ @@ -13,15 +12,14 @@ namespace Tarantool\Queue\Tests\Integration; +use Tarantool\PhpUnit\Attribute\Lua; use Tarantool\Queue\Queue; use Tarantool\Queue\States; trait Ttl { - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('ttr_1', {ttr = 1}) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('ttr_1', {ttr = 1})")] public function testTimeToRun() : void { $queue = $this->getQueue(); @@ -35,10 +33,8 @@ public function testTimeToRun() : void self::assertEquals($task1, $task2); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('ttl_1', {ttl = 1}) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('ttl_1', {ttl = 1})")] public function testTimeToLive() : void { sleep(1); @@ -47,10 +43,8 @@ public function testTimeToLive() : void self::assertNull($task); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('touch_ttr_1', {ttr = 1}) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('touch_ttr_1', {ttr = 1})")] public function testTouchTimeToRun() : void { $queue = $this->getQueue(); @@ -66,10 +60,8 @@ public function testTouchTimeToRun() : void self::assertNull($task3); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('touch_ttl_1', {ttl = 1}) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('touch_ttl_1', {ttl = 1})")] public function testTouchTimeToLive() : void { $queue = $this->getQueue(); @@ -86,10 +78,8 @@ public function testTouchTimeToLive() : void self::assertEquals($task2, $task3); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('touch_invalid_interval') - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('touch_invalid_interval')")] public function testTouchInvalidInterval() : void { $queue = $this->getQueue(); @@ -100,11 +90,9 @@ public function testTouchInvalidInterval() : void } } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('pri_low', {pri = 2}) - * @lua tube:put('pri_high', {pri = 1}) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('pri_low', {pri = 2})")] + #[Lua("tube:put('pri_high', {pri = 1})")] public function testPriority() : void { $queue = $this->getQueue(); @@ -119,10 +107,8 @@ public function testPriority() : void self::assertSame('pri_low', $task2->getData()); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('delay_1', {delay = 1}) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('delay_1', {delay = 1})")] public function testDelay() : void { $queue = $this->getQueue(); @@ -136,11 +122,9 @@ public function testDelay() : void self::assertTask($task, 0, States::TAKEN, 'delay_1'); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('release_0') - * @lua tube:take() - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('release_0')")] + #[Lua('tube:take()')] public function testDelayedRelease() : void { $queue = $this->getQueue(); @@ -156,11 +140,9 @@ public function testDelayedRelease() : void self::assertTask($task, 0, States::TAKEN, 'release_0'); } - /** - * @lua tube = create_tube('%tube_name%', '%tube_type%') - * @lua tube:put('stat_delayed_0', {delay = 9999}) - * @lua tube:put('stat_delayed_1', {delay = 9999}) - */ + #[Lua("tube = create_tube('%tube_name%', '%tube_type%')")] + #[Lua("tube:put('stat_delayed_0', {delay = 9999})")] + #[Lua("tube:put('stat_delayed_1', {delay = 9999})")] public function testStatsDelayed() : void { $count = $this->getQueue()->stats('tasks.delayed'); diff --git a/tests/Integration/TubePlaceholderResolver.php b/tests/Integration/TubePlaceholderResolver.php index 2cadeda..c28ae76 100644 --- a/tests/Integration/TubePlaceholderResolver.php +++ b/tests/Integration/TubePlaceholderResolver.php @@ -13,8 +13,8 @@ namespace Tarantool\Queue\Tests\Integration; -use PHPUnitExtras\Annotation\PlaceholderResolver\PlaceholderResolver; -use PHPUnitExtras\Annotation\Target; +use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver; +use PHPUnitExtras\Attribute\Target; final class TubePlaceholderResolver implements PlaceholderResolver { @@ -25,11 +25,6 @@ public function __construct(TestCase $testCase) $this->testCase = $testCase; } - public function getName() : string - { - return 'tube'; - } - public function resolve(string $value, Target $target) : string { return strtr($value, [ diff --git a/tests/PhpUnitCompat.php b/tests/PhpUnitCompat.php deleted file mode 100644 index 1f2950d..0000000 --- a/tests/PhpUnitCompat.php +++ /dev/null @@ -1,27 +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\Queue\Tests; - -/** - * A compatibility layer for the legacy PHPUnit 7. - */ -trait PhpUnitCompat -{ - public function expectExceptionMessageMatches(string $regularExpression) : void - { - \is_callable(parent::class.'::expectExceptionMessageMatches') - ? parent::expectExceptionMessageMatches(...func_get_args()) - : parent::expectExceptionMessageRegExp(...func_get_args()); - } -} diff --git a/tests/Unit/TaskTest.php b/tests/Unit/TaskTest.php index 551b2a7..f6e47f0 100644 --- a/tests/Unit/TaskTest.php +++ b/tests/Unit/TaskTest.php @@ -1,6 +1,5 @@ @@ -13,15 +12,14 @@ namespace Tarantool\Queue\Tests\Unit; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Tarantool\Queue\States; use Tarantool\Queue\Task; final class TaskTest extends TestCase { - /** - * @dataProvider provideTuples - */ + #[DataProvider('provideTuples')] public function testCreateFromTuple(array $tuple) : void { $task = Task::fromTuple($tuple); @@ -43,9 +41,7 @@ public function provideTuples() : iterable ]; } - /** - * @dataProvider provideStates - */ + #[DataProvider('provideStates')] public function testIsser(string $state) : void { static $map = [ From d2e4547f1d38c30b19eb4413592c6a0b9ce773bf Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:03:36 +0200 Subject: [PATCH 02/20] fix: restore resolver name method --- tests/Integration/TubePlaceholderResolver.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Integration/TubePlaceholderResolver.php b/tests/Integration/TubePlaceholderResolver.php index c28ae76..daad719 100644 --- a/tests/Integration/TubePlaceholderResolver.php +++ b/tests/Integration/TubePlaceholderResolver.php @@ -25,6 +25,11 @@ public function __construct(TestCase $testCase) $this->testCase = $testCase; } + public function getName() : string + { + return 'tube'; + } + public function resolve(string $value, Target $target) : string { return strtr($value, [ From 4ce53778ddc3a1f9c7c49dd828d3d6cccf2661e8 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:06:36 +0200 Subject: [PATCH 03/20] fix: use PHPUnit 10 test name accessor --- tests/Integration/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php index 42acf03..81ca66e 100644 --- a/tests/Integration/TestCase.php +++ b/tests/Integration/TestCase.php @@ -30,7 +30,7 @@ abstract class TestCase extends BaseTestCase public function getQueueName() : string { - $methodName = $this->getName(false); + $methodName = $this->name(); if (0 === strpos($methodName, 'test')) { $methodName = substr($methodName, 4); } From d5b0c9aebea6b50780d9356305822dbc2bb79a7e Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:08:18 +0200 Subject: [PATCH 04/20] ci: avoid duplicate runs for pull request pushes --- .github/workflows/qa.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 71954c6..77b1175 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -1,6 +1,8 @@ name: QA on: push: + branches: + - master pull_request: workflow_dispatch: schedule: From afd9c7c6bce1d0ccecf0a235b576a883e8631dd7 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:31:19 +0200 Subject: [PATCH 05/20] test: allow optional empty extra stats --- tests/Integration/QueueTest.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/Integration/QueueTest.php b/tests/Integration/QueueTest.php index b48583f..13572ef 100644 --- a/tests/Integration/QueueTest.php +++ b/tests/Integration/QueueTest.php @@ -174,6 +174,10 @@ public function testTruncateEmpty() : void public function testStats() : void { $stats = $this->queue->stats(); + if (\array_key_exists('extra', $stats)) { + self::assertSame([], $stats['extra']); + unset($stats['extra']); + } self::assertEquals([ 'tasks' => [ @@ -197,8 +201,6 @@ public function testStats() : void 'delay' => 0, 'ttl' => 0, ], - 'extra' => [ - ], ], $stats); } @@ -206,6 +208,10 @@ public function testStats() : void public function testEmptyStats() : void { $stats = $this->queue->stats(); + if (\array_key_exists('extra', $stats)) { + self::assertSame([], $stats['extra']); + unset($stats['extra']); + } self::assertEquals([ 'tasks' => [ @@ -229,8 +235,6 @@ public function testEmptyStats() : void 'delay' => 0, 'ttl' => 0, ], - 'extra' => [ - ], ], $stats); } @@ -341,7 +345,7 @@ public function testThrowException(string $methodName, array $args) : void $this->queue->$methodName(...$args); } - public function provideFailureCallbackData() : iterable + public function provideFailureCallbackData() : array { return [ ['ack', [42]], From 0baee2afbf35abbf8711281d7f96cb2e234f9249 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:37:48 +0200 Subject: [PATCH 06/20] fix: fail CI only on actual test errors --- .gitattributes | 2 +- .gitignore | 1 + .php_cs.dist => .php-cs-fixer.dist.php | 29 ++++++++++++++---- phpunit.xml.dist | 41 +++++++++++--------------- src/Queue.php | 10 ++----- src/Task.php | 7 ----- tests/Integration/QueueTest.php | 16 +++++----- tests/Integration/TestCase.php | 6 ++-- tests/Integration/Ttl.php | 3 +- tests/Unit/TaskTest.php | 7 +++-- 10 files changed, 60 insertions(+), 62 deletions(-) rename .php_cs.dist => .php-cs-fixer.dist.php (76%) diff --git a/.gitattributes b/.gitattributes index 74ff349..31eff10 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,7 +2,7 @@ tests export-ignore .gitattributes export-ignore .gitignore export-ignore -.php_cs.dist export-ignore +.php-cs-fixer.dist.php export-ignore dockerfile.sh export-ignore phpunit.xml.dist export-ignore psalm.xml export-ignore diff --git a/.gitignore b/.gitignore index d9db3c6..ed4279c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /vendor/ /.phpunit.result.cache +/.phpunit.cache/ /composer.lock /coverage* /phpunit.xml diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 76% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index 6c1e197..0fe3237 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -5,22 +5,34 @@ namespace Tarantool\Queue; use PhpCsFixer\Config; +use PhpCsFixer\Fixer\ConfigurableFixerInterface; use PhpCsFixer\Fixer\ConstantNotation\NativeConstantInvocationFixer; -use PhpCsFixer\Fixer\FixerInterface; use PhpCsFixer\Fixer\FunctionNotation\NativeFunctionInvocationFixer; +use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface; +use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Tokenizer\Tokens; -final class FilterableFixer implements FixerInterface +final class FilterableFixer implements ConfigurableFixerInterface { - private $fixer; - private $pathRegex; + private ConfigurableFixerInterface $fixer; + private string $pathRegex; - public function __construct(FixerInterface $fixer, string $pathRegex) + public function __construct(ConfigurableFixerInterface $fixer, string $pathRegex) { $this->fixer = $fixer; $this->pathRegex = $pathRegex; } + public function configure(array $configuration) : void + { + $this->fixer->configure($configuration); + } + + public function getConfigurationDefinition() : FixerConfigurationResolverInterface + { + return $this->fixer->getConfigurationDefinition(); + } + public function isCandidate(Tokens $tokens) : bool { return $this->fixer->isCandidate($tokens); @@ -36,6 +48,11 @@ public function fix(\SplFileInfo $file, Tokens $tokens) : void $this->fixer->fix($file, $tokens); } + public function getDefinition() : FixerDefinitionInterface + { + return $this->fixer->getDefinition(); + } + public function getName() : string { return (new \ReflectionClass($this))->getShortName().'/'.$this->fixer->getName(); @@ -65,7 +82,7 @@ public function supports(\SplFileInfo $file) : bool file that was distributed with this source code. EOF; -return Config::create() +return (new Config()) ->setUsingCache(false) ->setRiskyAllowed(true) ->registerCustomFixers([ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 82595ab..4a5366c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,26 +1,19 @@ - - - - - - - - - - - - - - tests/Unit - - - - tests/Integration - - + + + + + + + + + + + tests/Unit + + + tests/Integration + tests/Integration/QueueTest.php + + diff --git a/src/Queue.php b/src/Queue.php index 152f9ae..2c37a73 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -36,9 +36,6 @@ public function getName() : string return $this->name; } - /** - * @param mixed $data - */ public function put($data, array $options = []) : Task { return Task::fromTuple( @@ -46,7 +43,7 @@ public function put($data, array $options = []) : Task ); } - public function take(float $timeout = null) : ?Task + public function take(?float $timeout = null) : ?Task { $result = $this->client->call("queue.tube.$this->name:take", $timeout); @@ -118,7 +115,7 @@ public function stats(?string $path = null) return $stats; } - foreach (\explode('.', $path) as $key) { + foreach (explode('.', $path) as $key) { if (!isset($stats[$key])) { throw new \InvalidArgumentException(\sprintf('Invalid path "%s"', $path)); } @@ -128,9 +125,6 @@ public function stats(?string $path = null) return $stats; } - /** - * @param mixed ...$args - */ public function call(string $methodName, ...$args) : array { return $this->client->call("queue.tube.$this->name:$methodName", ...$args); diff --git a/src/Task.php b/src/Task.php index 264771d..4760efd 100644 --- a/src/Task.php +++ b/src/Task.php @@ -18,12 +18,8 @@ final class Task private $id; private $state; - /** @var mixed */ private $data; - /** - * @param mixed $data - */ private function __construct(int $id, string $state, $data) { $this->id = $id; @@ -48,9 +44,6 @@ public function getState() : string return $this->state; } - /** - * @return mixed - */ public function getData() { return $this->data; diff --git a/tests/Integration/QueueTest.php b/tests/Integration/QueueTest.php index 13572ef..97795fc 100644 --- a/tests/Integration/QueueTest.php +++ b/tests/Integration/QueueTest.php @@ -1,5 +1,6 @@ @@ -18,9 +19,6 @@ abstract class QueueTest extends TestCase { - /** - * @param mixed $data - */ #[DataProvider('provideTaskData')] #[Lua("create_tube('%tube_name%', '%tube_type%')")] public function testPut($data) : void @@ -30,7 +28,7 @@ public function testPut($data) : void self::assertTask($task, 0, States::READY, $data); } - public function provideTaskData() : iterable + public static function provideTaskData() : iterable { return [ [null], @@ -200,7 +198,7 @@ public function testStats() : void 'ttr' => 0, 'delay' => 0, 'ttl' => 0, - ], + ], ], $stats); } @@ -234,7 +232,7 @@ public function testEmptyStats() : void 'ttr' => 0, 'delay' => 0, 'ttl' => 0, - ], + ], ], $stats); } @@ -313,7 +311,7 @@ public function testStatsInvalidPath(?string $path) : void $this->queue->stats($path); } - public function provideStatsInvalidPathData() : iterable + public static function provideStatsInvalidPathData() : iterable { return [ [''], @@ -345,7 +343,7 @@ public function testThrowException(string $methodName, array $args) : void $this->queue->$methodName(...$args); } - public function provideFailureCallbackData() : array + public static function provideFailureCallbackData() : array { return [ ['ack', [42]], diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php index 81ca66e..a88b503 100644 --- a/tests/Integration/TestCase.php +++ b/tests/Integration/TestCase.php @@ -31,7 +31,7 @@ abstract class TestCase extends BaseTestCase public function getQueueName() : string { $methodName = $this->name(); - if (0 === strpos($methodName, 'test')) { + if (str_starts_with($methodName, 'test')) { $methodName = substr($methodName, 4); } @@ -56,9 +56,9 @@ final protected function getClient() : Client return $this->client = Client::fromDefaults(); } - if (0 === strpos($uri, '/')) { + if (str_starts_with($uri, '/')) { $dsn = 'unix://'.$uri; - } elseif (0 === strpos($uri, 'unix/:')) { + } elseif (str_starts_with($uri, 'unix/:')) { $dsn = 'unix://'.substr($uri, 6); } elseif (ctype_digit($uri)) { $dsn = 'tcp://127.0.0.1:'.$uri; diff --git a/tests/Integration/Ttl.php b/tests/Integration/Ttl.php index c741f58..e9fe409 100644 --- a/tests/Integration/Ttl.php +++ b/tests/Integration/Ttl.php @@ -1,5 +1,6 @@ diff --git a/tests/Unit/TaskTest.php b/tests/Unit/TaskTest.php index f6e47f0..f7ef792 100644 --- a/tests/Unit/TaskTest.php +++ b/tests/Unit/TaskTest.php @@ -1,5 +1,6 @@ @@ -32,7 +33,7 @@ public function testCreateFromTuple(array $tuple) : void } } - public function provideTuples() : iterable + public static function provideTuples() : iterable { return [ [[0, States::READY, [42]]], @@ -64,7 +65,7 @@ public function testIsser(string $state) : void } } - public function provideStates() : iterable + public static function provideStates() : iterable { return [ [States::READY], From 04a3a766bf9e56404bb9d44fe29df789a969d123 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:42:38 +0200 Subject: [PATCH 07/20] fix: configure PHPUnit coverage source --- phpunit.xml.dist | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4a5366c..683bb91 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,4 +16,9 @@ tests/Integration/QueueTest.php + + + src + + From 4d7dd1e35f10b13d3d857ca13aadbe8fa378fa6c Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:46:54 +0200 Subject: [PATCH 08/20] fix: mark unused public API as informational in Psalm --- psalm.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/psalm.xml b/psalm.xml index 30258a7..44ba4d8 100644 --- a/psalm.xml +++ b/psalm.xml @@ -6,6 +6,12 @@ xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" > + + + + + + From 7a1a085fd8a1042d79b42949c4ddda9bcd52cfbb Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:50:27 +0200 Subject: [PATCH 09/20] ci: expand PHP and Tarantool test matrix --- .github/workflows/qa.yml | 46 ++++++++++++++++++++++++------------ tests/Integration/queues.lua | 4 ++-- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 77b1175..6b79208 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -17,31 +17,47 @@ jobs: TNT_IMAGE: tarantool/tarantool:2.11 PHP_IMAGE: php:8.2-cli strategy: + fail-fast: false + max-parallel: 10 matrix: - operating-system: [ubuntu-latest] + operating-system: [ubuntu-24.04] env: + - TNT_IMAGE: tarantool/tarantool:3.7 + - TNT_IMAGE: tarantool/tarantool:3.6 + - TNT_IMAGE: tarantool/tarantool:3.5 + - TNT_IMAGE: tarantool/tarantool:3.4 + - TNT_IMAGE: tarantool/tarantool:3.3 + - TNT_IMAGE: tarantool/tarantool:3.2 + - TNT_IMAGE: tarantool/tarantool:3.1 + - TNT_IMAGE: tarantool/tarantool:3.0 + - TNT_IMAGE: tarantool/tarantool:2.11 + - TNT_IMAGE: tarantool/tarantool:2.10 + - TNT_IMAGE: tarantool/tarantool:2.8 + - TNT_IMAGE: tarantool/tarantool:2.7 + - TNT_IMAGE: tarantool/tarantool:2.6 + - TNT_IMAGE: tarantool/tarantool:2.5 + - TNT_IMAGE: tarantool/tarantool:2.4 + - TNT_IMAGE: tarantool/tarantool:2.3 + - TNT_IMAGE: tarantool/tarantool:2.2 + - TNT_IMAGE: tarantool/tarantool:2.1 + - TNT_IMAGE: tarantool/tarantool:1 + - TNT_IMAGE: tarantool/tarantool:1.9 + - TNT_IMAGE: tarantool/tarantool:1.7 + + - PHP_IMAGE: php:8.5-cli + - PHP_IMAGE: php:8.4-cli + - PHP_IMAGE: php:8.2-cli QA: 1 - COVERAGE_FILE: coverage.xml - - - TNT_IMAGE: tarantool/tarantool:1.7 - - TNT_IMAGE: tarantool/tarantool:1.9 - - TNT_IMAGE: tarantool/tarantool:1 - - TNT_IMAGE: tarantool/tarantool:2.1 - - TNT_IMAGE: tarantool/tarantool:2.3 - - TNT_IMAGE: tarantool/tarantool:2.4 - - TNT_IMAGE: tarantool/tarantool:2.5 - - TNT_IMAGE: tarantool/tarantool:2.6 - - TNT_IMAGE: tarantool/tarantool:2.7 - - TNT_IMAGE: tarantool/tarantool:2.8 - - TNT_IMAGE: tarantool/tarantool:2.10 + PHP_IMAGE: php:8.3-cli name: ${{ matrix.env.PHP_IMAGE || 'php:8.2-cli' }}, ${{ matrix.env.TNT_IMAGE || 'tarantool/tarantool:2.11' }}${{ matrix.env.QA && ', qa' || '' }}${{ matrix.env.COVERAGE_FILE && ', coverage' || '' }} runs-on: ${{ matrix.operating-system }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v7 - name: Build docker image env: ${{ matrix.env }} @@ -79,7 +95,7 @@ jobs: statuses: write steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v7 - name: Download coverage reports uses: actions/download-artifact@v8 diff --git a/tests/Integration/queues.lua b/tests/Integration/queues.lua index 7388bf9..f39f016 100755 --- a/tests/Integration/queues.lua +++ b/tests/Integration/queues.lua @@ -5,8 +5,8 @@ local listen = os.getenv('TNT_LISTEN_URI') box.cfg { listen = (listen == '' or listen == nil) and 3301 or listen, log_level = 6, - wal_mode = 'none', - snap_dir = '/tmp' + memtx_dir = '/tmp', + wal_dir = '/tmp' } box.schema.user.grant('guest', 'read,write,execute,create,drop,alter', 'universe', nil, {if_not_exists = true}) From 14f373a600edc50b45c51292113f91763629b289 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Thu, 22 Oct 2026 23:52:02 +0200 Subject: [PATCH 10/20] ci: resolve Tarantool executable from PATH --- .github/workflows/qa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 6b79208..42e0c13 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -67,7 +67,7 @@ jobs: env: ${{ matrix.env }} run: | docker network create tarantool-php - docker run --net=tarantool-php --rm $TNT_IMAGE /usr/local/bin/tarantool --version + docker run --net=tarantool-php --rm $TNT_IMAGE sh -c "tarantool --version" docker run -d --net=tarantool-php --name=tarantool -v `pwd`:/queue -e TNT_LISTEN_URI=$TNT_LISTEN_URI ${TNT_IMAGE} tarantool /queue/tests/Integration/queues.lua docker run --rm --net=tarantool-php -v `pwd`:/queue -w /queue queue From b06d1837e887e6e91a5ab02d5af0be22cc01044d Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 00:13:09 +0200 Subject: [PATCH 11/20] ci: install queue rock for Tarantool 3 --- .github/workflows/qa.yml | 16 +++++++++++++--- tests/Integration/Dockerfile | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 tests/Integration/Dockerfile diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 42e0c13..511d9f1 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -61,14 +61,24 @@ jobs: - name: Build docker image env: ${{ matrix.env }} - run: ./dockerfile.sh | tee /dev/tty | docker build -t queue - + run: | + ./dockerfile.sh | tee /dev/tty | docker build -t queue - + docker build \ + --build-arg TNT_IMAGE="$TNT_IMAGE" \ + -f tests/Integration/Dockerfile \ + -t queue-tarantool . - name: Test env: ${{ matrix.env }} run: | docker network create tarantool-php - docker run --net=tarantool-php --rm $TNT_IMAGE sh -c "tarantool --version" - docker run -d --net=tarantool-php --name=tarantool -v `pwd`:/queue -e TNT_LISTEN_URI=$TNT_LISTEN_URI ${TNT_IMAGE} tarantool /queue/tests/Integration/queues.lua + docker run --net=tarantool-php --rm queue-tarantool tarantool --version + docker run -d \ + --net=tarantool-php \ + --name=tarantool \ + -v "$PWD:/queue" \ + -e TNT_LISTEN_URI="$TNT_LISTEN_URI" \ + queue-tarantool tarantool /queue/tests/Integration/queues.lua docker run --rm --net=tarantool-php -v `pwd`:/queue -w /queue queue - name: Check code quality diff --git a/tests/Integration/Dockerfile b/tests/Integration/Dockerfile new file mode 100644 index 0000000..82b8653 --- /dev/null +++ b/tests/Integration/Dockerfile @@ -0,0 +1,4 @@ +ARG TNT_IMAGE +FROM ${TNT_IMAGE} + +RUN tarantool -e "require('queue')" >/dev/null 2>&1 || tt rocks install queue From 59e226f51a90ecdccc4544c39d2a274a7df71a29 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 00:49:21 +0200 Subject: [PATCH 12/20] ci: install Tarantool on runner --- .github/workflows/qa.yml | 103 +++++++++++++++-------------------- tests/Integration/Dockerfile | 4 -- 2 files changed, 45 insertions(+), 62 deletions(-) delete mode 100644 tests/Integration/Dockerfile diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 511d9f1..8c574a6 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -14,87 +14,74 @@ jobs: actions: read contents: read env: - TNT_IMAGE: tarantool/tarantool:2.11 - PHP_IMAGE: php:8.2-cli + TNT_LISTEN_URI: localhost:3301 strategy: fail-fast: false max-parallel: 10 matrix: operating-system: [ubuntu-24.04] - env: - - TNT_IMAGE: tarantool/tarantool:3.7 - - TNT_IMAGE: tarantool/tarantool:3.6 - - TNT_IMAGE: tarantool/tarantool:3.5 - - TNT_IMAGE: tarantool/tarantool:3.4 - - TNT_IMAGE: tarantool/tarantool:3.3 - - TNT_IMAGE: tarantool/tarantool:3.2 - - TNT_IMAGE: tarantool/tarantool:3.1 - - TNT_IMAGE: tarantool/tarantool:3.0 - - TNT_IMAGE: tarantool/tarantool:2.11 - - TNT_IMAGE: tarantool/tarantool:2.10 - - TNT_IMAGE: tarantool/tarantool:2.8 - - TNT_IMAGE: tarantool/tarantool:2.7 - - TNT_IMAGE: tarantool/tarantool:2.6 - - TNT_IMAGE: tarantool/tarantool:2.5 - - TNT_IMAGE: tarantool/tarantool:2.4 - - TNT_IMAGE: tarantool/tarantool:2.3 - - TNT_IMAGE: tarantool/tarantool:2.2 - - TNT_IMAGE: tarantool/tarantool:2.1 - - TNT_IMAGE: tarantool/tarantool:1 - - TNT_IMAGE: tarantool/tarantool:1.9 - - TNT_IMAGE: tarantool/tarantool:1.7 - - - PHP_IMAGE: php:8.5-cli - - PHP_IMAGE: php:8.4-cli - - - PHP_IMAGE: php:8.2-cli - QA: 1 - - - COVERAGE_FILE: coverage.xml - PHP_IMAGE: php:8.3-cli + tarantool-release: ['2', '3'] + php-version: ['8.2', '8.3', '8.4', '8.5'] + include: + - php-version: '8.2' + QA: true + - php-version: '8.3' + tarantool-release: '3' + COVERAGE: true - name: ${{ matrix.env.PHP_IMAGE || 'php:8.2-cli' }}, ${{ matrix.env.TNT_IMAGE || 'tarantool/tarantool:2.11' }}${{ matrix.env.QA && ', qa' || '' }}${{ matrix.env.COVERAGE_FILE && ', coverage' || '' }} + name: PHP ${{ matrix.php-version }}, Tarantool ${{ matrix.tarantool-release }}${{ matrix.QA && ', qa' || '' }}${{ matrix.COVERAGE && ', coverage' || '' }} runs-on: ${{ matrix.operating-system }} steps: - name: Checkout uses: actions/checkout@v7 - - name: Build docker image - env: ${{ matrix.env }} + - name: Install Tarantool and tt + env: + TNT_RELEASE: ${{ matrix.tarantool-release }} run: | - ./dockerfile.sh | tee /dev/tty | docker build -t queue - - docker build \ - --build-arg TNT_IMAGE="$TNT_IMAGE" \ - -f tests/Integration/Dockerfile \ - -t queue-tarantool . + curl -L "https://tarantool.io/release/$TNT_RELEASE/installer.sh" | sudo bash + sudo apt-get install -y tarantool tt + tarantool --version + tt version + + - name: Install the tarantool/queue module + run: tt rocks install queue + + - name: Build PHP image + env: + PHP_IMAGE: php:${{ matrix.php-version }}-cli + TNT_LISTEN_URI: localhost:3301 + COVERAGE_FILE: ${{ matrix.COVERAGE && 'coverage.xml' || '' }} + run: ./dockerfile.sh | tee /dev/tty | docker build -t queue - - name: Test - env: ${{ matrix.env }} run: | - docker network create tarantool-php - docker run --net=tarantool-php --rm queue-tarantool tarantool --version - docker run -d \ - --net=tarantool-php \ - --name=tarantool \ - -v "$PWD:/queue" \ - -e TNT_LISTEN_URI="$TNT_LISTEN_URI" \ - queue-tarantool tarantool /queue/tests/Integration/queues.lua - docker run --rm --net=tarantool-php -v `pwd`:/queue -w /queue queue + tarantool ./tests/Integration/queues.lua > tarantool.log 2>&1 & + for attempt in $(seq 1 30); do + if bash -c 'echo > /dev/tcp/127.0.0.1/3301' 2>/dev/null; then + ready=1 + break + fi + sleep 1 + done + if [ "${ready:-}" != 1 ]; then + cat tarantool.log + exit 1 + fi + docker run --rm --network=host -v "$PWD:/queue" -w /queue queue - name: Check code quality - env: ${{ matrix.env }} - if: ${{ env.QA }} + if: ${{ matrix.QA }} run: | - docker run --net=tarantool-php --rm -v $PWD:/queue -w /queue queue php vendor/bin/php-cs-fixer fix --dry-run --diff --verbose . - docker run --net=tarantool-php --rm -v $PWD:/queue -w /queue queue php vendor/bin/psalm + docker run --rm --network=host -v "$PWD:/queue" -w /queue queue php vendor/bin/php-cs-fixer fix --dry-run --diff --verbose . + docker run --rm --network=host -v "$PWD:/queue" -w /queue queue php vendor/bin/psalm - name: Upload code coverage report - env: ${{ matrix.env }} - if: ${{ env.COVERAGE_FILE }} + if: ${{ matrix.COVERAGE }} uses: actions/upload-artifact@v7 with: name: coverage-${{ strategy.job-index }} - path: ${{ env.COVERAGE_FILE }} + path: coverage.xml coverage: needs: tests diff --git a/tests/Integration/Dockerfile b/tests/Integration/Dockerfile deleted file mode 100644 index 82b8653..0000000 --- a/tests/Integration/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -ARG TNT_IMAGE -FROM ${TNT_IMAGE} - -RUN tarantool -e "require('queue')" >/dev/null 2>&1 || tt rocks install queue From 529c3e0a85e8c8f22c4c0208043f074019a0b1c7 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 00:51:21 +0200 Subject: [PATCH 13/20] ci: install Tarantool development headers --- .github/workflows/qa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 8c574a6..37d0c28 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -40,7 +40,7 @@ jobs: TNT_RELEASE: ${{ matrix.tarantool-release }} run: | curl -L "https://tarantool.io/release/$TNT_RELEASE/installer.sh" | sudo bash - sudo apt-get install -y tarantool tt + sudo apt-get install -y tarantool tarantool-dev tt tarantool --version tt version From 0263460ad7bbf17d922aa76d694e179d82604005 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 00:53:15 +0200 Subject: [PATCH 14/20] ci: run quality checks once --- .github/workflows/qa.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 37d0c28..a387d04 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -24,6 +24,7 @@ jobs: php-version: ['8.2', '8.3', '8.4', '8.5'] include: - php-version: '8.2' + tarantool-release: '3' QA: true - php-version: '8.3' tarantool-release: '3' From d7686bea768b8e91059ad1ffe7ffeba248b32d7e Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 00:54:15 +0200 Subject: [PATCH 15/20] ci: cancel superseded QA runs --- .github/workflows/qa.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index a387d04..4377f91 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -8,6 +8,10 @@ on: schedule: - cron: '40 2 * * *' +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: tests: permissions: From d16e84cb108bdd24b76ab5b87d0028e6a9f91871 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 01:02:50 +0200 Subject: [PATCH 16/20] docs: recommend tt in README --- README.md | 78 +++++++++++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 2a9be2f..142c939 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,15 @@ [![Mentioned in Awesome PHP](https://awesome.re/mentioned-badge.svg)](https://github.com/ziadoz/awesome-php) [![Telegram](https://img.shields.io/badge/Telegram-join%20chat-blue.svg)](https://t.me/tarantool_php) -[Tarantool](https://www.tarantool.io/en/developers/) is a NoSQL database running in a Lua application server. It integrates -Lua modules, called [LuaRocks](https://luarocks.org/). This package provides PHP bindings for +[Tarantool](https://www.tarantool.io/en/developers/) is a NoSQL database and Lua application server. It supports Lua +modules, which can be installed with [LuaRocks](https://luarocks.org/). This package provides PHP bindings for the [Tarantool Queue LuaRock](https://github.com/tarantool/queue/). ## Table of contents * [Installation](#installation) - * [Before start](#before-start) + * [Before you start](#before-you-start) * [Working with queue](#working-with-queue) * [Data types](#data-types) * [Tasks](#tasks) @@ -26,8 +26,6 @@ Lua modules, called [LuaRocks](https://luarocks.org/). This package provides PHP ## Installation -This library requires PHP 8.2 or newer. - The recommended way to install the library is through [Composer](http://getcomposer.org): ```bash @@ -35,10 +33,9 @@ composer require tarantool/queue ``` -## Before start +## Before you start -In order to use queue, you first need to make sure that your Tarantool instance -is configured, up and running. The minimal required configuration might look like this: +To use the queue, make sure your Tarantool instance is configured and running. A minimal configuration looks like this: ```lua -- queues.lua @@ -52,17 +49,17 @@ queue.create_tube('foobar', 'fifottl', {if_not_exists = true}) > *You can read more about the box configuration in the official Tarantool [documentation](http://tarantool.org/doc/book/configuration/index.html#initialization-file). > More information on queue configuration can be found [here](https://github.com/tarantool/queue/blob/master/README.md).* -To start the instance you need to copy (or symlink) `queues.lua` file into the `/etc/tarantool/instances.enabled` -directory and run the following command: +To start the instance, copy (or symlink) the `queues.lua` file into the `/etc/tarantool/instances.enabled` directory +and run the following command: ```bash -sudo tarantoolctl start queues +sudo tt start queues ``` ## Working with queue -Once you have your instance running, you can start by creating a queue object with the queue (tube) name you defined +Once you have your instance running, create a queue object using the queue (tube) name you defined in the Lua script: ```php @@ -78,8 +75,8 @@ where `$client` is an instance of `Tarantool\Client\Client` from the [tarantool/ ### Data types -Under the hood Tarantool uses [MessagePack](http://msgpack.org/) binary format to serialize/deserialize -data being stored in a queue. It can handle most of the PHP data types (except resources and closures) without +Under the hood, Tarantool uses the [MessagePack](http://msgpack.org/) binary format to serialize and deserialize +data stored in a queue. It can handle most PHP data types (except resources and closures) without any manual pre- or post-processing: ```php @@ -91,13 +88,13 @@ $queue->put(['foo' => ['bar' => ['baz' => null]]]); $queue->put(new MyObject()); ``` -> *To learn more about object serialization, please follow this [link](https://github.com/tarantool-php/client#user-defined-types).* +> *To learn more about object serialization, see [User-defined types](https://github.com/tarantool-php/client#user-defined-types).* ### Tasks -Most of the [Queue API](src/Queue.php) methods return a [Task](src/Task.php) object -containing the following getters: +Most [Queue API](src/Queue.php) methods return a [Task](src/Task.php) object +that provides the following getters: ```php Task::getId() @@ -105,7 +102,7 @@ Task::getState() // States::READY, States::TAKEN, States::DONE, States::BURY or Task::getData() ``` -And some sugar methods: +It also provides these convenience methods: ```php Task::isReady() @@ -118,10 +115,9 @@ Task::isDelayed() ### Producer API -As you've already seen, to insert a task into a queue you need to call `put()` method, which accepts -two arguments: the data you want to process and optional array of task options, which this particular -queue supports. For example, `fifottl` queue (which we defined [earlier](#before-start) in our Lua config -file), supports `delay`, `ttl`, `ttr` and `pri` options: +As you've already seen, to insert a task into a queue, call the `put()` method. It accepts two arguments: the data you +want to process and an optional array of task options supported by the queue. For example, the `fifottl` queue, which +we defined [earlier](#before-you-start) in our Lua configuration, supports the `delay`, `ttl`, `ttr`, and `pri` options: ```php use Tarantool\Queue\Options; @@ -131,13 +127,13 @@ $queue->put('bar', [Options::TTL => 5.0]); $queue->put('baz', [Options::TTR => 10.0, Options::PRI => 42]); ``` -> *See the full list of available options [here](https://github.com/tarantool/queue#queue-types).* +> *See the [full list of available options](https://github.com/tarantool/queue#queue-types).* ### Consumer API -To reserve a task for execution, call `take()` method. It accepts an optional `timeout` parameter. -If a timeout value is supplied the call will wait `timeout` seconds until a `READY` task appears in the queue. +To reserve a task for execution, call the `take()` method. It accepts an optional `timeout` parameter. +If you supply a timeout, the call waits for up to that many seconds for a `READY` task to appear in the queue. The method returns either a [Task](#tasks) object or `null`: ```php @@ -150,7 +146,7 @@ $taskOrNull = $queue->take(2.0); $taskOrNull = $queue->take(.1); ``` -After successful execution, a task can be marked as acknowledged (that will also delete the task from a queue): +After successful execution, acknowledge the task to delete it from the queue: ```php $data = $task->getData(); @@ -160,16 +156,16 @@ $data = $task->getData(); $task = $queue->ack($task->getId()); ``` -Or put back into the queue in case it cannot be executed: +If the task cannot be executed, return it to the queue: ```php $task = $queue->release($task->getId()); -// for *ttl queues you can specify a delay +// For *ttl queues, you can specify a delay. $task = $queue->release($task->getId(), [Options::DELAY => 30.0]); ``` -To look at a task without changing its state, use: +To view a task without changing its state, use: ```php $task = $queue->peek($task->getId()); @@ -181,19 +177,19 @@ To bury (disable) a task: $task = $queue->bury($task->getId()); ``` -To reset buried task(s) back to `READY` state: +To return buried tasks to the `READY` state: ```php $count = $queue->kick(3); // kick 3 buried tasks ``` -To increase TTR and/or TTL of a running task (only for *ttl queues): +To increase the TTR or TTL of a running task (only for `*ttl` queues): ```php $taskOrNull = $queue->touch($takenTask->getId(), 5.0); // increase ttr/ttl to 5 seconds ``` -A task (in any state) can be deleted permanently with `delete()`: +You can permanently delete a task in any state by calling `delete()`: ```php $task = $queue->delete($task->getId()); @@ -212,8 +208,7 @@ $queue->truncate(); ### Statistics -The `stats()` method provides access to the statistical information accumulated -since a queue was created: +The `stats()` method returns statistics accumulated since the queue was created: ```php $stats = $queue->stats(); @@ -250,9 +245,9 @@ $total = $queue->stats('tasks.total'); ### Custom methods -Thanks to flexible nature of the [queue](https://github.com/tarantool/queue/) Lua module, you can easily create -your own queue drivers or extend existing ones with an additional functionality. For example, suppose you added -the `put_many` method to your `foobar` queue, which inserts multiple tasks atomically: +Thanks to the flexibility of the [queue](https://github.com/tarantool/queue/) Lua module, you can create your own queue +drivers or extend existing ones with additional functionality. For example, suppose you add a `put_many` method to your +`foobar` queue to insert multiple tasks atomically: ```lua -- queues.lua @@ -272,7 +267,7 @@ queue.tube.foobar.put_many = function(self, items) end ``` -To invoke this method from php, use `Queue::call()`: +To invoke this method from PHP, use `Queue::call()`: ```php $result = $queue->call('put_many', [ @@ -299,15 +294,14 @@ docker run -d --net=tarantool-php -p 3301:3301 --name=tarantool \ tarantool/tarantool:2 tarantool /queues.lua ``` -And then run both unit and integration tests: +Then run both unit and integration tests: ```bash docker run --rm --net=tarantool-php -v $(pwd):/queue -w /queue queue ``` -The library uses [PHPUnit](https://phpunit.de/) under the hood, and if needed, -you can pass additional arguments and options to the `phpunit` command. -For example, to run only unit tests, execute: +The tests use [PHPUnit](https://phpunit.de/). You can pass additional arguments and options to the `phpunit` command. +For example, run only the unit tests with: ```bash docker run --rm --net=tarantool-php -v $(pwd):/queue -w /queue queue \ From e8286ea46d15253550be78363b30f4eb82c7f3ef Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 01:09:38 +0200 Subject: [PATCH 17/20] test: disable WAL for integration server --- tests/Integration/queues.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/queues.lua b/tests/Integration/queues.lua index f39f016..081ded9 100755 --- a/tests/Integration/queues.lua +++ b/tests/Integration/queues.lua @@ -5,8 +5,8 @@ local listen = os.getenv('TNT_LISTEN_URI') box.cfg { listen = (listen == '' or listen == nil) and 3301 or listen, log_level = 6, - memtx_dir = '/tmp', - wal_dir = '/tmp' + wal_mode = 'none', + memtx_dir = '/tmp' } box.schema.user.grant('guest', 'read,write,execute,create,drop,alter', 'universe', nil, {if_not_exists = true}) From 5fd943ffe221361b70440393b25972ea50b9a280 Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 01:11:01 +0200 Subject: [PATCH 18/20] test: type data providers as arrays --- tests/Integration/QueueTest.php | 4 ++-- tests/Unit/TaskTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Integration/QueueTest.php b/tests/Integration/QueueTest.php index 97795fc..ef22fa2 100644 --- a/tests/Integration/QueueTest.php +++ b/tests/Integration/QueueTest.php @@ -28,7 +28,7 @@ public function testPut($data) : void self::assertTask($task, 0, States::READY, $data); } - public static function provideTaskData() : iterable + public static function provideTaskData() : array { return [ [null], @@ -311,7 +311,7 @@ public function testStatsInvalidPath(?string $path) : void $this->queue->stats($path); } - public static function provideStatsInvalidPathData() : iterable + public static function provideStatsInvalidPathData() : array { return [ [''], diff --git a/tests/Unit/TaskTest.php b/tests/Unit/TaskTest.php index f7ef792..8977601 100644 --- a/tests/Unit/TaskTest.php +++ b/tests/Unit/TaskTest.php @@ -33,7 +33,7 @@ public function testCreateFromTuple(array $tuple) : void } } - public static function provideTuples() : iterable + public static function provideTuples() : array { return [ [[0, States::READY, [42]]], @@ -65,7 +65,7 @@ public function testIsser(string $state) : void } } - public static function provideStates() : iterable + public static function provideStates() : array { return [ [States::READY], From bc15139d74cc16d45b23a1928b085065716bd1ca Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 01:18:28 +0200 Subject: [PATCH 19/20] refactor: modernize queue types for PHP 8.2 --- src/Queue.php | 13 +++++-------- src/Task.php | 17 ++++++----------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/Queue.php b/src/Queue.php index 2c37a73..d4daff4 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -17,13 +17,10 @@ class Queue { - private $client; - private $name; - - public function __construct(Client $client, string $name) - { - $this->client = $client; - $this->name = $name; + public function __construct( + private readonly Client $client, + private readonly string $name + ) { } public function getClient() : Client @@ -36,7 +33,7 @@ public function getName() : string return $this->name; } - public function put($data, array $options = []) : Task + public function put(mixed $data, array $options = []) : Task { return Task::fromTuple( $this->client->call("queue.tube.$this->name:put", $data, $options)[0] diff --git a/src/Task.php b/src/Task.php index 4760efd..aab1e4b 100644 --- a/src/Task.php +++ b/src/Task.php @@ -15,16 +15,11 @@ final class Task { - private $id; - private $state; - - private $data; - - private function __construct(int $id, string $state, $data) - { - $this->id = $id; - $this->state = $state; - $this->data = $data; + private function __construct( + private readonly int $id, + private readonly string $state, + private readonly mixed $data + ) { } public static function fromTuple(array $tuple) : self @@ -44,7 +39,7 @@ public function getState() : string return $this->state; } - public function getData() + public function getData() : mixed { return $this->data; } From 9b58f30c24d3631d50eddb82db122f7511f2135f Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Fri, 23 Oct 2026 01:21:59 +0200 Subject: [PATCH 20/20] style: add trailing commas to constructors --- src/Queue.php | 2 +- src/Task.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Queue.php b/src/Queue.php index d4daff4..7b3ec7e 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -19,7 +19,7 @@ class Queue { public function __construct( private readonly Client $client, - private readonly string $name + private readonly string $name, ) { } diff --git a/src/Task.php b/src/Task.php index aab1e4b..7494a97 100644 --- a/src/Task.php +++ b/src/Task.php @@ -18,7 +18,7 @@ final class Task private function __construct( private readonly int $id, private readonly string $state, - private readonly mixed $data + private readonly mixed $data, ) { }