diff --git a/.editorconfig b/.editorconfig index 5d24621f..847981db 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,23 +1,12 @@ root = true [*] -end_of_line = lf charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[composer.json] -indent_style = space -indent_size = 4 - -[*.php] -indent_style = space +end_of_line = lf indent_size = 4 - -[*.yml] indent_style = space -indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true -[phpspec.*] -indent_style = space +[*.nix] indent_size = 2 diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..1e62cb13 --- /dev/null +++ b/.envrc @@ -0,0 +1,6 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.1.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.1.0/direnvrc" "sha256-FAT2R9yYvVg516v3LiogjIc8YfsbWbMM/itqWsm5xTA=" +fi +use flake + +export PATH="$PWD/$(composer config vendor-dir)/bin:$PATH" diff --git a/.gitattributes b/.gitattributes index 2cad7e1c..0465b39f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,14 @@ -/tests export-ignore -/doc export-ignore -.gitattributes export-ignore -.gitignore export-ignore -.editorconfig export-ignore -.travis.yml export-ignore -phpunit.xml.dist export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +/.flintci.yml export-ignore +/.github/ export-ignore +/.php_cs.dist export-ignore +/.scrutinizer.yml export-ignore +/.travis.yml export-ignore +/doc/ export-ignore +/phpspec.ci.yml export-ignore +/phpspec.yml.dist export-ignore +/phpunit.xml.dist export-ignore +/spec/ export-ignore +/tests/ export-ignore diff --git a/.github/.editorconfig b/.github/.editorconfig new file mode 100644 index 00000000..7bd3346f --- /dev/null +++ b/.github/.editorconfig @@ -0,0 +1,2 @@ +[*.yml] +indent_size = 2 diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml new file mode 100644 index 00000000..5a23b55c --- /dev/null +++ b/.github/workflows/checks.yaml @@ -0,0 +1,19 @@ +name: Checks + +on: + push: + branches: + - master + pull_request: + +jobs: + composer-normalize: + name: Composer Normalize + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Composer normalize + uses: docker://ergebnis/composer-normalize-action diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..87fda681 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,62 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + build-lowest-version: + name: Build lowest version + runs-on: ubuntu-latest + + steps: + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + coverage: none + extensions: mbstring, intl + tools: composer:v2 + + - name: Setup Problem Matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Download dependencies + run: composer update --no-interaction --prefer-stable --prefer-lowest --prefer-dist + + - name: Run tests + run: composer test + + build: + name: Build + runs-on: ubuntu-latest + strategy: + max-parallel: 10 + matrix: + php: [ '8.0', '8.1', '8.3' ] + + steps: + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + extensions: mbstring, intl + tools: composer:v2 + + - name: Setup Problem Matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Download dependencies + run: composer update --no-interaction --prefer-dist + + - name: Run tests + run: composer test diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml deleted file mode 100644 index d6214483..00000000 --- a/.github/workflows/php.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Tests - -on: - push: - branches: [ master ] - pull_request: - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v2 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 7.4 - - - name: Install dependencies - run: | - composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} - composer install --prefer-dist --no-progress --ignore-platform-req=php - - - name: Run test suite - run: vendor/bin/phpunit diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml new file mode 100644 index 00000000..96f5abf8 --- /dev/null +++ b/.github/workflows/static.yaml @@ -0,0 +1,28 @@ +name: Static analysis + +on: + push: + branches: + - master + pull_request: + +jobs: + php-cs-fixer: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Nix + uses: cachix/install-nix-action@v17 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + + - name: Download dependencies + run: nix develop -c composer update --no-interaction --no-progress + + - name: Run PHP CS Fixer + run: nix develop -c php-cs-fixer fix --diff --dry-run diff --git a/.gitignore b/.gitignore index 5e3bf393..606ff74b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ -composer.lock -vendor -phpunit.xml -coverage -_build +/.direnv/ +.php-cs-fixer.php +.php-cs-fixer.cache +.phpunit.result.cache +/_build/ +/build/ +/composer.lock +/phpspec.yml +/phpunit.xml +/vendor/ +.idea/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 00000000..4d1935a1 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,22 @@ +setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + '@PHP80Migration' => true, + '@PHP80Migration:risky' => true, + '@PHPUnit84Migration:risky' => true, + '@Symfony' => true, + '@Symfony:risky' => true, + 'yoda_style' => false, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__ . '/src') + ->in(__DIR__ . '/tests') + ->name('*.php') + ); + +return $config; + diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 00000000..dac69adf --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,15 @@ +exclude('spec') + ->in(__DIR__) +; + +return PhpCsFixer\Config::create() + ->setRules([ + '@Symfony' => true, + 'array_syntax' => ['syntax' => 'short'], + 'yoda_style' => false, + ]) + ->setFinder($finder) +; diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 153ea7ea..00000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -sudo: false -dist: trusty - -language: php - -php: - - 5.6 - - 7.0 - - hhvm - -env: - - COMPOSER_OPTS="" - - COMPOSER_OPTS="--prefer-lowest" - -cache: - directories: - - $HOME/.composer/cache/files - -services: - - mongodb - - mysql - - postgresql - -before_script: - - tests/travis.sh - -script: - - php vendor/bin/phpunit -v - - php vendor/bin/phpunit --group functional -v - - php vendor/bin/phpspec run diff --git a/README.md b/README.md index 3cf7a9b5..a21b70cd 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,55 @@

-Bernard makes it super easy and enjoyable to do background processing in PHP. It does this by utilizing queues and long running processes. It supports normal queueing drivers but also implements simple ones with Redis and Doctrine. +[![Latest Version](https://img.shields.io/github/release/bernardphp/bernard.svg?style=flat-square)](https://github.com/bernardphp/bernard/releases) +[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.4-8892BF.svg?style=flat-square)](https://php.net/) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/bernardphp/bernard/CI?style=flat-square)](https://github.com/bernardphp/bernard/actions?query=workflow%3ACI) +[![Total Downloads](https://img.shields.io/packagist/dt/bernard/bernard.svg?style=flat-square)](https://packagist.org/packages/bernard/bernard) + +Bernard makes it super easy and enjoyable to do background processing in PHP. +It does this by utilizing queues and long running processes. +It supports normal queueing drivers but also implements simple ones with Redis and Doctrine. Currently these are the supported backends, with more coming with each release: - * Predis / PhpRedis - * Amazon SQS - * Iron MQ - * Doctrine DBAL - * Pheanstalk - * PhpAmqp / RabbitMQ - * Queue interop +- Predis / PhpRedis +- Amazon SQS +- Iron MQ +- Doctrine DBAL +- Pheanstalk +- PhpAmqp / RabbitMQ +- Queue interop + + +## Install + +Via Composer + +```bash +$ composer require bernard/bernard +``` + + +## Documentation + +Please see the [official documentation](https://bernard.readthedocs.org). + + +## Testing + +We try to follow BDD and TDD, as such we use both [phpspec](http://www.phpspec.net) and [phpunit](https://phpunit.de) to test this library. + +```bash +$ composer test +``` + +You can run the functional tests by executing: + +```bash +$ composer test-functional +``` -You can learn more on our website about Bernard and its [related projects][website] or just dive directly into [the -documentation][documentation]. -[![Build Status](https://travis-ci.org/bernardphp/bernard.png?branch=master)][travis] [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/bernardphp/bernard/badges/quality-score.png?s=f752c78d347624081f5b6d3d818fe14eef0311c2)](https://scrutinizer-ci.com/g/bernardphp/bernard/) +## License -[documentation]: https://bernard.readthedocs.org -[website]: http://bernardphp-com.rtfd.org -[travis]: https://travis-ci.org/bernardphp/bernard +The MIT License (MIT). Please see [License File](LICENSE) for more information. diff --git a/composer.json b/composer.json index ef65c69e..89ebd43d 100644 --- a/composer.json +++ b/composer.json @@ -1,56 +1,68 @@ { "name": "bernard/bernard", "description": "Message queue abstraction layer", - "keywords": ["message queue", "message", "queue", "bernard"], + "license": "MIT", + "keywords": [ + "message queue", + "message", + "queue", + "bernard" + ], "homepage": "https://github.com/bernardphp/bernard", "type": "library", - "license": "MIT", - "require": { - "php": "^5.6 || ^7.0", - "bernard/normalt": "~1.0", - "symfony/event-dispatcher": "^4.0", - "beberlei/assert": "~2.1" - }, - "require-dev" : { - "psr/log": "~1.0", - "pimple/pimple": "~1.0", - "predis/predis": "~0.8", - "symfony/console": "^2.7|^3.0|^4.0", - "symfony/dependency-injection": "^2.7|^3.0|^4.0", - "doctrine/dbal": "~2.3", - "aws/aws-sdk-php": "~2.4|~3.0", - "pda/pheanstalk": "~3.0", - "php-amqplib/php-amqplib": "~2.5", - "phpspec/phpspec": "^4.3", - "phpunit/phpunit": "^5.5|^6.0", - "iron-io/iron_mq": "~4.0", - "league/container": "~2.3", - "queue-interop/queue-interop": "^0.6", - "queue-interop/amqp-interop": "^0.6" + "php": ">=8.0", + "beberlei/assert": "^2.1 || ^3.0", + "bernard/normalt": "^1.0", + "symfony/event-dispatcher": "v6.0.19", + "symfony/event-dispatcher-contracts": "^2" + }, + "require-dev": { + "doctrine/dbal": "^2.5", + "doctrine/instantiator": "^1.0.5", + "friends-of-phpspec/phpspec-code-coverage": "^6.0", + "phpspec/phpspec": "^7.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.3", + "psr/container": "^1.0", + "psr/log": "^1.0", + "symfony/console": "v6.0.19" }, "suggest": { - "php-amqplib/php-amqplib": "Allow sending messages to an AMQP server using php-amqplib", "doctrine/dbal": "Allow sending messages to simulated message queue in a database via doctrine dbal", - "iron-io/iron_mq": "Allow sending messages to IronMQ", - "pda/pheanstalk": "Allow sending messages to Beanstalk using pheanstalk", - "predis/predis": "Allow sending messages to Redis using predis", - "aws/aws-sdk-php": "Allow sending messages to AWS services like Simple Queue Service", - "mongodb/mongodb": "Allow sending messages to a MongoDB server via PHP Driver", - "queue-interop/queue-interop": "Allow sending messages using queue interop compatible transports", - "queue-interop/amqp-interop": "Allow sending messages using amqp interop compatible transports" - }, - - "autoload" : { - "psr-4" : { "Bernard\\" : "src/" } - }, - "autoload-dev" : { - "psr-4" : { "Bernard\\Tests\\" : "tests/" } - }, - - "extra" : { - "branch-alias" : { - "dev-master" : "1.0.x-dev" + "mongodb/mongodb": "Allow sending messages to a MongoDB server via PHP Driver" + }, + "autoload": { + "psr-4": { + "Bernard\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Bernard\\Tests\\": "tests/", + "spec\\Bernard\\": "spec/" } - } + }, + "config": { + "sort-packages": true + }, + "scripts": { + "clean": "rm -rf build/ vendor/", + "test": [ + "vendor/bin/phpspec run", + "vendor/bin/phpunit -v" + ], + "test-coverage": [ + "vendor/bin/phpspec run -c phpspec.ci.yml", + "vendor/bin/phpunit -v --coverage-text --coverage-clover=build/unit_coverage.xml" + ], + "test-functional": "php vendor/bin/phpunit -v --group functional" + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "prefer-stable": true, + "minimum-stability": "dev" } diff --git a/doc/consuming.rst b/doc/consuming.rst index b93e39be..316c2262 100644 --- a/doc/consuming.rst +++ b/doc/consuming.rst @@ -1,43 +1,25 @@ Consuming Messages ================== -A single message represents a job that needs to be performed, and as described -earlier, a message's name is used to determine which service object should -receive that message. +Consuming messages has two requirements: + +* the system needs to know how messages should be handled +* the system needs to provide extension points for certain events -A service object can be any object that has a method corresponding to the name of the -message with the first letter lower cased. So ``new PlainMessage('SendNewsletter')`` will trigger a -call to ``$serviceObject->sendNewsletter($message)``. For the system to know which service -object should handle which messages, you are required to register them first. +The first requirement is fulfilled by message routing, the second is by the event dispatcher system. .. code-block:: php add('SendNewsletter', new NewsletterMessageHandler); + use Symfony\Component\EventDispatcher\EventDispatcher; - // Bernard also comes with a router for Pimple (Silex) which allows you - // to use service ids and have your service object lazy loader. - // - // $router = new \Bernard\Router\PimpleAwareRouter($pimple); - // $router->add('SendNewsletter', 'my.service.id'); - // - // Symfony DependencyInjection component is also supported. - // - // $router = new \Bernard\Router\ContainerAwareRouter($container); - // $router->add('SendNewsletter', 'my.service.id'); + // $router = see bellow + $eventDispatcher = new EventDispatcher(); // Create a Consumer and start the loop. $consumer = new Consumer($router, $eventDispatcher); - + // The second argument is optional and is an array // of options. Currently only ``max-runtime`` is supported which specifies the max runtime // in seconds. @@ -45,6 +27,82 @@ object should handle which messages, you are required to register them first. 'max-runtime' => 900, )); + +Routing +------- + +A single message represents a job that needs to be performed, and as described +earlier, by default a message's name is used to determine which receiver should +receive that message. + +A receiver can be any of the following: + +* callable +* class with a static method with the name of the message with the first letter lower cased +* object with a method with the name of the message with the first letter lower cased +* object implementing the ``Bernard\Receiver`` interface + + +For the system to know which receiver should handle which messages, you are required to register them first. + +.. code-block:: php + + new NewsletterMessageHandler(), + ]); + + +Message routing can also happen based on the message class instead of the message name. + +.. code-block:: php + + new NewsletterMessageHandler(), + ]); + + +In some cases the above described receiver rules might not be enough. +The provided router implementations also accept a receiver resolver which can be used for example to resolve +receivers from a Dependency Injection container. A good example for that is the PSR-11 container resolver +implementation that comes with this package. + +.. code-block:: php + + NewsletterMessageHandler::class, + ], + new ContainerReceiverResolver($container), + ); + + Commandline Interface --------------------- diff --git a/doc/drivers.rst b/doc/drivers.rst index 23e15a77..a01655a1 100644 --- a/doc/drivers.rst +++ b/doc/drivers.rst @@ -38,9 +38,9 @@ preconfigured. '/url_endpoint', )); @@ -106,7 +106,7 @@ as appropriate for your use case. 'your-ironmq-token', @@ -238,10 +238,10 @@ in the drivers constructor. )); - $driver = new IronMqDriver($connection); + $driver = new Driver($connection); // or with a prefetching number - $driver = new IronMqDriver($connection, 5); + $driver = new Driver($connection, 5); It is also possible to use push queues with some additional logic. Basically, it is needed to deserialize the message in the request and route it to the @@ -296,7 +296,7 @@ corresponding to the queue and message collections, respectively. selectCollection('bernardDatabase', 'queues'), $mongoClient->selectCollection('bernardDatabase', 'messages'), ); @@ -341,12 +341,12 @@ Requires the installation of pda/pheanstalk. Add the following to your connect('127.0.0.1', 6379); $redis->setOption(Redis::OPT_PREFIX, 'bernard:'); - $driver = new PhpRedisDriver($redis); + $driver = new Driver($redis); Predis ------ @@ -415,14 +415,14 @@ Requires the installation of predis. Add the following to your 'bernard:', )); - $driver = new PredisDriver($predis); + $driver = new Driver($predis); Amazon SQS ---------- @@ -459,7 +459,7 @@ require a HTTP request to amazon to be resolved. 'your-aws-access-key', @@ -467,13 +467,13 @@ require a HTTP request to amazon to be resolved. 'region' => 'the-aws-region-you-choose' )); - $driver = new SqsDriver($connection); + $driver = new Driver($connection); // or with prefetching - $driver = new SqsDriver($connection, array(), 5); + $driver = new Driver($connection, array(), 5); // or with aliased queue urls - $driver = new SqsDriver($connection, array( + $driver = new Driver($connection, array( 'queue-name' => 'queue-url', )); @@ -495,7 +495,7 @@ For example we choose enqueue/fs one to demonstrate how it is working. createContext(); diff --git a/doc/queues.rst b/doc/queues.rst index f3df7269..dde1b2ff 100644 --- a/doc/queues.rst +++ b/doc/queues.rst @@ -16,7 +16,7 @@ With the roundrobin queue you can produce messages to multiple queues In Memory Queue --------------- -Bernard comes with an implementation for ``SplQueue`` which is completly in memory. +Bernard comes with an implementation for ``SplQueue`` which is completely in memory. It is useful for development and/or testing, when you don't necessarily want actions to be performed. diff --git a/example/bootstrap.php b/example/bootstrap.php index 170060bf..58f3b212 100644 --- a/example/bootstrap.php +++ b/example/bootstrap.php @@ -5,75 +5,84 @@ use Bernard\Message; use Bernard\Producer; use Bernard\QueueFactory\PersistentFactory; -use Bernard\Router\SimpleRouter; +use Bernard\Router\ReceiverMapRouter; use Bernard\Serializer; use Symfony\Component\EventDispatcher\EventDispatcher; -/** +/* * This file contains helper methods for the examples. See example/$driver.php * for how to initiate the driver. Also the helper methods can be used as * guidance if you are using Bernard outside a framework or you are developing * a plugin to a framework. */ -if (file_exists($autoloadFile = __DIR__ . '/../vendor/autoload.php') || file_exists($autoloadFile = __DIR__ . '/../../../autoload.php')) { +if (file_exists($autoloadFile = __DIR__.'/../vendor/autoload.php') || file_exists($autoloadFile = __DIR__.'/../../../autoload.php')) { require $autoloadFile; } -require __DIR__ . '/EchoTimeService.php'; +require __DIR__.'/EchoTimeService.php'; ini_set('display_errors', 1); error_reporting(E_ALL); -function get_serializer() { - return new Serializer; +function get_serializer() +{ + return new Serializer(); } -function get_event_dispatcher() { - $dispatcher = new EventDispatcher; - $dispatcher->addSubscriber(new EventListener\ErrorLogSubscriber); +function get_event_dispatcher() +{ + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber(new EventListener\ErrorLogSubscriber()); $dispatcher->addSubscriber(new EventListener\FailureSubscriber(get_producer())); return $dispatcher; } -function get_queue_factory() { +function get_queue_factory() +{ return new PersistentFactory(get_driver(), get_serializer()); } -function get_producer() { - return new Producer(get_queue_factory(), get_event_dispatcher()); +function get_producer() +{ + return new Producer(get_queue_factory(), new EventDispatcher()); } -function get_receivers() { - return new SimpleRouter(array( - 'EchoTime' => new EchoTimeService, - )); +function get_receivers() +{ + return new ReceiverMapRouter([ + 'EchoTime' => new EchoTimeService(), + ]); } -function get_consumer() { +function get_consumer() +{ return new Consumer(get_receivers(), get_event_dispatcher()); } -function produce() { +function produce() +{ $producer = get_producer(); while (true) { - $producer->produce(new Message\PlainMessage('EchoTime', array( + $producer->produce(new Message\PlainMessage('EchoTime', [ 'time' => time(), - ))); + ])); usleep(rand(100, 1000)); } } -function consume() { - $queues = get_queue_factory(); +function consume() +{ + $queues = get_queue_factory(); $consumer = get_consumer(); $consumer->consume($queues->create('echo-time')); } -function main() { +function main() +{ if (!isset($_SERVER['argv'][1])) { die('You must provide an argument of either "consume" or "produce"'); } diff --git a/example/doctrine.php b/example/doctrine.php index 554b732a..99a81460 100644 --- a/example/doctrine.php +++ b/example/doctrine.php @@ -1,22 +1,23 @@ 'bernard', 'user' => 'root', 'password' => null, 'host' => 'localhost', 'driver' => 'pdo_mysql', - )); + ]); - $doctrineDriver = new DoctrineDriver($connection); + $doctrineDriver = new Driver($connection); //Don't do this in your application. Use a database set up script instead. try { @@ -24,9 +25,9 @@ function get_driver() { } catch (\Exception $ex) { $schema = new \Doctrine\DBAL\Schema\Schema(); - \Bernard\Doctrine\MessagesSchema::create($schema); + \Bernard\Driver\Doctrine\MessagesSchema::create($schema); - array_map(array($connection, 'executeQuery'), $schema->toSql($connection->getDatabasePlatform())); + array_map([$connection, 'executeQuery'], $schema->toSql($connection->getDatabasePlatform())); } return $doctrineDriver; diff --git a/example/file.php b/example/file.php index 699ed4ef..f83ab828 100644 --- a/example/file.php +++ b/example/file.php @@ -1,20 +1,20 @@ getenv('IRONMQ_TOKEN'), +function get_driver() +{ + $ironmq = new IronMQ([ + 'token' => getenv('IRONMQ_TOKEN'), 'project_id' => getenv('IRONMQ_PROJECT_ID'), - )); + ]); - return new IronMqDriver($ironmq); + return new Driver($ironmq); } if (!getenv('IRONMQ_TOKEN') || !getenv('IRONMQ_PROJECT_ID')) { diff --git a/example/pheanstalk.php b/example/pheanstalk.php index 2e95de4f..1a3f0ef7 100644 --- a/example/pheanstalk.php +++ b/example/pheanstalk.php @@ -1,16 +1,17 @@ connect('localhost'); $redis->setOption(Redis::OPT_PREFIX, 'bernard:'); diff --git a/example/predis.php b/example/predis.php index 6d38b587..e433c39d 100644 --- a/example/predis.php +++ b/example/predis.php @@ -1,16 +1,17 @@ 'bernard:', - ))); + ])); } require 'bootstrap.php'; diff --git a/example/sqs.php b/example/sqs.php index 1e1ae06a..6f64c694 100644 --- a/example/sqs.php +++ b/example/sqs.php @@ -1,20 +1,21 @@ getenv('ACCESS_KEY'), +function get_driver() +{ + $sqs = SqsClient::factory([ + 'key' => getenv('ACCESS_KEY'), 'secret' => getenv('SECRET_KEY'), - 'region' => getenv('SQS_REGION') - )); + 'region' => getenv('SQS_REGION'), + ]); - return new SqsDriver($sqs); + return new Driver($sqs); } if (!getenv('ACCESS_KEY') || !getenv('SECRET_KEY') || !getenv('SQS_REGION')) { diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..1146b231 --- /dev/null +++ b/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1649676176, + "narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1651558728, + "narHash": "sha256-8HzyRnWlgZluUrVFNOfZAOlA1fghpOSezXvxhalGMUo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "cbe587c735b734405f56803e267820ee1559e6c1", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..7ca8ad25 --- /dev/null +++ b/flake.nix @@ -0,0 +1,29 @@ +{ + description = "Official Bernard drivers"; + + inputs = { + nixpkgs.url = "nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; + [ + git + php + php.packages.composer + php.packages.phpstan + php.packages.php-cs-fixer + # php.packages.psalm + ]; + }; + } + ); +} diff --git a/phpspec.ci.yml b/phpspec.ci.yml new file mode 100644 index 00000000..6b839c50 --- /dev/null +++ b/phpspec.ci.yml @@ -0,0 +1,11 @@ +suites: + main: + namespace: Bernard + psr4_prefix: Bernard +formatter.name: pretty +extensions: + FriendsOfPhpSpec\PhpSpec\CodeCoverage\CodeCoverageExtension: + format: + - clover + output: + clover: build/spec_coverage.xml diff --git a/phpspec.yml.dist b/phpspec.yml.dist new file mode 100644 index 00000000..d4b15f40 --- /dev/null +++ b/phpspec.yml.dist @@ -0,0 +1,5 @@ +suites: + main: + namespace: Bernard + psr4_prefix: Bernard +formatter.name: pretty diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6009990d..eaffd498 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,26 +1,17 @@ - + + + + + ./src + + - + ./tests/ - - - ./src - - - functional diff --git a/spec/.php_cs.dist b/spec/.php_cs.dist new file mode 100644 index 00000000..599607ef --- /dev/null +++ b/spec/.php_cs.dist @@ -0,0 +1,15 @@ +in(__DIR__) +; + +return PhpCsFixer\Config::create() + ->setRules([ + '@Symfony' => true, + 'array_syntax' => ['syntax' => 'short'], + 'yoda_style' => false, + 'visibility_required' => [], + ]) + ->setFinder($finder) +; diff --git a/spec/Bernard/Normalizer/EnvelopeNormalizerSpec.php b/spec/Bernard/Normalizer/EnvelopeNormalizerSpec.php deleted file mode 100644 index e2e26c7b..00000000 --- a/spec/Bernard/Normalizer/EnvelopeNormalizerSpec.php +++ /dev/null @@ -1,67 +0,0 @@ -shouldHaveType('Symfony\Component\Serializer\Normalizer\NormalizerInterface'); - $this->shouldHaveType('Symfony\Component\Serializer\Normalizer\DenormalizerInterface'); - } - - /** - * @param Bernard\Envelope $envelope - * @param Bernard\Message $message - */ - function it_normalizes_envelope_and_delegates_message_to_aggregate($envelope, $message, $aggregate) - { - $envelope->getMessage()->willReturn($message); - $envelope->getClass()->willReturn('Bernard\Message\PlainMessage'); - $envelope->getTimestamp()->willReturn(1337); - - $aggregate->normalize($message)->willReturn(array( - 'key' => 'value', - )); - - $this->setAggregateNormalizer($aggregate); - - $this->normalize($envelope)->shouldReturn(array( - 'class' => 'Bernard\\Message\\PlainMessage', - 'timestamp' => 1337, - 'message' => array('key' => 'value'), - )); - } - - /** - * @param Bernard\Message $message - */ - function it_denormalizes_envelope_and_delegates_message_to_aggregate($message, $aggregate) - { - $this->setAggregateNormalizer($aggregate); - - $aggregate->denormalize(array('key' => 'value'), 'Bernard\\Message\\PlainMessage', null)->willReturn($message); - - $normalized = array( - 'class' => 'Bernard\\Message\\PlainMessage', - 'timestamp' => 1337, - 'message' => array('key' => 'value'), - ); - - $envelope = $this->denormalize($normalized, 'Bernard\\Envelope'); - $envelope->shouldHaveType('Bernard\\Envelope'); - $envelope->getMessage()->shouldReturn($message); - $envelope->getTimestamp()->shouldReturn(1337); - $envelope->getClass()->shouldReturn('Bernard\\Message\\PlainMessage'); - } -} diff --git a/spec/Bernard/Router/ClassNameRouterSpec.php b/spec/Bernard/Router/ClassNameRouterSpec.php deleted file mode 100644 index 298700ee..00000000 --- a/spec/Bernard/Router/ClassNameRouterSpec.php +++ /dev/null @@ -1,40 +0,0 @@ -beConstructedWith([ - 'Bernard\\Message' => function() {}, - ]); - } - - function it_is_initializable() - { - $this->shouldHaveType('Bernard\\Router\\ClassNameRouter'); - } - - function it_is_a_router() - { - $this->shouldImplement('Bernard\\Router'); - } - - function it_maps_an_envelope(Envelope $envelope) - { - $envelope->getClass()->willReturn('Bernard\\Message\\DefaultMessage'); - - $this->map($envelope)->shouldBeCallable(); - } - - function it_throws_an_exception_when_envelope_cannot_be_mapped(Envelope $envelope) - { - $envelope->getClass()->willReturn('Bernard\\Producer'); - - $this->shouldThrow('Bernard\\Exception\\ReceiverNotFoundException')->duringMap($envelope); - } -} diff --git a/spec/Bernard/SerializerSpec.php b/spec/Bernard/SerializerSpec.php deleted file mode 100644 index 4ac5fc3c..00000000 --- a/spec/Bernard/SerializerSpec.php +++ /dev/null @@ -1,46 +0,0 @@ -beConstructedWith($aggregate); - } - - /** - * @param Bernard\Envelope $envelope - */ - function it_serializes_normalized_envelope_into_json($envelope, $aggregate) - { - $aggregate->normalize($envelope, null)->willReturn(array( - 'class' => 'Bernard\\Message\\PlainMessage', - 'timestamp' => 1337, - 'message' => array('name' => 'Import', 'arguments' => array('arg1' => 'value')), - )); - - $this->serialize($envelope) - ->shouldReturn('{"class":"Bernard\\\\Message\\\\PlainMessage","timestamp":1337,"message":{"name":"Import","arguments":{"arg1":"value"}}}'); - } - - /** - * @param Bernard\Envelope $envelope - */ - function it_unserializes_into_envelope($envelope, $aggregate) - { - $normalized = array( - 'class' => 'Bernard\\Message\\PlainMessage', - 'timestamp' => 1337, - 'message' => array('name' => 'Import', 'arguments' => array('arg1' => 'value')), - ); - - $aggregate->denormalize($normalized, 'Bernard\Envelope', null)->willReturn($envelope); - - $this->unserialize('{"class":"Bernard\\\\Message\\\\PlainMessage","timestamp":1337,"message":{"name":"Import","arguments":{"arg1":"value"}}}') - ->shouldReturn($envelope); - } -} diff --git a/spec/Exception/InvalidOperationExceptionSpec.php b/spec/Exception/InvalidOperationExceptionSpec.php new file mode 100644 index 00000000..68bd34fb --- /dev/null +++ b/spec/Exception/InvalidOperationExceptionSpec.php @@ -0,0 +1,20 @@ +shouldHaveType(InvalidOperationException::class); + } + + function it_is_an_exception() + { + $this->shouldHaveType(Exception::class); + } +} diff --git a/spec/Exception/NotImplementedExceptionSpec.php b/spec/Exception/NotImplementedExceptionSpec.php new file mode 100644 index 00000000..5e824e42 --- /dev/null +++ b/spec/Exception/NotImplementedExceptionSpec.php @@ -0,0 +1,20 @@ +shouldHaveType(NotImplementedException::class); + } + + function it_is_an_exception() + { + $this->shouldHaveType(Exception::class); + } +} diff --git a/spec/Exception/ReceiverNotFoundExceptionSpec.php b/spec/Exception/ReceiverNotFoundExceptionSpec.php new file mode 100644 index 00000000..ef38fe3c --- /dev/null +++ b/spec/Exception/ReceiverNotFoundExceptionSpec.php @@ -0,0 +1,20 @@ +shouldHaveType(ReceiverNotFoundException::class); + } + + function it_is_an_exception() + { + $this->shouldHaveType(Exception::class); + } +} diff --git a/spec/Exception/ServiceUnavailableExceptionSpec.php b/spec/Exception/ServiceUnavailableExceptionSpec.php new file mode 100644 index 00000000..76231b14 --- /dev/null +++ b/spec/Exception/ServiceUnavailableExceptionSpec.php @@ -0,0 +1,20 @@ +shouldHaveType(ServiceUnavailableException::class); + } + + function it_is_an_exception() + { + $this->shouldHaveType(Exception::class); + } +} diff --git a/spec/Message/PlainMessageSpec.php b/spec/Message/PlainMessageSpec.php new file mode 100644 index 00000000..0e189bef --- /dev/null +++ b/spec/Message/PlainMessageSpec.php @@ -0,0 +1,46 @@ +beConstructedWith('SendNewsletter'); + } + + function it_is_initializable() + { + $this->shouldHaveType(PlainMessage::class); + } + + function it_is_a_message() + { + $this->shouldImplement(Message::class); + } + + function it_has_a_name() + { + $this->getName()->shouldReturn('SendNewsletter'); + } + + function it_has_arguments() + { + $this->beConstructedWith( + 'SendNewsletter', + $args = [ + 'key1' => 1, + 'key2' => [1, 2, 3, 4], + 'key3' => null, + ] + ); + + $this->get('key1')->shouldReturn(1); + $this->has('key1')->shouldReturn(true); + $this->all()->shouldReturn($args); + } +} diff --git a/spec/Normalizer/EnvelopeNormalizerSpec.php b/spec/Normalizer/EnvelopeNormalizerSpec.php new file mode 100644 index 00000000..f3537e47 --- /dev/null +++ b/spec/Normalizer/EnvelopeNormalizerSpec.php @@ -0,0 +1,66 @@ +shouldHaveType(EnvelopeNormalizer::class); + } + + function it_is_a_normalizer() + { + $this->shouldImplement(NormalizerInterface::class); + } + + function it_is_adenormailzer() + { + $this->shouldImplement(DenormalizerInterface::class); + } + + function it_normalizes_envelope_and_delegates_message_to_aggregate(Message $message, AggregateNormalizer $aggregate) + { + $envelope = new Envelope($message->getWrappedObject()); + $time = time(); + + $aggregate->normalize($message)->willReturn([ + 'key' => 'value', + ]); + + $this->setAggregateNormalizer($aggregate); + + $this->normalize($envelope)->shouldReturn([ + 'class' => get_class($message->getWrappedObject()), + 'timestamp' => $time, + 'message' => ['key' => 'value'], + ]); + } + + function it_denormalizes_envelope_and_delegates_message_to_aggregate(Message $message, AggregateNormalizer $aggregate) + { + $this->setAggregateNormalizer($aggregate); + + $aggregate->denormalize(['key' => 'value'], Message\PlainMessage::class, null)->willReturn($message); + + $normalized = [ + 'class' => Message\PlainMessage::class, + 'timestamp' => 1337, + 'message' => ['key' => 'value'], + ]; + + $envelope = $this->denormalize($normalized, null); + $envelope->shouldHaveType(Envelope::class); + $envelope->getMessage()->shouldReturn($message); + $envelope->getTimestamp()->shouldReturn(1337); + $envelope->getClass()->shouldReturn(Message\PlainMessage::class); + } +} diff --git a/spec/Normalizer/PlainMessageNormalizerSpec.php b/spec/Normalizer/PlainMessageNormalizerSpec.php new file mode 100644 index 00000000..1f36a036 --- /dev/null +++ b/spec/Normalizer/PlainMessageNormalizerSpec.php @@ -0,0 +1,26 @@ +shouldHaveType(PlainMessageNormalizer::class); + } + + function it_is_a_normalizer() + { + $this->shouldImplement(NormalizerInterface::class); + } + + function it_is_adenormailzer() + { + $this->shouldImplement(DenormalizerInterface::class); + } +} diff --git a/spec/Receiver/CallableReceiverSpec.php b/spec/Receiver/CallableReceiverSpec.php new file mode 100644 index 00000000..88744c3a --- /dev/null +++ b/spec/Receiver/CallableReceiverSpec.php @@ -0,0 +1,37 @@ +beConstructedWith(function () {}); + } + + function it_is_initializable() + { + $this->shouldHaveType(CallableReceiver::class); + } + + function it_is_a_receiver() + { + $this->shouldHaveType(Receiver::class); + } + + function it_receives_a_message(Message $message) + { + $this->beConstructedWith(function (Message $message) { + $message->getName(); + }); + + $message->getName()->shouldBeCalled(); + + $this->receive($message); + } +} diff --git a/spec/Router/ClassNameRouterSpec.php b/spec/Router/ClassNameRouterSpec.php new file mode 100644 index 00000000..e4e65174 --- /dev/null +++ b/spec/Router/ClassNameRouterSpec.php @@ -0,0 +1,90 @@ +shouldHaveType(ClassNameRouter::class); + } + + function it_is_a_router() + { + $this->shouldImplement(Router::class); + } + + function it_routes_an_envelope_to_a_receiver(Message $message, Router\ReceiverResolver $receiverResolver, Receiver $receiver) + { + $envelope = new Envelope($message->getWrappedObject()); + + $receiverResolver->accepts('receiver')->willReturn(true); + $receiverResolver->resolve('receiver', $envelope)->willReturn($receiver); + + $this->beConstructedWith( + [ + get_class($message->getWrappedObject()) => 'receiver', + ], + $receiverResolver + ); + + $this->route($envelope)->shouldReturn($receiver); + } + + function it_routes_an_envelope_to_a_receiver_based_on_message_parent(Message $message, Router\ReceiverResolver $receiverResolver, Receiver $receiver) + { + $envelope = new Envelope($message->getWrappedObject()); + + $receiverResolver->accepts('receiver')->willReturn(true); + $receiverResolver->resolve('receiver', $envelope)->willReturn($receiver); + + $this->beConstructedWith( + [ + Message::class => 'receiver', + ], + $receiverResolver + ); + + $this->route($envelope)->shouldReturn($receiver); + } + + function it_throws_an_exception_when_a_receiver_is_not_accepted(Router\ReceiverResolver $receiverResolver) + { + $receiverResolver->accepts('receiver')->willReturn(false); + + $this->beConstructedWith( + [ + Message::class => 'receiver', + ], + $receiverResolver + ); + + $this->shouldThrow(\InvalidArgumentException::class)->duringInstantiation(); + } + + function it_throws_an_exception_when_an_envelop_cannot_be_routed_to_a_receiver(Message $message, Router\ReceiverResolver $receiverResolver) + { + $message->getName()->willReturn('message'); + $envelope = new Envelope($message->getWrappedObject()); + + $receiverResolver->accepts('receiver')->willReturn(true); + $receiverResolver->resolve('receiver', $envelope)->willReturn(null); + + $this->beConstructedWith( + [ + get_class($message->getWrappedObject()) => 'receiver', + ], + $receiverResolver + ); + + $this->shouldThrow(ReceiverNotFoundException::class)->duringRoute($envelope); + } +} diff --git a/spec/Router/ContainerNotFoundExceptionStub.php b/spec/Router/ContainerNotFoundExceptionStub.php new file mode 100644 index 00000000..9ac7a3ef --- /dev/null +++ b/spec/Router/ContainerNotFoundExceptionStub.php @@ -0,0 +1,9 @@ +beConstructedWith($container); + } + + function it_is_initializable() + { + $this->shouldHaveType(ContainerReceiverResolver::class); + } + + function it_is_a_receiver_resolver() + { + $this->shouldHaveType(ReceiverResolver::class); + } + + function it_accepts_a_service_receiver(ContainerInterface $container) + { + $container->has('my.service')->willReturn(true); + + $this->accepts('my.service')->shouldReturn(true); + } + + function it_returns_null_when_the_service_is_not_found(Message $message, ContainerInterface $container) + { + $container->get('my.service')->willThrow(ContainerNotFoundExceptionStub::class); + + $this->resolve('my.service', new Envelope($message->getWrappedObject()))->shouldReturn(null); + } + + function it_returns_the_receiver_when_it_is_already_a_receiver(Message $message, Receiver $receiver, ContainerInterface $container) + { + $container->get('my.service')->willReturn($receiver); + + $this->resolve('my.service', new Envelope($message->getWrappedObject()))->shouldReturn($receiver); + } + + function it_returns_a_callable_receiver_when_the_receiver_is_callable(Message $message, ContainerInterface $container) + { + $container->get('my.service')->willReturn(function (Message $message) {}); + + $this->resolve('my.service', new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } + + function it_returns_a_callable_receiver_when_the_receiver_class_has_a_static_method_for_the_message(Message $message, ContainerInterface $container) + { + $container->get('my.service')->willReturn(ReceiverStub::class); + $message->getName()->willReturn('StaticMessageName'); + + $this->resolve('my.service', new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } + + function it_returns_a_callable_receiver_when_the_receiver_object_has_a_method_for_the_message(Message $message, ContainerInterface $container) + { + $container->get('my.service')->willReturn(new ReceiverStub()); + $message->getName()->willReturn('MessageName'); + + $this->resolve('my.service', new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } + + function it_returns_a_callable_receiver_when_the_receiver_object_has_a_static_method_for_the_message(Message $message, ContainerInterface $container) + { + $container->get('my.service')->willReturn(new ReceiverStub()); + $message->getName()->willReturn('StaticMessageName'); + + $this->resolve('my.service', new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } +} diff --git a/spec/Router/ReceiverMapRouterSpec.php b/spec/Router/ReceiverMapRouterSpec.php new file mode 100644 index 00000000..af710441 --- /dev/null +++ b/spec/Router/ReceiverMapRouterSpec.php @@ -0,0 +1,74 @@ +shouldHaveType(ReceiverMapRouter::class); + } + + function it_is_a_router() + { + $this->shouldImplement(Router::class); + } + + function it_routes_an_envelope_to_a_receiver(Message $message, Router\ReceiverResolver $receiverResolver, Receiver $receiver) + { + $message->getName()->willReturn('message'); + $envelope = new Envelope($message->getWrappedObject()); + + $receiverResolver->accepts('receiver')->willReturn(true); + $receiverResolver->resolve('receiver', $envelope)->willReturn($receiver); + + $this->beConstructedWith( + [ + 'message' => 'receiver', + ], + $receiverResolver + ); + + $this->route($envelope)->shouldReturn($receiver); + } + + function it_throws_an_exception_when_a_receiver_is_not_accepted(Router\ReceiverResolver $receiverResolver) + { + $receiverResolver->accepts('receiver')->willReturn(false); + + $this->beConstructedWith( + [ + 'message' => 'receiver', + ], + $receiverResolver + ); + + $this->shouldThrow(\InvalidArgumentException::class)->duringInstantiation(); + } + + function it_throws_an_exception_when_an_envelop_cannot_be_routed_to_a_receiver(Message $message, Router\ReceiverResolver $receiverResolver) + { + $message->getName()->willReturn('message'); + $envelope = new Envelope($message->getWrappedObject()); + + $receiverResolver->accepts('receiver')->willReturn(true); + $receiverResolver->resolve('receiver', $envelope)->willReturn(null); + + $this->beConstructedWith( + [ + 'message' => 'receiver', + ], + $receiverResolver + ); + + $this->shouldThrow(ReceiverNotFoundException::class)->duringRoute($envelope); + } +} diff --git a/spec/Router/ReceiverStub.php b/spec/Router/ReceiverStub.php new file mode 100644 index 00000000..78094ad4 --- /dev/null +++ b/spec/Router/ReceiverStub.php @@ -0,0 +1,14 @@ +shouldHaveType(SimpleReceiverResolver::class); + } + + function it_is_a_receiver_resolver() + { + $this->shouldHaveType(ReceiverResolver::class); + } + + function it_accepts_a_callable_receiver() + { + $callable = function (Message $message) {}; + + $this->accepts($callable)->shouldReturn(true); + } + + function it_accepts_a_class_receiver() + { + $this->accepts(ReceiverStub::class)->shouldReturn(true); + } + + function it_accepts_an_object_receiver() + { + $this->accepts(new ReceiverStub())->shouldReturn(true); + } + + function it_returns_null_when_the_receiver_is_null(Message $message) + { + $this->resolve(null, new Envelope($message->getWrappedObject()))->shouldReturn(null); + } + + function it_returns_the_receiver_when_it_is_already_a_receiver(Message $message, Receiver $receiver) + { + $this->resolve($receiver, new Envelope($message->getWrappedObject()))->shouldReturn($receiver); + } + + function it_returns_a_callable_receiver_when_the_receiver_is_callable(Message $message) + { + $callable = function (Message $message) {}; + $this->resolve($callable, new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } + + function it_returns_a_callable_receiver_when_the_receiver_class_has_a_static_method_for_the_message(Message $message) + { + $message->getName()->willReturn('StaticMessageName'); + + $this->resolve(ReceiverStub::class, new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } + + function it_returns_a_callable_receiver_when_the_receiver_object_has_a_method_for_the_message(Message $message) + { + $message->getName()->willReturn('MessageName'); + + $this->resolve(new ReceiverStub(), new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } + + function it_returns_a_callable_receiver_when_the_receiver_object_has_a_static_method_for_the_message(Message $message) + { + $message->getName()->willReturn('StaticMessageName'); + + $this->resolve(new ReceiverStub(), new Envelope($message->getWrappedObject()))->shouldHaveType(Receiver\CallableReceiver::class); + } +} diff --git a/spec/SerializerSpec.php b/spec/SerializerSpec.php new file mode 100644 index 00000000..567878b6 --- /dev/null +++ b/spec/SerializerSpec.php @@ -0,0 +1,45 @@ +beConstructedWith($aggregate); + } + + function it_serializes_normalized_envelope_into_json(Message $message, AggregateNormalizer $aggregate) + { + $envelope = new Envelope($message->getWrappedObject()); + $aggregate->normalize($envelope, null)->willReturn([ + 'class' => PlainMessage::class, + 'timestamp' => 1337, + 'message' => ['name' => 'Import', 'arguments' => ['arg1' => 'value']], + ]); + + $this->serialize($envelope) + ->shouldReturn('{"class":"Bernard\\\\Message\\\\PlainMessage","timestamp":1337,"message":{"name":"Import","arguments":{"arg1":"value"}}}'); + } + + function it_unserializes_into_envelope(Message $message, AggregateNormalizer $aggregate) + { + $envelope = new Envelope($message->getWrappedObject()); + + $normalized = [ + 'class' => PlainMessage::class, + 'timestamp' => 1337, + 'message' => ['name' => 'Import', 'arguments' => ['arg1' => 'value']], + ]; + + $aggregate->denormalize($normalized, Envelope::class, null)->willReturn($envelope); + + $this->unserialize('{"class":"Bernard\\\\Message\\\\PlainMessage","timestamp":1337,"message":{"name":"Import","arguments":{"arg1":"value"}}}') + ->shouldReturn($envelope); + } +} diff --git a/src/BernardEvents.php b/src/BernardEvents.php index bfb82c53..f43b36a6 100644 --- a/src/BernardEvents.php +++ b/src/BernardEvents.php @@ -1,9 +1,11 @@ consumer = $consumer; @@ -34,7 +29,7 @@ public function __construct(Consumer $consumer, QueueFactory $queues) /** * {@inheritdoc} */ - public function configure() + public function configure(): void { $this ->addOption('max-runtime', null, InputOption::VALUE_OPTIONAL, 'Maximum time in seconds the consumer will run.', null) @@ -48,11 +43,13 @@ public function configure() /** * {@inheritdoc} */ - public function execute(InputInterface $input, OutputInterface $output) + public function execute(InputInterface $input, OutputInterface $output): int { $queue = $this->getQueue($input->getArgument('queue')); $this->consumer->consume($queue, $input->getOptions()); + + return self::SUCCESS; } /** @@ -62,13 +59,13 @@ public function execute(InputInterface $input, OutputInterface $output) */ protected function getQueue($queue) { - if (!is_string($queue) && count($queue) > 1) { - $queues = array_map([$this->queues, 'create'], $queue); + if (\is_array($queue)) { + if (\count($queue) > 1) { + $queues = array_map([$this->queues, 'create'], $queue); - return new RoundRobinQueue($queues); - } + return new RoundRobinQueue($queues); + } - if (is_array($queue)) { $queue = $queue[0]; } diff --git a/src/Command/ProduceCommand.php b/src/Command/ProduceCommand.php index ffc5503a..86aee2b0 100644 --- a/src/Command/ProduceCommand.php +++ b/src/Command/ProduceCommand.php @@ -1,24 +1,20 @@ producer = $producer; @@ -29,7 +25,7 @@ public function __construct(Producer $producer) /** * {@inheritdoc} */ - public function configure() + public function configure(): void { $this ->addOption('queue', null, InputOption::VALUE_OPTIONAL, 'Name of a queue to add this job to. By default the queue is guessed from the message name.', null) @@ -41,7 +37,7 @@ public function configure() /** * {@inheritdoc} */ - public function execute(InputInterface $input, OutputInterface $output) + public function execute(InputInterface $input, OutputInterface $output): int { $name = $input->getArgument('name'); $queue = $input->getOption('queue'); @@ -51,10 +47,12 @@ public function execute(InputInterface $input, OutputInterface $output) $message = json_decode($input->getArgument('message'), true); if (json_last_error()) { - throw new \RuntimeException('Could not decode invalid JSON [' . json_last_error() . ']'); + throw new \RuntimeException('Could not decode invalid JSON ['.json_last_error().']'); } } $this->producer->produce(new PlainMessage($name, $message), $queue); + + return self::SUCCESS; } } diff --git a/src/Consumer.php b/src/Consumer.php index ebbc0bf6..82f8f8c1 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -1,15 +1,14 @@ PHP_INT_MAX, + 'max-runtime' => \PHP_INT_MAX, 'max-messages' => null, 'stop-when-empty' => false, 'stop-on-error' => false, ]; - /** - * @param Router $router - * @param EventDispatcherInterface $dispatcher - */ public function __construct(Router $router, EventDispatcherInterface $dispatcher) { $this->router = $router; @@ -35,14 +30,11 @@ public function __construct(Router $router, EventDispatcherInterface $dispatcher } /** - * Starts an infinite loop calling Consumer::tick(); - * - * @param Queue $queue - * @param array $options + * Starts an infinite loop calling Consumer::tick();. */ - public function consume(Queue $queue, array $options = []) + public function consume(Queue $queue, array $options = []): void { - declare (ticks = 1); + declare(ticks=1); $this->bind(); @@ -55,9 +47,6 @@ public function consume(Queue $queue, array $options = []) * Returns true do indicate it should be run again or false to indicate * it should not be run again. * - * @param Queue $queue - * @param array $options - * * @return bool */ public function tick(Queue $queue, array $options = []) @@ -88,29 +77,29 @@ public function tick(Queue $queue, array $options = []) return true; } - return (boolean) --$this->options['max-messages']; + return (bool) --$this->options['max-messages']; } /** - * Mark Consumer as shutdown + * Mark Consumer as shutdown. */ - public function shutdown() + public function shutdown(): void { $this->shutdown = true; } /** - * Pause consuming + * Pause consuming. */ - public function pause() + public function pause(): void { $this->pause = true; } /** - * Resume consuming + * Resume consuming. */ - public function resume() + public function resume(): void { $this->pause = false; } @@ -119,19 +108,16 @@ public function resume() * Until there is a real extension point to doing invoked stuff, this can be used * by wrapping the invoke method. * - * @param Envelope $envelope - * @param Queue $queue - * * @throws \Exception * @throws \Throwable */ - public function invoke(Envelope $envelope, Queue $queue) + public function invoke(Envelope $envelope, Queue $queue): void { try { $this->dispatcher->dispatch(new EnvelopeEvent($envelope, $queue), BernardEvents::INVOKE); - // for 5.3 support where a function name is not a callable - call_user_func($this->router->map($envelope), $envelope->getMessage()); + $receiver = $this->router->route($envelope); + $receiver->receive($envelope->getMessage()); // We successfully processed the message. $queue->acknowledge($envelope); @@ -144,11 +130,6 @@ public function invoke(Envelope $envelope, Queue $queue) } } - /** - * @param array $options - * - * @return void - */ protected function configure(array $options) { if ($this->configured) { @@ -167,27 +148,24 @@ protected function configure(array $options) * The difference is that when terminating the consumer, running processes will not stop gracefully * and will terminate immediately. */ - protected function bind() + protected function bind(): void { - if (function_exists('pcntl_signal')) { - pcntl_signal(SIGTERM, [$this, 'shutdown']); - pcntl_signal(SIGINT, [$this, 'shutdown']); - pcntl_signal(SIGQUIT, [$this, 'shutdown']); - pcntl_signal(SIGUSR2, [$this, 'pause']); - pcntl_signal(SIGCONT, [$this, 'resume']); + if (\function_exists('pcntl_signal')) { + pcntl_signal(\SIGTERM, [$this, 'shutdown']); + pcntl_signal(\SIGINT, [$this, 'shutdown']); + pcntl_signal(\SIGQUIT, [$this, 'shutdown']); + pcntl_signal(\SIGUSR2, [$this, 'pause']); + pcntl_signal(\SIGCONT, [$this, 'resume']); } } /** * @param \Throwable|\Exception $exception note that the type-hint is missing due to PHP 5.x compat * - * @param Envelope $envelope - * @param Queue $queue - * * @throws \Exception * @throws \Throwable */ - private function rejectDispatch($exception, Envelope $envelope, Queue $queue) + private function rejectDispatch($exception, Envelope $envelope, Queue $queue): void { // Make sure the exception is not interfering. // Previously failing jobs handling have been moved to a middleware. diff --git a/src/DelayableDriver.php b/src/DelayableDriver.php index 0787ccf2..fb517697 100644 --- a/src/DelayableDriver.php +++ b/src/DelayableDriver.php @@ -1,18 +1,20 @@ */ interface DelayableDriver extends Driver { /** - * Insert a message with delay + * Insert a message with delay in seconds. * - * @param string $queueName - * @param string $message - * @param int $delay Delay in seconds + * @param string $queueName + * @param string $message + * @param int $delay */ - public function pushMessageWithDelay($queueName, $message, $delay); -} \ No newline at end of file + public function pushMessageWithDelay($queueName, $message, $delay): void; +} diff --git a/src/Driver.php b/src/Driver.php index 5ba15431..2c3bbe58 100644 --- a/src/Driver.php +++ b/src/Driver.php @@ -1,82 +1,60 @@ prefetch = $prefetch ? (integer) $prefetch : 2; + $this->prefetch = $prefetch ?? 2; $this->cache = new PrefetchMessageCache(); } } diff --git a/src/Driver/AppEngineDriver.php b/src/Driver/AppEngineDriver.php deleted file mode 100644 index 817b5472..00000000 --- a/src/Driver/AppEngineDriver.php +++ /dev/null @@ -1,109 +0,0 @@ - 'endpoint') to route messages to the - * correct place. - * - * @package Bernard - */ -class AppEngineDriver implements \Bernard\Driver -{ - protected $queueMap; - - /** - * @param array $queueMap - */ - public function __construct(array $queueMap) - { - $this->queueMap = $queueMap; - } - - /** - * {@inheritdoc} - */ - public function listQueues() - { - return array_flip($this->queueMap); - } - - /** - * {@inheritdoc} - */ - public function createQueue($queueName) - { - } - - /** - * {@inheritdoc} - */ - public function countMessages($queueName) - { - } - - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) - { - $task = new PushTask($this->resolveEndpoint($queueName), compact('message')); - $task->add($queueName); - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - } - - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - } - - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) - { - } - - /** - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - return []; - } - - /** - * {@inheritdoc} - */ - public function info() - { - return []; - } - - /** - * @param string $queueName - * - * @throws InvalidArgumentException - * - * @return string - */ - protected function resolveEndpoint($queueName) - { - if (isset($this->queueMap[$queueName])) { - return $this->queueMap[$queueName]; - } - - return '/_ah/queue/' . $queueName; - } -} diff --git a/src/Driver/Delayable/DelayablePheanstalkDriver.php b/src/Driver/Delayable/DelayablePheanstalkDriver.php index 33686cd5..22105046 100644 --- a/src/Driver/Delayable/DelayablePheanstalkDriver.php +++ b/src/Driver/Delayable/DelayablePheanstalkDriver.php @@ -1,28 +1,31 @@ */ -class DelayablePheanstalkDriver extends PheanstalkDriver implements DelayableDriver +class DelayablePheanstalkDriver extends Pheanstalk\Driver implements DelayableDriver { + public const DEFAULT_PRIORITY = 1024; // most urgent: 0, least urgent: 4294967295 + /** * {@inheritDoc} */ - public function pushMessageWithDelay($queueName, $message, $delay) + public function pushMessageWithDelay($queueName, $message, $delay): void { $this->pheanstalk->putInTube( $queueName, $message, - PheanstalkInterface::DEFAULT_PRIORITY, + self::DEFAULT_PRIORITY, (int) $delay ); } -} \ No newline at end of file +} diff --git a/src/Command/Doctrine/AbstractCommand.php b/src/Driver/Doctrine/Command/AbstractCommand.php similarity index 67% rename from src/Command/Doctrine/AbstractCommand.php rename to src/Driver/Doctrine/Command/AbstractCommand.php index a2850751..2bb2b78a 100644 --- a/src/Command/Doctrine/AbstractCommand.php +++ b/src/Driver/Doctrine/Command/AbstractCommand.php @@ -1,8 +1,10 @@ addOption('dump-sql', null, InputOption::VALUE_NONE, 'Output generated SQL statements instead of applying them'); } /** - * {@inheritDoc} + * {@inheritdoc} */ - public function execute(InputInterface $input, OutputInterface $output) + public function execute(InputInterface $input, OutputInterface $output): int { - $schema = new Schema; + $schema = new Schema(); MessagesSchema::create($schema); $sync = $this->getSynchronizer($this->getHelper('connection')->getConnection()); if ($input->getOption('dump-sql')) { - $output->writeln(implode(';' . PHP_EOL, $this->getSql($sync, $schema)) . ';'); - return; + $output->writeln(implode(';'.\PHP_EOL, $this->getSql($sync, $schema)).';'); + + return self::SUCCESS; } - $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + $output->writeln('ATTENTION: This operation should not be executed in a production environment.'.\PHP_EOL); $output->writeln('Applying database schema changes...'); $this->applySql($sync, $schema); $output->writeln('Schema changes applied successfully!'); + + return self::SUCCESS; } /** @@ -58,15 +60,9 @@ protected function getSynchronizer(Connection $connection) } /** - * @param \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer $sync - * @param \Doctrine\DBAL\Schema\Schema $schema * @return array */ abstract protected function getSql(Synchronizer $sync, Schema $schema); - /** - * @param \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer $sync - * @param \Doctrine\DBAL\Schema\Schema $schema - */ abstract protected function applySql(Synchronizer $sync, Schema $schema); } diff --git a/src/Command/Doctrine/CreateCommand.php b/src/Driver/Doctrine/Command/CreateCommand.php similarity index 81% rename from src/Command/Doctrine/CreateCommand.php rename to src/Driver/Doctrine/Command/CreateCommand.php index 95c73824..ecddfa1a 100644 --- a/src/Command/Doctrine/CreateCommand.php +++ b/src/Driver/Doctrine/Command/CreateCommand.php @@ -1,13 +1,12 @@ createSchema($schema); } diff --git a/src/Command/Doctrine/DropCommand.php b/src/Driver/Doctrine/Command/DropCommand.php similarity index 81% rename from src/Command/Doctrine/DropCommand.php rename to src/Driver/Doctrine/Command/DropCommand.php index f35ac88a..e79bd7c4 100644 --- a/src/Command/Doctrine/DropCommand.php +++ b/src/Driver/Doctrine/Command/DropCommand.php @@ -1,13 +1,12 @@ dropSchema($schema); } diff --git a/src/Command/Doctrine/UpdateCommand.php b/src/Driver/Doctrine/Command/UpdateCommand.php similarity index 81% rename from src/Command/Doctrine/UpdateCommand.php rename to src/Driver/Doctrine/Command/UpdateCommand.php index 632f6c62..ab3837c2 100644 --- a/src/Command/Doctrine/UpdateCommand.php +++ b/src/Driver/Doctrine/Command/UpdateCommand.php @@ -1,13 +1,12 @@ updateSchema($schema); } diff --git a/src/Doctrine/ConnectionListener.php b/src/Driver/Doctrine/ConnectionListener.php similarity index 82% rename from src/Doctrine/ConnectionListener.php rename to src/Driver/Doctrine/ConnectionListener.php index 3e8bf61b..24027174 100644 --- a/src/Doctrine/ConnectionListener.php +++ b/src/Driver/Doctrine/ConnectionListener.php @@ -1,15 +1,16 @@ - * @package Bernard */ class ConnectionListener implements EventSubscriberInterface { @@ -20,21 +21,21 @@ class ConnectionListener implements EventSubscriberInterface public static function getSubscribedEvents() { - return array( + return [ 'bernard.invoke' => 'onPing', - ); + ]; } public function __construct($connections) { - if (!is_array($connections)) { - $connections = array($connections); + if (!\is_array($connections)) { + $connections = [$connections]; } $this->connections = $connections; } - public function onPing() + public function onPing(): void { foreach ($this->connections as $connection) { if (!$connection->isConnected()) { diff --git a/src/Driver/DoctrineDriver.php b/src/Driver/Doctrine/Driver.php similarity index 61% rename from src/Driver/DoctrineDriver.php rename to src/Driver/Doctrine/Driver.php index bcc48fbf..8fbbe70d 100644 --- a/src/Driver/DoctrineDriver.php +++ b/src/Driver/Doctrine/Driver.php @@ -1,30 +1,22 @@ connection = $connection; } - /** - * {@inheritdoc} - */ - public function listQueues() + public function listQueues(): array { $statement = $this->connection->prepare('SELECT name FROM bernard_queues'); $statement->execute(); @@ -32,10 +24,7 @@ public function listQueues() return $statement->fetchAll(\PDO::FETCH_COLUMN); } - /** - * {@inheritdoc} - */ - public function createQueue($queueName) + public function createQueue(string $queueName): void { try { $this->connection->insert('bernard_queues', ['name' => $queueName]); @@ -45,23 +34,13 @@ public function createQueue($queueName) } } - /** - * {@inheritdoc} - */ - public function countMessages($queueName) + public function removeQueue(string $queueName): void { - $query = 'SELECT COUNT(id) FROM bernard_messages WHERE queue = :queue AND visible = :visible'; - - return (integer) $this->connection->fetchColumn($query, [ - 'queue' => $queueName, - 'visible' => true, - ]); + $this->connection->delete('bernard_messages', ['queue' => $queueName]); + $this->connection->delete('bernard_queues', ['name' => $queueName]); } - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) + public function pushMessage(string $queueName, string $message): void { $types = ['string', 'string', 'datetime']; $data = [ @@ -74,10 +53,7 @@ public function pushMessage($queueName, $message) $this->connection->insert('bernard_messages', $data, $types); } - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) + public function popMessage(string $queueName, int $duration = 5): ?Message { $runtime = microtime(true) + $duration; @@ -96,49 +72,40 @@ public function popMessage($queueName, $duration = 5) return $message; } - //sleep for 10 ms + // sleep for 10 ms usleep(10000); } - } - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - $this->connection->delete('bernard_messages', ['id' => $receipt, 'queue' => $queueName]); + return null; } /** - * {@inheritdoc} + * Execute the actual query and process the response. */ - public function peekQueue($queueName, $index = 0, $limit = 20) + private function doPopMessage(string $queueName): ?Message { - $parameters = [$queueName, $limit, $index]; - $types = ['string', 'integer', 'integer']; + $query = 'SELECT id, message FROM bernard_messages + WHERE queue = :queue AND visible = :visible + ORDER BY sentAt LIMIT 1 '.$this->connection->getDatabasePlatform()->getForUpdateSql(); + + [$id, $message] = $this->connection->fetchArray($query, [ + 'queue' => $queueName, + 'visible' => true, + ]); - $query = 'SELECT message FROM bernard_messages WHERE queue = ? ORDER BY sentAt LIMIT ? OFFSET ?'; + if ($id) { + $this->connection->update('bernard_messages', ['visible' => 0], compact('id')); - return $this - ->connection - ->executeQuery($query, $parameters, $types) - ->fetchAll(\PDO::FETCH_COLUMN) - ; + return new Message($message, $id); + } } - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) + public function acknowledgeMessage(string $queueName, mixed $receipt): void { - $this->connection->delete('bernard_messages', ['queue' => $queueName]); - $this->connection->delete('bernard_queues', ['name' => $queueName]); + $this->connection->delete('bernard_messages', ['id' => $receipt, 'queue' => $queueName]); } - /** - * {@inheritdoc} - */ - public function info() + public function info(): array { $params = $this->connection->getParams(); @@ -147,28 +114,26 @@ public function info() return $params; } - /** - * Execute the actual query and process the response - * - * @param string $queueName - * - * @return array|null - */ - protected function doPopMessage($queueName) + public function countMessages(string $queueName): int { - $query = 'SELECT id, message FROM bernard_messages - WHERE queue = :queue AND visible = :visible - ORDER BY sentAt LIMIT 1 ' . $this->connection->getDatabasePlatform()->getForUpdateSql(); + $query = 'SELECT COUNT(id) FROM bernard_messages WHERE queue = :queue AND visible = :visible'; - list($id, $message) = $this->connection->fetchArray($query, [ + return (int) $this->connection->fetchColumn($query, [ 'queue' => $queueName, 'visible' => true, ]); + } - if ($id) { - $this->connection->update('bernard_messages', ['visible' => 0], compact('id')); + public function peekQueue(string $queueName, int $index = 0, int $limit = 20): array + { + $parameters = [$queueName, true, $limit, $index]; + $types = ['string', 'boolean', 'integer', 'integer']; - return [$message, $id]; - } + $query = 'SELECT message FROM bernard_messages WHERE queue = ? AND visible = ? ORDER BY sentAt LIMIT ? OFFSET ?'; + + return $this + ->connection + ->executeQuery($query, $parameters, $types) + ->fetchAll(\PDO::FETCH_COLUMN); } } diff --git a/src/Doctrine/MessagesSchema.php b/src/Driver/Doctrine/MessagesSchema.php similarity index 80% rename from src/Doctrine/MessagesSchema.php rename to src/Driver/Doctrine/MessagesSchema.php index a7b3f0da..36957af7 100644 --- a/src/Doctrine/MessagesSchema.php +++ b/src/Driver/Doctrine/MessagesSchema.php @@ -1,20 +1,17 @@ createTable('bernard_queues'); $table->addColumn('name', 'string'); @@ -34,10 +29,8 @@ protected static function createQueueTable(Schema $schema) /** * Creates message table on the current schema given. - * - * @param Schema $schema */ - protected static function createMessagesTable(Schema $schema) + protected static function createMessagesTable(Schema $schema): void { $table = $schema->createTable('bernard_messages'); $table->addColumn('id', 'integer', [ diff --git a/src/Driver/FlatFileDriver.php b/src/Driver/FlatFile/Driver.php similarity index 50% rename from src/Driver/FlatFileDriver.php rename to src/Driver/FlatFile/Driver.php index a904bb9e..27e94151 100644 --- a/src/Driver/FlatFileDriver.php +++ b/src/Driver/FlatFile/Driver.php @@ -1,6 +1,10 @@ */ -class FlatFileDriver implements \Bernard\Driver +final class Driver implements \Bernard\Driver { - private $baseDirectory; + private string $baseDirectory; - private $permissions; + private int $permissions; - /** - * @param string $baseDirectory The base directory - * @param int $permissions Permissions to create the file with. - */ - public function __construct($baseDirectory, $permissions = 0740) + public function __construct(string $baseDirectory, int $permissions = 0740) { $this->baseDirectory = $baseDirectory; $this->permissions = $permissions; } - /** - * {@inheritdoc} - */ - public function listQueues() + public function listQueues(): array { $it = new \FilesystemIterator($this->baseDirectory, \FilesystemIterator::SKIP_DOTS); @@ -38,16 +35,13 @@ public function listQueues() continue; } - array_push($queues, $file->getBasename()); + $queues[] = $file->getBasename(); } return $queues; } - /** - * {@inheritdoc} - */ - public function createQueue($queueName) + public function createQueue(string $queueName): void { $queueDir = $this->getQueueDirectory($queueName); @@ -58,68 +52,73 @@ public function createQueue($queueName) mkdir($queueDir, 0755, true); } - /** - * {@inheritdoc} - */ - public function countMessages($queueName) + public function removeQueue(string $queueName): void { $iterator = new \RecursiveDirectoryIterator( $this->getQueueDirectory($queueName), \FilesystemIterator::SKIP_DOTS ); $iterator = new \RecursiveIteratorIterator($iterator); - $iterator = new \RegexIterator($iterator, '#\.job$#'); + $iterator = new \RegexIterator($iterator, '#\.job(.proceed)?$#'); - return iterator_count($iterator); + foreach ($iterator as $file) { + /* @var $file \DirectoryIterator */ + unlink($file->getRealPath()); + } + + rmdir($this->getQueueDirectory($queueName)); } - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) + public function pushMessage(string $queueName, string $message): void { $queueDir = $this->getQueueDirectory($queueName); $filename = $this->getJobFilename($queueName); - file_put_contents($queueDir.DIRECTORY_SEPARATOR.$filename, $message); - chmod($queueDir . DIRECTORY_SEPARATOR . $filename, $this->permissions); + file_put_contents($queueDir.\DIRECTORY_SEPARATOR.$filename, $message); + chmod($queueDir.\DIRECTORY_SEPARATOR.$filename, $this->permissions); } - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) + public function popMessage(string $queueName, int $duration = 5): ?Message { $runtime = microtime(true) + $duration; $queueDir = $this->getQueueDirectory($queueName); - $it = new \GlobIterator($queueDir.DIRECTORY_SEPARATOR.'*.job', \FilesystemIterator::KEY_AS_FILENAME); - $files = array_keys(iterator_to_array($it)); - - natsort($files); + $files = $this->getJobFiles($queueName); while (microtime(true) < $runtime) { if ($files) { $id = array_pop($files); - if (@rename($queueDir.DIRECTORY_SEPARATOR.$id, $queueDir.DIRECTORY_SEPARATOR.$id.'.proceed')) { - return array(file_get_contents($queueDir.DIRECTORY_SEPARATOR.$id.'.proceed'), $id); + if (@rename($queueDir.\DIRECTORY_SEPARATOR.$id, $queueDir.\DIRECTORY_SEPARATOR.$id.'.proceed')) { + return new Message(file_get_contents($queueDir.\DIRECTORY_SEPARATOR.$id.'.proceed'), $id); } + + return $this->processFileOrFail($queueDir, $id); + } else { + // In order to notice that a new message received, update the list. + $files = $this->getJobFiles($queueName); } usleep(1000); } + } - return array(null, null); + private function processFileOrFail(string $queueDir, string $id): Message + { + $name = $queueDir.\DIRECTORY_SEPARATOR.$id; + $newName = $name.'.proceed'; + + if (!@rename($name, $newName)) { + throw new InsufficientPermissionsException('Unable to process file: '.$name); + } + + return new Message(file_get_contents($newName), $id); } - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) + public function acknowledgeMessage(string $queueName, mixed $receipt): void { $queueDir = $this->getQueueDirectory($queueName); - $path = $queueDir.DIRECTORY_SEPARATOR.$receipt.'.proceed'; + $path = $queueDir.\DIRECTORY_SEPARATOR.$receipt.'.proceed'; if (!is_file($path)) { return; @@ -128,76 +127,50 @@ public function acknowledgeMessage($queueName, $receipt) unlink($path); } - /** - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) + public function info(): array + { + return []; + } + + public function countMessages(string $queueName): int + { + $iterator = new \RecursiveDirectoryIterator( + $this->getQueueDirectory($queueName), + \FilesystemIterator::SKIP_DOTS + ); + $iterator = new \RecursiveIteratorIterator($iterator); + $iterator = new \RegexIterator($iterator, '#\.job$#'); + + return iterator_count($iterator); + } + + public function peekQueue(string $queueName, int $index = 0, int $limit = 20): array { $queueDir = $this->getQueueDirectory($queueName); - $it = new \GlobIterator($queueDir.DIRECTORY_SEPARATOR.'*.job', \FilesystemIterator::KEY_AS_FILENAME); + $it = new \GlobIterator($queueDir.\DIRECTORY_SEPARATOR.'*.job', \FilesystemIterator::KEY_AS_FILENAME); $files = array_keys(iterator_to_array($it)); natsort($files); $files = array_reverse($files); - $files = array_slice($files, $index, $limit); + $files = \array_slice($files, $index, $limit); $messages = []; foreach ($files as $file) { - array_push($messages, file_get_contents($queueDir.DIRECTORY_SEPARATOR.$file)); + $messages[] = file_get_contents($queueDir.\DIRECTORY_SEPARATOR.$file); } return $messages; } - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) + private function getQueueDirectory(string $queueName): string { - $iterator = new \RecursiveDirectoryIterator( - $this->getQueueDirectory($queueName), - \FilesystemIterator::SKIP_DOTS - ); - $iterator = new \RecursiveIteratorIterator($iterator); - $iterator = new \RegexIterator($iterator, '#\.job(.proceed)?$#'); - - foreach ($iterator as $file) { - /* @var $file \DirectoryIterator */ - unlink($file->getRealPath()); - } - - rmdir($this->getQueueDirectory($queueName)); - } - - /** - * {@inheritdoc} - */ - public function info() - { - return []; - } - - /** - * @param string $queueName - * - * @return string - */ - private function getQueueDirectory($queueName) - { - return $this->baseDirectory.DIRECTORY_SEPARATOR.str_replace(array('\\', '.'), '-', $queueName); + return $this->baseDirectory.\DIRECTORY_SEPARATOR.str_replace(['\\', '.'], '-', $queueName); } - /** - * Generates a uuid. - * - * @param string $queueName - * - * @return string - */ - private function getJobFilename($queueName) + private function getJobFilename(string $queueName): string { $path = $this->baseDirectory.'/bernard.meta'; @@ -207,12 +180,12 @@ private function getJobFilename($queueName) } $file = new \SplFileObject($path, 'r+'); - $file->flock(LOCK_EX); + $file->flock(\LOCK_EX); $meta = unserialize($file->fgets()); - $id = isset($meta[$queueName]) ? $meta[$queueName] : 0; - $id++; + $id = $meta[$queueName] ?? 0; + ++$id; $filename = sprintf('%d.job', $id); $meta[$queueName] = $id; @@ -220,9 +193,21 @@ private function getJobFilename($queueName) $content = serialize($meta); $file->fseek(0); - $file->fwrite($content, strlen($content)); - $file->flock(LOCK_UN); + $file->fwrite($content, \strlen($content)); + $file->flock(\LOCK_UN); return $filename; } + + private function getJobFiles(string $queueName): array + { + $it = new \GlobIterator( + $this->getQueueDirectory($queueName).\DIRECTORY_SEPARATOR.'*.job', + \FilesystemIterator::KEY_AS_FILENAME + ); + $files = array_keys(iterator_to_array($it)); + natsort($files); + + return $files; + } } diff --git a/src/Driver/FlatFile/InsufficientPermissionsException.php b/src/Driver/FlatFile/InsufficientPermissionsException.php new file mode 100644 index 00000000..d8d6fcfc --- /dev/null +++ b/src/Driver/FlatFile/InsufficientPermissionsException.php @@ -0,0 +1,11 @@ + + */ +final class Driver implements \Bernard\Driver +{ + private array $queues = []; + + public function listQueues(): array + { + return array_keys($this->queues); + } + + public function createQueue(string $queueName): void + { + if (!\array_key_exists($queueName, $this->queues)) { + $this->queues[$queueName] = []; + } + } + + public function removeQueue(string $queueName): void + { + if (\array_key_exists($queueName, $this->queues)) { + unset($this->queues[$queueName]); + } + } + + public function pushMessage(string $queueName, string $message): void + { + $this->queues[$queueName][] = $message; + } + + public function popMessage(string $queueName, int $duration = 5): ?Message + { + if (!\array_key_exists($queueName, $this->queues) || \count($this->queues[$queueName]) < 1) { + return null; + } + + return new Message(array_shift($this->queues[$queueName])); + } + + public function acknowledgeMessage(string $queueName, mixed $receipt): void + { + // Noop + } + + public function info(): array + { + return []; + } + + public function countMessages(string $queueName): int + { + if (\array_key_exists($queueName, $this->queues)) { + return \count($this->queues[$queueName]); + } + + return 0; + } + + public function peekQueue(string $queueName, int $index = 0, int $limit = 20): array + { + if (\array_key_exists($queueName, $this->queues)) { + return \array_slice($this->queues[$queueName], $index, $limit); + } + + return []; + } +} diff --git a/src/Driver/InteropDriver.php b/src/Driver/InteropDriver.php deleted file mode 100644 index 4b087d78..00000000 --- a/src/Driver/InteropDriver.php +++ /dev/null @@ -1,150 +0,0 @@ -context = $context; - - $this->consumers = []; - } - - /** - * {@inheritdoc} - */ - public function listQueues() - { - return []; - } - - /** - * {@inheritdoc} - */ - public function createQueue($queueName) - { - if ($this->context instanceof AmqpContext) { - $this->context->declareQueue($this->createAmqpQueue($queueName)); - } - } - - /** - * {@inheritdoc} - */ - public function countMessages($queueName) - { - if ($this->context instanceof AmqpContext) { - return $this->context->declareQueue($this->createAmqpQueue($queueName)); - } - - return 0; - } - - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) - { - $queue = $this->context->createQueue($queueName); - $message = $this->context->createMessage($message); - - $this->context->createProducer()->send($queue, $message); - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - if ($message = $this->getQueueConsumer($queueName)->receive($duration * 1000)) { - return [$message->getBody(), $message]; - } - } - - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - $this->getQueueConsumer($queueName)->acknowledge($receipt); - } - - /** - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - return []; - } - - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) - { - if ($this->context instanceof AmqpContext) { - $queue = $this->createAmqpQueue($queueName); - - $this->context->deleteQueue($queue); - } - } - - /** - * {@inheritdoc} - */ - public function info() - { - return []; - } - - /** - * @param string $queueName - * - * @return PsrConsumer - */ - private function getQueueConsumer($queueName) - { - if (false == array_key_exists($queueName, $this->consumers)) { - $queue = $this->context->createQueue($queueName); - - $this->consumers[$queueName] = $this->context->createConsumer($queue); - } - - return $this->consumers[$queueName]; - } - - /** - * @param string $queueName - * - * @return AmqpQueue - */ - private function createAmqpQueue($queueName) - { - /** @var AmqpContext $context */ - $context = $this->context; - - $queue = $context->createQueue($queueName); - $queue->addFlag(AmqpQueue::FLAG_DURABLE); - - return $queue; - } -} diff --git a/src/Driver/IronMqDriver.php b/src/Driver/IronMqDriver.php deleted file mode 100644 index 8d77b7aa..00000000 --- a/src/Driver/IronMqDriver.php +++ /dev/null @@ -1,155 +0,0 @@ -ironmq = $ironmq; - } - - /** - * {@inheritdoc} - */ - public function listQueues() - { - $queueNames = array(); - $page = 0; - - while ($queues = $this->ironmq->getQueues($page, 100)) { - $queueNames += $this->pluck($queues, 'name'); - - // If we get 100 results the probability of another page is high. - if (count($queues) < 100) { - break; - } - - $page++; - } - - return $queueNames; - } - - /** - * {@inheritdoc} - */ - public function createQueue($queueName) - { - } - - /** - * {@inheritdoc} - */ - public function countMessages($queueName) - { - if ($info = $this->ironmq->getQueue($queueName)) { - return $info->size; - } - } - - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) - { - $this->ironmq->postMessage($queueName, $message); - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - if ($message = $this->cache->pop($queueName)) { - return $message; - } - - $timeout = IronMQ::GET_MESSAGE_TIMEOUT; - - $messages = $this->ironmq->getMessages($queueName, $this->prefetch, $timeout, $duration); - - if (!$messages) { - return array(null, null); - } - - foreach ($messages as $message) { - $this->cache->push($queueName, array($message->body, $message->id)); - } - - return $this->cache->pop($queueName); - } - - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - $this->ironmq->deleteMessage($queueName, $receipt); - } - - /** - * IronMQ does not support an offset when peeking messages. - * - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - if ($messages = $this->ironmq->peekMessages($queueName, $limit)) { - return $this->pluck($messages, 'body'); - } - - return []; - } - - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) - { - $this->ironmq->deleteQueue($queueName); - } - - /** - * {@inheritdoc} - */ - public function info() - { - return [ - 'prefetch' => $this->prefetch, - ]; - } - - /** - * The missing array_pluck but for objects array - * - * @param array $objects - * @param string $property - * - * @return array - */ - protected function pluck(array $objects, $property) - { - $function = function ($object) use ($property) { - return $object->$property; - }; - - return array_map($function, $objects); - } -} diff --git a/src/Driver/Message.php b/src/Driver/Message.php new file mode 100644 index 00000000..5e05c647 --- /dev/null +++ b/src/Driver/Message.php @@ -0,0 +1,17 @@ +queues = $queues; + $this->messages = $messages; + } + + public function listQueues(): array + { + return $this->queues->distinct('_id'); + } + + public function createQueue(string $queueName): void + { + $data = ['_id' => $queueName]; + + $this->queues->update($data, $data, ['upsert' => true]); + } + + public function removeQueue(string $queueName): void + { + $this->queues->remove(['_id' => $queueName]); + $this->messages->remove(['queue' => $queueName]); + } + + public function pushMessage(string $queueName, string $message): void + { + $data = [ + 'queue' => $queueName, + 'message' => $message, + 'sentAt' => new MongoDate(), + 'visible' => true, + ]; + + $this->messages->insert($data); + } + + public function popMessage(string $queueName, int $duration = 5): ?Message + { + $runtime = microtime(true) + $duration; + + while (microtime(true) < $runtime) { + $result = $this->messages->findAndModify( + ['queue' => $queueName, 'visible' => true], + ['$set' => ['visible' => false]], + ['message' => 1], + ['sort' => ['sentAt' => 1]] + ); + + if ($result) { + return new Message((string) $result['message'], (string) $result['_id']); + } + + usleep(10000); + } + + return null; + } + + public function acknowledgeMessage(string $queueName, mixed $receipt): void + { + $this->messages->remove([ + '_id' => new MongoId((string) $receipt), + 'queue' => $queueName, + ]); + } + + public function info(): array + { + return [ + 'queues' => (string) $this->queues, + 'messages' => (string) $this->messages, + ]; + } + + public function countMessages(string $queueName): int + { + return $this->messages->count([ + 'queue' => $queueName, + 'visible' => true, + ]); + } + + public function peekQueue(string $queueName, int $index = 0, int $limit = 20): array + { + $query = ['queue' => $queueName, 'visible' => true]; + $fields = ['_id' => 0, 'message' => 1]; + + $cursor = $this->messages + ->find($query, $fields) + ->sort(['sentAt' => 1]) + ->limit($limit) + ->skip($index); + + $mapper = fn ($result) => (string) $result['message']; + + return array_map($mapper, iterator_to_array($cursor, false)); + } +} diff --git a/src/Driver/MongoDBDriver.php b/src/Driver/MongoDBDriver.php deleted file mode 100644 index 09468005..00000000 --- a/src/Driver/MongoDBDriver.php +++ /dev/null @@ -1,150 +0,0 @@ -queues = $queues; - $this->messages = $messages; - } - - /** - * {@inheritdoc} - */ - public function listQueues() - { - return $this->queues->distinct('_id'); - } - - /** - * {@inheritdoc} - */ - public function createQueue($queueName) - { - $data = ['_id' => (string) $queueName]; - - $this->queues->update($data, $data, ['upsert' => true]); - } - - /** - * {@inheritdoc} - */ - public function countMessages($queueName) - { - return $this->messages->count([ - 'queue' => (string) $queueName, - 'visible' => true, - ]); - } - - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) - { - $data = [ - 'queue' => (string) $queueName, - 'message' => (string) $message, - 'sentAt' => new MongoDate(), - 'visible' => true, - ]; - - $this->messages->insert($data); - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - $runtime = microtime(true) + $duration; - - while (microtime(true) < $runtime) { - $result = $this->messages->findAndModify( - ['queue' => (string) $queueName, 'visible' => true], - ['$set' => ['visible' => false]], - ['message' => 1], - ['sort' => ['sentAt' => 1]] - ); - - if ($result) { - return [(string) $result['message'], (string) $result['_id']]; - } - - usleep(10000); - } - - return [null, null]; - } - - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - $this->messages->remove([ - '_id' => new MongoId((string) $receipt), - 'queue' => (string) $queueName, - ]); - } - - /** - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - $query = ['queue' => (string) $queueName, 'visible' => true]; - $fields = ['_id' => 0, 'message' => 1]; - - $cursor = $this->messages - ->find($query, $fields) - ->sort(['sentAt' => 1]) - ->limit($limit) - ->skip($index) - ; - - $mapper = function ($result) { - return (string) $result['message']; - }; - - return array_map($mapper, iterator_to_array($cursor, false)); - } - - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) - { - $this->queues->remove(['_id' => $queueName]); - $this->messages->remove(['queue' => (string) $queueName]); - } - - /** - * {@inheritdoc} - */ - public function info() - { - return [ - 'messages' => (string) $this->messages, - 'queues' => (string) $this->queues, - ]; - } -} diff --git a/src/Driver/Pheanstalk/Driver.php b/src/Driver/Pheanstalk/Driver.php new file mode 100644 index 00000000..3ee8cbbf --- /dev/null +++ b/src/Driver/Pheanstalk/Driver.php @@ -0,0 +1,64 @@ +pheanstalk->listTubes(); + } + + public function createQueue(string $queueName): void + { + } + + public function removeQueue(string $queueName): void + { + } + + public function pushMessage(string $queueName, string $message): void + { + $this->pheanstalk->useTube($queueName)->put($message); + } + + public function popMessage(string $queueName, int $duration = 5): ?Message + { + if ($job = $this->pheanstalk->watchOnly($queueName)->reserveFromTube($queueName, $duration)) { + return new Message($job->getData(), $job); + } + + return null; + } + + public function acknowledgeMessage(string $queueName, mixed $receipt): void + { + $this->pheanstalk->delete($receipt); + } + + public function info(): array + { + return iterator_to_array($this->pheanstalk->stats()); + } + + public function countMessages(string $queueName): int + { + $stats = $this->pheanstalk->statsTube($queueName); + + return (int) $stats['current-jobs-ready']; + } + + public function peekQueue(string $queueName, int $index = 0, int $limit = 20): array + { + return []; + } +} diff --git a/src/Driver/PheanstalkDriver.php b/src/Driver/PheanstalkDriver.php deleted file mode 100644 index 8ba06a37..00000000 --- a/src/Driver/PheanstalkDriver.php +++ /dev/null @@ -1,102 +0,0 @@ -pheanstalk = $pheanstalk; - } - - /** - * {@inheritdoc} - */ - public function listQueues() - { - return $this->pheanstalk->listTubes(); - } - - /** - * {@inheritdoc} - */ - public function createQueue($queueName) - { - } - - /** - * {@inheritdoc} - */ - public function countMessages($queueName) - { - $stats = $this->pheanstalk->statsTube($queueName); - - return $stats['current-jobs-ready']; - } - - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) - { - $this->pheanstalk->putInTube($queueName, $message); - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - if ($job = $this->pheanstalk->reserveFromTube($queueName, $duration)) { - return [$job->getData(), $job]; - } - - return array(null, null); - } - - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - $this->pheanstalk->delete($receipt); - } - - /** - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - return []; - } - - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) - { - } - - /** - * {@inheritdoc} - */ - public function info() - { - return $this->pheanstalk - ->stats() - ->getArrayCopy() - ; - } -} diff --git a/src/Driver/PhpAmqpDriver.php b/src/Driver/PhpAmqpDriver.php deleted file mode 100644 index 402df9f5..00000000 --- a/src/Driver/PhpAmqpDriver.php +++ /dev/null @@ -1,174 +0,0 @@ -connection = $connection; - $this->exchange = $exchange; - $this->defaultMessageParams = $defaultMessageParams; - } - - /** - * Returns a list of all queue names. - * - * @return array - */ - public function listQueues() - { - } - - /** - * Create a queue. - * - * @param string $queueName - */ - public function createQueue($queueName) - { - $channel = $this->getChannel(); - $channel->exchange_declare($this->exchange, 'direct', false, true, false); - $channel->queue_declare($queueName, false, true, false, false); - $channel->queue_bind($queueName, $this->exchange, $queueName); - } - - /** - * Count the number of messages in queue. This can be a approximately number. - * - * @param string $queueName - * - * @return int - */ - public function countMessages($queueName) - { - list(,$messageCount) = $this->getChannel()->queue_declare($queueName, true); - - return $messageCount; - } - - /** - * Insert a message at the top of the queue. - * - * @param string $queueName - * @param string $message - */ - public function pushMessage($queueName, $message) - { - $amqpMessage = new AMQPMessage($message, $this->defaultMessageParams); - $this->getChannel()->basic_publish($amqpMessage, $this->exchange, $queueName); - } - - /** - * Remove the next message in line. And if no message is available - * wait $duration seconds. - * - * @param string $queueName - * @param int $duration - * - * @return array An array like array($message, $receipt); - */ - public function popMessage($queueName, $duration = 5) - { - $runtime = microtime(true) + $duration; - - while (microtime(true) < $runtime) { - $message = $this->getChannel()->basic_get($queueName); - - if ($message) { - return [$message->body, $message->get('delivery_tag')]; - } - - // sleep for 10 ms to prevent hammering CPU - usleep(10000); - } - - return [null, null]; - } - - /** - * If the driver supports it, this will be called when a message - * have been consumed. - * - * @param string $queueName - * @param mixed $receipt - */ - public function acknowledgeMessage($queueName, $receipt) - { - $this->getChannel()->basic_ack($receipt); - } - - /** - * Returns a $limit numbers of messages without removing them - * from the queue. - * - * @param string $queueName - * @param int $index - * @param int $limit - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - } - - /** - * Removes the queue. - * - * @param string $queueName - */ - public function removeQueue($queueName) - { - $this->getChannel()->queue_delete($queueName); - } - - /** - * @return array - */ - public function info() - { - } - - public function __destruct() - { - if (null !== $this->channel) { - $this->channel->close(); - } - } - - private function getChannel() - { - if (null === $this->channel) { - $this->channel = $this->connection->channel(); - } - - return $this->channel; - } -} diff --git a/src/Driver/PhpRedisDriver.php b/src/Driver/PhpRedisDriver.php deleted file mode 100644 index ebba970b..00000000 --- a/src/Driver/PhpRedisDriver.php +++ /dev/null @@ -1,121 +0,0 @@ -redis = $redis; - } - - /** - * {@inheritdoc} - */ - public function listQueues() - { - return $this->redis->sMembers('queues'); - } - - /** - * {@inheritdoc} - */ - public function createQueue($queueName) - { - $this->redis->sAdd('queues', $queueName); - } - - /** - * {@inheritdoc} - */ - public function countMessages($queueName) - { - return $this->redis->lLen(self::QUEUE_PREFIX . $queueName); - } - - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) - { - $this->redis->rpush($this->resolveKey($queueName), $message); - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - // When PhpRedis is set up with an Redis::OPT_PREFIX - // it does set the prefix to the key and to the timeout value something like: - // "BLPOP" "bernard:queue:my-queue" "bernard:5" - // - // To set the resolved key in an array seems fixing this issue. We get: - // "BLPOP" "bernard:queue:my-queue" "5" - // - // see https://github.com/nicolasff/phpredis/issues/158 - list(, $message) = $this->redis->blpop([$this->resolveKey($queueName)], $duration) ?: null; - - return [$message, null]; - } - - /** - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - $limit += $index - 1; - - return $this->redis->lRange($this->resolveKey($queueName), $index, $limit); - } - - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - } - - /** - * {@inheritdoc} - */ - public function removeQueue($queueName) - { - $this->redis->sRem('queues', $queueName); - $this->redis->del($this->resolveKey($queueName)); - } - - /** - * {@inheritdoc} - */ - public function info() - { - return $this->redis->info(); - } - - /** - * Transform the queueName into a key. - * - * @param string $queueName - * - * @return string - */ - protected function resolveKey($queueName) - { - return self::QUEUE_PREFIX . $queueName; - } -} diff --git a/src/Driver/PredisDriver.php b/src/Driver/PredisDriver.php deleted file mode 100644 index 8c5421f8..00000000 --- a/src/Driver/PredisDriver.php +++ /dev/null @@ -1,48 +0,0 @@ -redis = $redis; - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - list(, $message) = $this->redis->blpop($this->resolveKey($queueName), $duration) ?: null; - - return [$message, null]; - } - - /** - * {@inheritdoc} - */ - public function info() - { - // Temporarily change the command use to get info as earlier and newer redis - // versions breaks it into sections. - $commandClass = $this->redis->getProfile()->getCommandClass('info'); - $this->redis->getProfile()->defineCommand('info', 'Predis\Command\ServerInfo'); - - $info = $this->redis->info(); - - $this->redis->getProfile()->defineCommand('info', $commandClass); - - return $info; - } -} diff --git a/src/Driver/PrefetchMessageCache.php b/src/Driver/PrefetchMessageCache.php index 91b07662..113d3814 100644 --- a/src/Driver/PrefetchMessageCache.php +++ b/src/Driver/PrefetchMessageCache.php @@ -1,21 +1,20 @@ get($queueName); $cache->enqueue($message); @@ -24,28 +23,22 @@ public function push($queueName, array $message) /** * Get the next message in line. Or nothing if there is no more * in the cache. - * - * @param string $queueName - * - * @return array|null */ - public function pop($queueName) + public function pop(string $queueName): ?Message { $cache = $this->get($queueName); if (!$cache->isEmpty()) { return $cache->dequeue(); } + + return null; } /** * Create the queue cache internally if it doesn't yet exists. - * - * @param string $queueName - * - * @return \SplQueue */ - protected function get($queueName) + private function get(string $queueName): \SplQueue { if (isset($this->caches[$queueName])) { return $this->caches[$queueName]; diff --git a/src/Driver/SqsDriver.php b/src/Driver/SqsDriver.php deleted file mode 100644 index 973dc9d5..00000000 --- a/src/Driver/SqsDriver.php +++ /dev/null @@ -1,274 +0,0 @@ -sqs = $sqs; - $this->queueUrls = $queueUrls; - } - - /** - * {@inheritdoc} - */ - public function listQueues() - { - $result = $this->sqs->listQueues(); - - if (!$queueUrls = $result->get('QueueUrls')) { - return array_keys($this->queueUrls); - } - - foreach ($queueUrls as $queueUrl) { - if (in_array($queueUrl, $this->queueUrls)) { - continue; - } - - $queueName = current(array_reverse(explode('/', $queueUrl))); - $this->queueUrls[$queueName] = $queueUrl; - } - - return array_keys($this->queueUrls); - } - - /** - * {@inheritdoc} - * - * @link http://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sqs-2012-11-05.html#createqueue - * - * @return void - * - * @throws SqsException - */ - public function createQueue($queueName) - { - if($this->queueExists($queueName)){ - return; - } - - $parameters = [ - 'QueueName' => $queueName, - ]; - - if($this->isFifoQueue($queueName)) { - $parameters['Attributes'] = [ - 'FifoQueue' => 'true', - ]; - } - - $result = $this->sqs->createQueue($parameters); - - $this->queueUrls[$queueName] = $result['QueueUrl']; - } - - /** - * @param string $queueName - * - * @return bool - * - * @throws SqsException - */ - private function queueExists($queueName) - { - try { - $this->resolveUrl($queueName); - return true; - } catch (\InvalidArgumentException $exception) { - return false; - } catch (SqsException $exception) { - if ($previousException = $exception->getPrevious()) { - if ($previousException->getCode() === self::AWS_SQS_EXCEPTION_BAD_REQUEST) { - return false; - } - } - - throw $exception; - } - } - - /** - * @param string $queueName - * - * @return bool - */ - private function isFifoQueue($queueName) - { - return $this->endsWith($queueName, self::AWS_SQS_FIFO_SUFFIX); - } - - /** - * @param string $haystack - * @param string $needle - * - * @return bool - */ - private function endsWith($haystack, $needle) - { - $length = strlen($needle); - if ($length === 0) { - return true; - } - - return (substr($haystack, -$length) === $needle); - } - - /** - * {@inheritdoc} - */ - public function countMessages($queueName) - { - $queueUrl = $this->resolveUrl($queueName); - - $result = $this->sqs->getQueueAttributes([ - 'QueueUrl' => $queueUrl, - 'AttributeNames' => ['ApproximateNumberOfMessages'], - ]); - - if (isset($result['Attributes']['ApproximateNumberOfMessages'])) { - return (int)$result['Attributes']['ApproximateNumberOfMessages']; - } - - return 0; - } - - /** - * {@inheritdoc} - */ - public function pushMessage($queueName, $message) - { - $queueUrl = $this->resolveUrl($queueName); - - $parameters = [ - 'QueueUrl' => $queueUrl, - 'MessageBody' => $message, - ]; - - if($this->isFifoQueue($queueName)){ - $parameters['MessageGroupId'] = __METHOD__; - $parameters['MessageDeduplicationId'] = md5($message); - } - - $this->sqs->sendMessage($parameters); - } - - /** - * {@inheritdoc} - */ - public function popMessage($queueName, $duration = 5) - { - if ($message = $this->cache->pop($queueName)) { - return $message; - } - - $queueUrl = $this->resolveUrl($queueName); - - $result = $this->sqs->receiveMessage([ - 'QueueUrl' => $queueUrl, - 'MaxNumberOfMessages' => $this->prefetch, - 'WaitTimeSeconds' => $duration, - ]); - - if (!$result || !$messages = $result->get('Messages')) { - return [null, null]; - } - - foreach ($messages as $message) { - $this->cache->push($queueName, [$message['Body'], $message['ReceiptHandle']]); - } - - return $this->cache->pop($queueName); - } - - /** - * {@inheritdoc} - */ - public function acknowledgeMessage($queueName, $receipt) - { - $queueUrl = $this->resolveUrl($queueName); - - $this->sqs->deleteMessage([ - 'QueueUrl' => $queueUrl, - 'ReceiptHandle' => $receipt, - ]); - } - - /** - * {@inheritdoc} - */ - public function peekQueue($queueName, $index = 0, $limit = 20) - { - return []; - } - - /** - * {@inheritdoc} - * - * @link http://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sqs-2012-11-05.html#deletequeue - */ - public function removeQueue($queueName) - { - $queueUrl = $this->resolveUrl($queueName); - - $this->sqs->deleteQueue([ - 'QueueUrl' => $queueUrl, - ]); - } - - /** - * {@inheritdoc} - */ - public function info() - { - return [ - 'prefetch' => $this->prefetch, - ]; - } - - /** - * AWS works with queue URLs rather than queue names. Returns either queue URL (if queue exists) for given name or null if not. - * - * @param string $queueName - * - * @return mixed - * - * @throws SqsException - */ - protected function resolveUrl($queueName) - { - if (isset($this->queueUrls[$queueName])) { - return $this->queueUrls[$queueName]; - } - - $result = $this->sqs->getQueueUrl(['QueueName' => $queueName]); - - if ($result && $queueUrl = $result->get('QueueUrl')) { - return $this->queueUrls[$queueName] = $queueUrl; - } - - throw new \InvalidArgumentException('Queue "' . $queueName .'" cannot be resolved to an url.'); - } -} diff --git a/src/Envelope.php b/src/Envelope.php index 45176acf..23b023a5 100644 --- a/src/Envelope.php +++ b/src/Envelope.php @@ -1,5 +1,7 @@ message = $message; - $this->delay = (int) $delay; - $this->class = get_class($message); + $this->delay = $delay; + $this->class = $message::class; $this->timestamp = time(); } @@ -41,12 +38,9 @@ public function getMessage() return $this->message; } - /** - * @return bool - */ - public function isDelayed() + public function isDelayed(): bool { - return ($this->delay > 0); + return $this->delay > 0; } /** diff --git a/src/Event/EnvelopeEvent.php b/src/Event/EnvelopeEvent.php index 3919a78d..e3bef932 100644 --- a/src/Event/EnvelopeEvent.php +++ b/src/Event/EnvelopeEvent.php @@ -1,22 +1,18 @@ envelope = $envelope; diff --git a/src/Event/PingEvent.php b/src/Event/PingEvent.php index 63d6c299..0c3d8dff 100644 --- a/src/Event/PingEvent.php +++ b/src/Event/PingEvent.php @@ -1,20 +1,16 @@ queue = $queue; diff --git a/src/Event/RejectEnvelopeEvent.php b/src/Event/RejectEnvelopeEvent.php index 949a83b0..ec9eee84 100644 --- a/src/Event/RejectEnvelopeEvent.php +++ b/src/Event/RejectEnvelopeEvent.php @@ -1,20 +1,17 @@ format($event->getEnvelope(), $event->getException())); } /** - * @param Envelope $envelope * @param mixed $exception * * @return string @@ -31,7 +26,7 @@ protected function format(Envelope $envelope, $exception) { if ($exception instanceof Exception || $exception instanceof Throwable) { $replacements = [ - '{class}' => get_class($exception), + '{class}' => $exception::class, '{message}' => $exception->getMessage(), '{envelope}' => $envelope->getName(), ]; @@ -40,9 +35,10 @@ protected function format(Envelope $envelope, $exception) } $replacements = [ - '{type}' => is_object($exception) ? get_class($exception) : gettype($exception), - '{envelope}' => $envelope->getName() + '{type}' => \is_object($exception) ? $exception::class : \gettype($exception), + '{envelope}' => $envelope->getName(), ]; + return strtr('[bernard] caught unknown error type {type} while processing {envelope}.', $replacements); } diff --git a/src/EventListener/FailureSubscriber.php b/src/EventListener/FailureSubscriber.php index b0c5a320..fa1ec2b1 100644 --- a/src/EventListener/FailureSubscriber.php +++ b/src/EventListener/FailureSubscriber.php @@ -1,21 +1,19 @@ name = $name; } - /** - * @param RejectEnvelopeEvent $event - */ - public function onReject(RejectEnvelopeEvent $event) + public function onReject(RejectEnvelopeEvent $event): void { $envelope = $event->getEnvelope(); $message = $envelope->getMessage(); diff --git a/src/EventListener/LoggerSubscriber.php b/src/EventListener/LoggerSubscriber.php index 3155e135..d878370c 100644 --- a/src/EventListener/LoggerSubscriber.php +++ b/src/EventListener/LoggerSubscriber.php @@ -1,5 +1,7 @@ logger = $logger; } - /** - * @param EnvelopeEvent $event - */ - public function onProduce(EnvelopeEvent $event) + public function onProduce(EnvelopeEvent $event): void { $this->logger->info('[bernard] produced {envelope} onto {queue}.', [ 'envelope' => $event->getEnvelope(), @@ -33,20 +26,14 @@ public function onProduce(EnvelopeEvent $event) ]); } - /** - * @param EnvelopeEvent $event - */ - public function onInvoke(EnvelopeEvent $event) + public function onInvoke(EnvelopeEvent $event): void { $this->logger->info('[bernard] invoking receiver for {envelope}.', [ 'envelope' => $event->getEnvelope(), ]); } - /** - * @param RejectEnvelopeEvent $event - */ - public function onReject(RejectEnvelopeEvent $event) + public function onReject(RejectEnvelopeEvent $event): void { $this->logger->error('[bernard] caught exception {exception} while processing {envelope}.', [ 'envelope' => $event->getEnvelope(), diff --git a/src/Exception.php b/src/Exception.php new file mode 100644 index 00000000..2891222e --- /dev/null +++ b/src/Exception.php @@ -0,0 +1,12 @@ +getName())), '-'); + } +} diff --git a/src/Message/PlainMessage.php b/src/Message/PlainMessage.php index 2217e42b..00590424 100644 --- a/src/Message/PlainMessage.php +++ b/src/Message/PlainMessage.php @@ -1,24 +1,24 @@ arguments = $arguments; } + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + /** * @return array */ @@ -35,60 +43,66 @@ public function all() } /** + * Returns the argument if found or null. + * * @param string $name * * @return mixed */ public function get($name) { - return $this->offsetGet($name); + return $this->has($name) ? $this->arguments[$name] : null; } /** + * Checks whether the arguments contain the given key. + * * @param string $name * * @return bool */ public function has($name) { - return $this->offsetExists($name); + return \array_key_exists($name, $this->arguments); } - public function offsetExists($offset) + public function offsetGet($offset) { - return array_key_exists($offset, $this->arguments); + return $this->get($offset); } - public function offsetGet($offset) + public function offsetExists($offset) { - return $this->offsetExists($offset) ? $this->arguments[$offset] : null; + return $this->has($offset); } - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { throw new \LogicException('Message is immutable'); } - public function offsetUnset($offset) + public function offsetUnset($offset): void { throw new \LogicException('Message is immutable'); } - /** - * {@inheritdoc} - */ - public function getName() + public function __get($property) { - return $this->name; + return $this->get($property); } - public function __get($property) + public function __isset($property) + { + return $this->has($property); + } + + public function __set($property, $value): void { - return $this->offsetGet($property); + throw new \LogicException('Message is immutable'); } - public function __set($property, $value) + public function __unset($property): void { - $this->offsetSet($property, $value); + throw new \LogicException('Message is immutable'); } } diff --git a/src/Normalizer/AbstractAggregateNormalizerAware.php b/src/Normalizer/AbstractAggregateNormalizerAware.php deleted file mode 100644 index 611a5721..00000000 --- a/src/Normalizer/AbstractAggregateNormalizerAware.php +++ /dev/null @@ -1,19 +0,0 @@ -aggregate = $aggregate; - } -} diff --git a/src/Normalizer/EnvelopeNormalizer.php b/src/Normalizer/EnvelopeNormalizer.php index 1e10ec16..27db120c 100644 --- a/src/Normalizer/EnvelopeNormalizer.php +++ b/src/Normalizer/EnvelopeNormalizer.php @@ -1,17 +1,28 @@ aggregate = $aggregate; + } + /** * {@inheritdoc} */ @@ -29,9 +40,12 @@ public function normalize($object, $format = null, array $context = []) */ public function denormalize($data, $class, $format = null, array $context = []) { - Assertion::choicesNotEmpty($data, ['message', 'class', 'timestamp']); - - Assertion::classExists($data['class']); + try { + Assertion::choicesNotEmpty($data, ['message', 'class', 'timestamp']); + Assertion::classExists($data['class']); + } catch (AssertionFailedException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } $envelope = new Envelope($this->aggregate->denormalize($data['message'], $data['class'])); @@ -46,7 +60,7 @@ public function denormalize($data, $class, $format = null, array $context = []) */ public function supportsDenormalization($data, $type, $format = null) { - return $type === 'Bernard\Envelope'; + return $type === Envelope::class; } /** @@ -58,13 +72,17 @@ public function supportsNormalization($data, $format = null) } /** - * @param Envelope $envelope - * @param string $property - * @param mixed $value + * @param string $property + * @param mixed $value */ - private function forcePropertyValue(Envelope $envelope, $property, $value) + private function forcePropertyValue(Envelope $envelope, $property, $value): void { - $property = new \ReflectionProperty($envelope, $property); + try { + $property = new \ReflectionProperty($envelope, $property); + } catch (\ReflectionException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + $property->setAccessible(true); $property->setValue($envelope, $value); } diff --git a/src/Normalizer/PlainMessageNormalizer.php b/src/Normalizer/PlainMessageNormalizer.php index 29e55828..964e4e56 100644 --- a/src/Normalizer/PlainMessageNormalizer.php +++ b/src/Normalizer/PlainMessageNormalizer.php @@ -1,16 +1,17 @@ getMessage(), $e->getCode(), $e); + } return new PlainMessage($data['name'], $data['arguments']); } @@ -40,7 +45,7 @@ public function denormalize($data, $class, $format = null, array $context = []) */ public function supportsDenormalization($data, $type, $format = null) { - return $type === 'Bernard\Message\PlainMessage'; + return $type === PlainMessage::class; } /** diff --git a/src/Producer.php b/src/Producer.php index f4cbc7ed..0a8aa012 100644 --- a/src/Producer.php +++ b/src/Producer.php @@ -1,22 +1,17 @@ queues = $queues; @@ -24,11 +19,11 @@ public function __construct(QueueFactory $queues, EventDispatcherInterface $disp } /** - * @param Message $message + * Produce a message with optional delay in seconds. + * * @param string|null $queueName - * @param int $delay Delay (in seconds) */ - public function produce(Message $message, $queueName = null, $delay = 0) + public function produce(Message $message, $queueName = null, int $delay = 0): void { $queueName = $queueName ?: Util::guessQueue($message); @@ -37,4 +32,9 @@ public function produce(Message $message, $queueName = null, $delay = 0) $this->dispatcher->dispatch(new EnvelopeEvent($envelope, $queue), BernardEvents::PRODUCE); } + + private function dispatch($eventName, EnvelopeEvent $event): void + { + $this->dispatcher->dispatch($event, $eventName); + } } diff --git a/src/Queue.php b/src/Queue.php index 1093087a..1660f970 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -1,15 +1,11 @@ closed = true; } @@ -34,7 +33,7 @@ public function close() * * {@inheritdoc} */ - public function acknowledge(Envelope $envelope) + public function acknowledge(Envelope $envelope): void { $this->errorIfClosed(); } @@ -42,7 +41,7 @@ public function acknowledge(Envelope $envelope) /** * @throws InvalidOperationException */ - protected function errorIfClosed() + protected function errorIfClosed(): void { if ($this->closed) { throw new InvalidOperationException(sprintf('Queue "%s" is closed.', $this->name)); diff --git a/src/Queue/InMemoryQueue.php b/src/Queue/InMemoryQueue.php index 0dfd0da8..2fb0f829 100644 --- a/src/Queue/InMemoryQueue.php +++ b/src/Queue/InMemoryQueue.php @@ -1,13 +1,13 @@ errorIfClosed(); @@ -67,11 +67,11 @@ public function peek($index = 0, $limit = 20) { $this->errorIfClosed(); - $envelopes = array(); + $envelopes = []; $queue = clone $this->queue; $key = 0; - while ($queue->count() && count($envelopes) < $limit && $envelope = $queue->dequeue()) { + while ($queue->count() && \count($envelopes) < $limit && $envelope = $queue->dequeue()) { if ($key++ < $index) { continue; } diff --git a/src/Queue/PersistentQueue.php b/src/Queue/PersistentQueue.php index ebb9455b..a076874e 100644 --- a/src/Queue/PersistentQueue.php +++ b/src/Queue/PersistentQueue.php @@ -1,16 +1,15 @@ errorIfClosed(); @@ -56,7 +53,7 @@ public function count() /** * {@inheritdoc} */ - public function close() + public function close(): void { parent::close(); @@ -66,7 +63,7 @@ public function close() /** * {@inheritdoc} */ - public function enqueue(Envelope $envelope) + public function enqueue(Envelope $envelope): void { $this->errorIfClosed(); @@ -82,7 +79,7 @@ public function enqueue(Envelope $envelope) } } - private function assertDriverIsDelayable() + private function assertDriverIsDelayable(): void { if (!($this->driver instanceof DelayableDriver)) { throw new InvalidOperationException('This driver can\'t manage delayed messages'); @@ -92,7 +89,7 @@ private function assertDriverIsDelayable() /** * {@inheritdoc} */ - public function acknowledge(Envelope $envelope) + public function acknowledge(Envelope $envelope): void { $this->errorIfClosed(); @@ -106,18 +103,18 @@ public function acknowledge(Envelope $envelope) /** * {@inheritdoc} * - * @param int $duration Number of seconds to keep polling for messages. + * @param int $duration number of seconds to keep polling for messages */ public function dequeue($duration = 5) { $this->errorIfClosed(); - list($serialized, $receipt) = $this->driver->popMessage($this->name, $duration); + $driverMessage = $this->driver->popMessage($this->name, $duration); - if ($serialized) { - $envelope = $this->serializer->unserialize($serialized); + if ($driverMessage) { + $envelope = $this->serializer->unserialize($driverMessage->message); - $this->receipts->attach($envelope, $receipt); + $this->receipts->attach($envelope, $driverMessage->receipt); return $envelope; } @@ -132,6 +129,6 @@ public function peek($index = 0, $limit = 20) $messages = $this->driver->peekQueue($this->name, $index, $limit); - return array_map(array($this->serializer, 'unserialize'), $messages); + return array_map([$this->serializer, 'unserialize'], $messages); } } diff --git a/src/Queue/RoundRobinQueue.php b/src/Queue/RoundRobinQueue.php index 2e5f7a46..d9dddbb6 100644 --- a/src/Queue/RoundRobinQueue.php +++ b/src/Queue/RoundRobinQueue.php @@ -1,13 +1,12 @@ verifyEnvelope($envelope); @@ -55,7 +54,7 @@ public function dequeue() $envelope = null; $checked = []; - while (count($checked) < count($this->queues)) { + while (\count($checked) < \count($this->queues)) { $queue = current($this->queues); $envelope = $queue->dequeue(); if (false === next($this->queues)) { @@ -75,7 +74,7 @@ public function dequeue() /** * {@inheritdoc} */ - public function close() + public function close(): void { if ($this->closed) { return; @@ -105,13 +104,13 @@ public function peek($index = 0, $limit = 20) // noop } - while (count($envelopes) < $limit && count($drained) < $it->count()) { + while (\count($envelopes) < $limit && \count($drained) < $it->count()) { $queue = $it->current(); $name = $it->key(); if ($peeked = $queue->peek($indexes[$name], 1)) { if ($shift < $index) { - $shift++; - $indexes[$name]++; + ++$shift; + ++$indexes[$name]; } else { $envelopes[] = array_shift($peeked); } @@ -127,12 +126,10 @@ public function peek($index = 0, $limit = 20) /** * {@inheritdoc} */ - public function acknowledge(Envelope $envelope) + public function acknowledge(Envelope $envelope): void { if (!$this->envelopes->contains($envelope)) { - throw new \DomainException( - 'Unrecognized queue specified: ' . $envelope->getName() - ); + throw new \DomainException('Unrecognized queue specified: '.$envelope->getName()); } $queue = $this->envelopes[$envelope]; @@ -151,6 +148,7 @@ public function __toString() /** * @return int */ + #[\ReturnTypeWillChange] public function count() { return array_sum(array_map('count', $this->queues)); @@ -159,7 +157,7 @@ public function count() /** * @param Queue[] $queues */ - protected function validateQueues(array $queues) + protected function validateQueues(array $queues): void { if (empty($queues)) { throw new \DomainException('$queues cannot be empty'); @@ -167,9 +165,7 @@ protected function validateQueues(array $queues) $filtered = array_filter( $queues, - function ($queue) { - return !$queue instanceof Queue; - } + fn ($queue) => !$queue instanceof Queue ); if (!empty($filtered)) { throw new \DomainException('All elements of $queues must implement Queue'); @@ -185,24 +181,19 @@ protected function indexQueues(array $queues) { return array_combine( array_map( - function ($queue) { - return (string) $queue; - }, + fn ($queue) => (string) $queue, $queues ), $queues ); } - /** - * @param Envelope $envelope - */ - protected function verifyEnvelope(Envelope $envelope) + protected function verifyEnvelope(Envelope $envelope): void { $queue = $envelope->getName(); if (isset($this->queues[$queue])) { return; } - throw new \DomainException('Unrecognized queue specified: ' . $queue); + throw new \DomainException('Unrecognized queue specified: '.$queue); } } diff --git a/src/QueueFactory.php b/src/QueueFactory.php index eab6be16..96c0889e 100644 --- a/src/QueueFactory.php +++ b/src/QueueFactory.php @@ -1,12 +1,12 @@ queues); + return \count($this->queues); } /** @@ -53,7 +54,7 @@ public function exists($queueName) /** * {@inheritdoc} */ - public function remove($queueName) + public function remove($queueName): void { if ($this->exists($queueName)) { $this->queues[$queueName]->close(); diff --git a/src/QueueFactory/PersistentFactory.php b/src/QueueFactory/PersistentFactory.php index e45f6c46..754c8fb3 100644 --- a/src/QueueFactory/PersistentFactory.php +++ b/src/QueueFactory/PersistentFactory.php @@ -1,5 +1,7 @@ queues = []; @@ -59,25 +55,18 @@ public function all() */ public function exists($queueName) { - return isset($this->queues[$queueName]) ?: in_array($queueName, $this->driver->listQueues()); + return isset($this->queues[$queueName]) ?: \in_array($queueName, $this->driver->listQueues()); } - /** - * @return int - */ - public function count() + public function count(): int { - $queues = $this->driver->listQueues(); - if (null === $queues) { - return 0; - } - return count($queues); + return \count($this->driver->listQueues()); } /** * {@inheritdoc} */ - public function remove($queueName) + public function remove($queueName): void { if (!$this->exists($queueName)) { return; diff --git a/src/Receiver.php b/src/Receiver.php new file mode 100644 index 00000000..a171965f --- /dev/null +++ b/src/Receiver.php @@ -0,0 +1,16 @@ +callable = $callable; + } + + /** + * {@inheritdoc} + */ + public function receive(Message $message): void + { + \call_user_func($this->callable, $message); + } +} diff --git a/src/Router.php b/src/Router.php index 814099cf..e01a2703 100644 --- a/src/Router.php +++ b/src/Router.php @@ -1,20 +1,22 @@ get($envelope->getClass()); - - if (!is_callable($receiver)) { - throw new ReceiverNotFoundException(sprintf('No receiver found for class "%s".', $envelope->getClass())); - } - - return $receiver; - } - /** * {@inheritdoc} */ @@ -37,4 +25,12 @@ protected function get($name) return null; } + + /** + * {@inheritdoc} + */ + protected function getName(Envelope $envelope) + { + return $envelope->getClass(); + } } diff --git a/src/Router/ContainerAwareRouter.php b/src/Router/ContainerAwareRouter.php deleted file mode 100644 index 71c1adba..00000000 --- a/src/Router/ContainerAwareRouter.php +++ /dev/null @@ -1,43 +0,0 @@ -container = $container; - - parent::__construct($receivers); - } - - /** - * {@inheritdoc} - */ - protected function get($name) - { - $serviceId = parent::get($name); - $serviceId = null !== $serviceId ? $serviceId : ''; - - return $this->container->get($serviceId); - } - - /** - * {@inheritdoc} - */ - protected function accepts($receiver) - { - return $this->container->has($receiver); - } -} diff --git a/src/Router/ContainerReceiverResolver.php b/src/Router/ContainerReceiverResolver.php new file mode 100644 index 00000000..6dd59485 --- /dev/null +++ b/src/Router/ContainerReceiverResolver.php @@ -0,0 +1,44 @@ +container = $container; + } + + /** + * {@inheritdoc} + */ + public function accepts($receiver) + { + return $this->container->has($receiver); + } + + /** + * {@inheritdoc} + */ + public function resolve($receiver, Envelope $envelope) + { + try { + $receiver = $this->container->get($receiver); + } catch (NotFoundExceptionInterface $e) { + return null; + } + + return parent::resolve($receiver, $envelope); + } +} diff --git a/src/Router/LeagueContainerAwareRouter.php b/src/Router/LeagueContainerAwareRouter.php deleted file mode 100644 index 4774e2ba..00000000 --- a/src/Router/LeagueContainerAwareRouter.php +++ /dev/null @@ -1,42 +0,0 @@ -container = $container; - - parent::__construct($receivers); - } - - /** - * {@inheritdoc} - */ - protected function get($name) - { - $serviceId = parent::get($name); - - return $this->container->get($serviceId); - } - - /** - * {@inheritdoc} - */ - protected function accepts($receiver) - { - return $this->container->has($receiver); - } -} diff --git a/src/Router/PimpleAwareRouter.php b/src/Router/PimpleAwareRouter.php deleted file mode 100644 index e19dd16e..00000000 --- a/src/Router/PimpleAwareRouter.php +++ /dev/null @@ -1,40 +0,0 @@ -pimple = $pimple; - - parent::__construct($receivers); - } - - /** - * {@inheritdoc} - */ - protected function get($name) - { - return $this->pimple[parent::get($name)]; - } - - /** - * {@inheritdoc} - */ - protected function accepts($receiver) - { - return isset($this->pimple[$receiver]); - } -} diff --git a/src/Router/ReceiverMapRouter.php b/src/Router/ReceiverMapRouter.php new file mode 100644 index 00000000..8a32ec10 --- /dev/null +++ b/src/Router/ReceiverMapRouter.php @@ -0,0 +1,80 @@ +receiverResolver = $receiverResolver; + + foreach ($receivers as $name => $receiver) { + $this->add($name, $receiver); + } + } + + /** + * @param string $name + * @param mixed $receiver + */ + private function add($name, $receiver): void + { + if (!$this->receiverResolver->accepts($receiver)) { + throw new \InvalidArgumentException(sprintf('Receiver "%s" is not supported.', $receiver)); + } + + $this->receivers[$name] = $receiver; + } + + /** + * {@inheritdoc} + */ + public function route(Envelope $envelope) + { + $receiver = $this->get($this->getName($envelope)); + $receiver = $this->receiverResolver->resolve($receiver, $envelope); + + if (null === $receiver) { + throw new ReceiverNotFoundException(sprintf('No receiver found with name "%s".', $envelope->getName())); + } + + return $receiver; + } + + /** + * @param string $name + * + * @return mixed + */ + protected function get($name) + { + return $this->receivers[$name] ?? null; + } + + /** + * Returns the (message) name to look for in the receiver map. + * + * @return string + */ + protected function getName(Envelope $envelope) + { + return $envelope->getName(); + } +} diff --git a/src/Router/ReceiverResolver.php b/src/Router/ReceiverResolver.php new file mode 100644 index 00000000..76092f04 --- /dev/null +++ b/src/Router/ReceiverResolver.php @@ -0,0 +1,32 @@ +getName())]; + } + + // Receiver is still not a callable which means it's not a valid receiver. + if (\is_callable($receiver) == false) { + return null; + } + + return new Receiver\CallableReceiver($receiver); + } +} diff --git a/src/Router/SimpleRouter.php b/src/Router/SimpleRouter.php deleted file mode 100644 index dba86d0b..00000000 --- a/src/Router/SimpleRouter.php +++ /dev/null @@ -1,77 +0,0 @@ - $receiver) { - $this->add($name, $receiver); - } - } - - /** - * @param string $name - * @param mixed $receiver - */ - public function add($name, $receiver) - { - if (!$this->accepts($receiver)) { - throw new \InvalidArgumentException(sprintf('Given "%s" is not supported.', $receiver)); - } - - $this->receivers[$name] = $receiver; - } - - /** - * {@inheritdoc} - */ - public function map(Envelope $envelope) - { - $receiver = $this->get($envelope->getName()); - - if (null === $receiver) { - throw new ReceiverNotFoundException(sprintf('No receiver found with name "%s".', $envelope->getName())); - } - - if (is_callable($receiver)) { - return $receiver; - } - - return array($receiver, lcfirst($envelope->getName())); - } - - /** - * @param mixed $receiver - * - * @return bool - */ - protected function accepts($receiver) - { - return is_callable($receiver) || is_object($receiver) || class_exists($receiver); - } - - /** - * @param string $name - * - * @return mixed - */ - protected function get($name) - { - return isset($this->receivers[$name]) ? $this->receivers[$name] : null; - } -} diff --git a/src/Serializer.php b/src/Serializer.php index ada8783b..156f6a24 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -1,27 +1,21 @@ aggregate = $aggregate ?: $this->createAggregateNormalizer(); } /** - * @param Envelope $envelope - * * @return string */ public function serialize(Envelope $envelope) diff --git a/src/Util.php b/src/Util.php index 8594c415..99a3aa71 100644 --- a/src/Util.php +++ b/src/Util.php @@ -1,17 +1,14 @@ queues = new InMemoryFactory; + $this->queues = new InMemoryFactory(); $this->consumer = $this->getMockBuilder('Bernard\Consumer') ->disableOriginalConstructor()->getMock(); } @@ -18,48 +20,48 @@ public function setUp() /** * @medium */ - public function testItConsumes() + public function testItConsumes(): void { $command = new ConsumeCommand($this->consumer, $this->queues); $queue = $this->queues->create('send-newsletter'); - $this->consumer->expects($this->once())->method('consume')->with($this->identicalTo($queue), $this->equalTo(array( + $this->consumer->expects($this->once())->method('consume')->with($this->identicalTo($queue), $this->equalTo([ 'max-runtime' => 100, 'max-messages' => 10, 'stop-when-empty' => true, 'stop-on-error' => false, - ))); + ])); $tester = new CommandTester($command); - $tester->execute(array( + $tester->execute([ '--max-runtime' => 100, '--max-messages' => 10, '--stop-when-empty' => true, '--stop-on-error' => false, 'queue' => 'send-newsletter', - )); + ]); } - public function testItConsumesRoundRobin() + public function testItConsumesRoundRobin(): void { $command = new ConsumeCommand($this->consumer, $this->queues); - $args = array( + $args = [ 'max-runtime' => 100, 'max-messages' => 10, 'stop-when-empty' => true, 'stop-on-error' => false, - ); + ]; $this->consumer->expects($this->once())->method('consume')->with($this->isInstanceOf('Bernard\Queue\RoundRobinQueue'), $args); $tester = new CommandTester($command); - $tester->execute(array( + $tester->execute([ '--max-runtime' => 100, '--max-messages' => 10, '--stop-when-empty' => true, '--stop-on-error' => false, 'queue' => ['queue-1', 'queue-2'], - )); + ]); } } diff --git a/tests/Command/ProduceCommandTest.php b/tests/Command/ProduceCommandTest.php index 4f45c1c7..0141b102 100644 --- a/tests/Command/ProduceCommandTest.php +++ b/tests/Command/ProduceCommandTest.php @@ -1,22 +1,24 @@ producer = $this->getMockBuilder('Bernard\Producer') ->disableOriginalConstructor()->getMock(); } - public function testProduceMessageWithNoArguments() + public function testProduceMessageWithNoArguments(): void { $command = new ProduceCommand($this->producer); $message = new PlainMessage('SendNewsletter'); @@ -24,36 +26,35 @@ public function testProduceMessageWithNoArguments() $this->producer->expects($this->once())->method('produce')->with($this->equalTo($message)); $tester = new CommandTester($command); - $tester->execute(array( - 'name' => 'SendNewsletter' - )); + $tester->execute([ + 'name' => 'SendNewsletter', + ]); } - /** - * @expectedException \RuntimeException - */ - public function testInvalidJsonThrowsException() + public function testInvalidJsonThrowsException(): void { + $this->expectException(\RuntimeException::class); + $command = new ProduceCommand($this->producer); $tester = new CommandTester($command); - $tester->execute(array( - 'name' => 'SendNewsletter', - 'message' => '{@*^#"foo":"bar"}' - )); + $tester->execute([ + 'name' => 'SendNewsletter', + 'message' => '{@*^#"foo":"bar"}', + ]); } - public function testItProducesMessageWithData() + public function testItProducesMessageWithData(): void { $command = new ProduceCommand($this->producer); - $message = new PlainMessage('SendNewsletter', array('foo' => 'bar')); + $message = new PlainMessage('SendNewsletter', ['foo' => 'bar']); $this->producer->expects($this->once())->method('produce')->with($this->equalTo($message)); $tester = new CommandTester($command); - $tester->execute(array( - 'name' => 'SendNewsletter', - 'message' => '{"foo":"bar"}' - )); + $tester->execute([ + 'name' => 'SendNewsletter', + 'message' => '{"foo":"bar"}', + ]); } } diff --git a/tests/ConsumerTest.php b/tests/ConsumerTest.php index 37950a3f..00e93795 100644 --- a/tests/ConsumerTest.php +++ b/tests/ConsumerTest.php @@ -1,20 +1,28 @@ router = new SimpleRouter; - $this->router->add('ImportUsers', new Fixtures\Service); + $this->router = $this->prophesize(Router::class); $this->dispatcher = $this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); - $this->consumer = new Consumer($this->router, $this->dispatcher); + + $this->consumer = new Consumer($this->router->reveal(), $this->dispatcher); } - public function testEmitsConsumeEvent() + public function testEmitsConsumeEvent(): void { - $envelope = new Envelope(new PlainMessage('ImportUsers')); + $envelope = new Envelope($message = new PlainMessage('ImportUsers')); + $queue = $this->getMockBuilder('Bernard\Queue\InMemoryQueue')->setMethods([ - 'dequeue' + 'dequeue', ])->setConstructorArgs(['queue'])->getMock(); + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message)->shouldBeCalled(); + $this->router->route($envelope)->willReturn($receiver); + $queue->expects($this->once()) ->method('dequeue') ->willReturn($envelope); $this->dispatcher->expects($this->at(0))->method('dispatch') - ->with( new PingEvent($queue), 'bernard.ping'); + ->with(new PingEvent($queue), 'bernard.ping'); $this->dispatcher->expects($this->at(1))->method('dispatch') ->with(new EnvelopeEvent($envelope, $queue), 'bernard.invoke'); @@ -60,24 +74,25 @@ public function testEmitsConsumeEvent() $this->assertTrue($this->consumer->tick($queue)); } - public function testEmitsExceptionEvent() + public function testEmitsExceptionEvent(): void { $exception = new \InvalidArgumentException(); - $this->router->add('ImportUsers', function () use ($exception) { - throw $exception; - }); - - $envelope = new Envelope(new PlainMessage('ImportUsers')); + $envelope = new Envelope($message = new PlainMessage('ImportUsers')); $queue = new InMemoryQueue('queue'); + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message)->willThrow($exception); + $this->router->route($envelope)->willReturn($receiver); + $this->dispatcher->expects($this->at(1))->method('dispatch') ->with(new RejectEnvelopeEvent($envelope, $queue, $exception), 'bernard.reject'); $this->consumer->invoke($envelope, $queue); } - public function testShutdown() + public function testShutdown(): void { $queue = new InMemoryQueue('queue'); @@ -86,95 +101,115 @@ public function testShutdown() $this->assertFalse($this->consumer->tick($queue)); } - public function testPauseResume() + public function testPauseResume(): void { - $service = new Fixtures\Service(); - - $this->router->add('ImportUsers', $service); - + $envelope = new Envelope($message = new PlainMessage('ImportUsers')); $queue = new InMemoryQueue('queue'); - $queue->enqueue(new Envelope(new PlainMessage('ImportUsers'))); + $queue->enqueue($envelope); + + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message)->shouldBeCalled(); + $this->router->route($envelope)->willReturn($receiver); $this->consumer->pause(); $this->assertTrue($this->consumer->tick($queue)); - $this->assertFalse($service->importUsers); $this->consumer->resume(); $this->assertTrue($this->consumer->tick($queue)); - $this->assertTrue($service->importUsers); } - public function testMaxRuntime() + public function testMaxRuntime(): void { $queue = new InMemoryQueue('queue'); - $this->assertFalse($this->consumer->tick($queue, array( - 'max-runtime' => -1 * PHP_INT_MAX, - ))); + $this->assertFalse($this->consumer->tick($queue, [ + 'max-runtime' => -1 * \PHP_INT_MAX, + ])); } - public function testNoEnvelopeInQueue() + public function testNoEnvelopeInQueue(): void { $queue = new InMemoryQueue('queue'); $this->assertTrue($this->consumer->tick($queue)); } - public function testEnvelopeIsAcknowledged() + public function testEnvelopeIsAcknowledged(): void { - $service = new Fixtures\Service(); - $envelope = new Envelope(new PlainMessage('ImportUsers')); + $envelope = new Envelope($message = new PlainMessage('ImportUsers')); - $this->router->add('ImportUsers', $service); + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message)->shouldBeCalled(); + $this->router->route($envelope)->willReturn($receiver); $queue = $this->createMock('Bernard\Queue'); - $queue->expects($this->once())->method('dequeue')->will($this->returnValue($envelope)); + $queue->expects($this->once())->method('dequeue')->willReturn($envelope); $queue->expects($this->once())->method('acknowledge')->with($this->equalTo($envelope)); $this->consumer->tick($queue); - - $this->assertTrue($service->importUsers); } - public function testMaxMessages() + public function testMaxMessages(): void { - $this->router->add('ImportUsers', new Fixtures\Service); + $envelope1 = new Envelope($message1 = new PlainMessage('ImportUsers')); + $envelope2 = new Envelope($message2 = new PlainMessage('ImportUsers')); + $envelope3 = new Envelope($message3 = new PlainMessage('ImportUsers')); $queue = new InMemoryQueue('send-newsletter'); - $queue->enqueue(new Envelope(new PlainMessage('ImportUsers'))); - $queue->enqueue(new Envelope(new PlainMessage('ImportUsers'))); - $queue->enqueue(new Envelope(new PlainMessage('ImportUsers'))); - - $this->assertFalse($this->consumer->tick($queue, array('max-messages' => 1))); + $queue->enqueue($envelope1); + $queue->enqueue($envelope2); + $queue->enqueue($envelope3); + + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message1)->shouldBeCalled(); + $receiver->receive($message2)->shouldBeCalled(); + $receiver->receive($message3)->shouldBeCalled(); + $this->router->route($envelope1)->willReturn($receiver); + $this->router->route($envelope2)->willReturn($receiver); + $this->router->route($envelope3)->willReturn($receiver); + + $this->assertFalse($this->consumer->tick($queue, ['max-messages' => 1])); $this->assertTrue($this->consumer->tick($queue)); - $this->assertTrue($this->consumer->tick($queue, array('max-messages' => 100))); + $this->assertTrue($this->consumer->tick($queue, ['max-messages' => 100])); } - public function testStopAfterLastMessage() + public function testStopAfterLastMessage(): void { - $this->router->add('ImportUsers', new Fixtures\Service); + $envelope1 = new Envelope($message1 = new PlainMessage('ImportUsers')); + $envelope2 = new Envelope($message2 = new PlainMessage('ImportUsers')); $queue = new InMemoryQueue('send-newsletter'); - $queue->enqueue(new Envelope(new PlainMessage('ImportUsers'))); - $queue->enqueue(new Envelope(new PlainMessage('ImportUsers'))); - - $this->assertTrue($this->consumer->tick($queue, array('stop-when-empty' => true))); - $this->assertTrue($this->consumer->tick($queue, array('stop-when-empty' => true))); - $this->assertFalse($this->consumer->tick($queue, array('stop-when-empty' => true))); + $queue->enqueue($envelope1); + $queue->enqueue($envelope2); + + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message1)->shouldBeCalled(); + $receiver->receive($message2)->shouldBeCalled(); + $this->router->route($envelope1)->willReturn($receiver); + $this->router->route($envelope2)->willReturn($receiver); + + $this->assertTrue($this->consumer->tick($queue, ['stop-when-empty' => true])); + $this->assertTrue($this->consumer->tick($queue, ['stop-when-empty' => true])); + $this->assertFalse($this->consumer->tick($queue, ['stop-when-empty' => true])); } - /** - * @expectedException \Bernard\Exception\ReceiverNotFoundException - */ - public function testStopOnError() + public function testStopOnError(): void { - $this->router->add('ImportUsers', new Fixtures\Service); + $this->expectException(\Bernard\Exception\ReceiverNotFoundException::class); + + $envelope = new Envelope($message = new PlainMessage('DifferentMessageKey')); $queue = new InMemoryQueue('send-newsletter'); - $queue->enqueue(new Envelope(new PlainMessage('DifferentMessageKey'))); + $queue->enqueue($envelope); - $this->consumer->tick($queue, array('stop-on-error' => true)); + $this->router->route($envelope)->willThrow(ReceiverNotFoundException::class); + + $this->consumer->tick($queue, ['stop-on-error' => true]); $this->assertEquals(1, $queue->count()); } @@ -182,33 +217,40 @@ public function testStopOnError() /** * @group debug */ - public function testEnvelopeWillBeInvoked() + public function testEnvelopeWillBeInvoked(): void { - $service = new Fixtures\Service(); - - $this->router->add('ImportUsers', $service); + $envelope = new Envelope($message = new PlainMessage('ImportUsers')); $queue = new InMemoryQueue('send-newsletter'); - $queue->enqueue(new Envelope(new PlainMessage('ImportUsers'))); + $queue->enqueue($envelope); - $this->consumer->tick($queue); + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message)->shouldBeCalled(); + $this->router->route($envelope)->willReturn($receiver); - $this->assertTrue($service->importUsers); + $this->consumer->tick($queue); } /** * @requires PHP 7.0 - * @expectedException \TypeError */ - public function testWillRejectDispatchOnThrowableError() + public function testWillRejectDispatchOnThrowableError(): void { - $this->router->add('ImportReport', new Fixtures\Service); + $this->expectException(\TypeError::class); + + $envelope = new Envelope($message = new PlainMessage('ImportReport')); $queue = new InMemoryQueue('send-newsletter'); - $queue->enqueue(new Envelope(new PlainMessage('ImportReport'))); + $queue->enqueue($envelope); + + /** @var Receiver|ObjectProphecy $receiver */ + $receiver = $this->prophesize(Receiver::class); + $receiver->receive($message)->willThrow(\TypeError::class); + $this->router->route($envelope)->willReturn($receiver); $this->dispatcher->expects(self::at(0))->method('dispatch')->with($this->isInstanceOf(PingEvent::class), 'bernard.ping'); - $this->dispatcher->expects(self::at(1))->method('dispatch')->with($this->isInstanceOf(EnvelopeEvent::class) , 'bernard.invoke'); + $this->dispatcher->expects(self::at(1))->method('dispatch')->with($this->isInstanceOf(EnvelopeEvent::class), 'bernard.invoke'); $this ->dispatcher diff --git a/tests/Driver/AppEngineDriverTest.php b/tests/Driver/AppEngineDriverTest.php deleted file mode 100644 index 40342295..00000000 --- a/tests/Driver/AppEngineDriverTest.php +++ /dev/null @@ -1,55 +0,0 @@ -driver = new AppEngineDriver(array( - 'send-newsletter' => '/url_endpoint', - )); - } - - public function tearDown() - { - PushTask::$messages = array(); - } - - public function testItQueuesPushTask() - { - $this->driver->pushMessage('send-newsletter', 'message'); - - $message = new PushTask('/url_endpoint', array('message' => 'message')); - $this->assertEquals($message, PushTask::$messages['send-newsletter'][0]); - } - - public function testItUsesDefaultEndpointWhenAliasArentThere() - { - $this->driver->pushMessage('import-users', 'message'); - $this->driver->pushMessage('calculate-reports', 'message'); - - $messages = array( - new PushTask('/_ah/queue/import-users', array('message' => 'message')), - new PushTask('/_ah/queue/calculate-reports', array('message' => 'message')), - ); - - $this->assertEquals($messages[0], PushTask::$messages['import-users'][0]); - $this->assertEquals($messages[1], PushTask::$messages['calculate-reports'][0]); - } - - public function testListQueues() - { - $this->assertEquals(array('/url_endpoint' => 'send-newsletter'), $this->driver->listQueues()); - } -} diff --git a/tests/Driver/Delayable/DelayablePheanstalkDriverTest.php b/tests/Driver/Delayable/DelayablePheanstalkDriverTest.php index eee05e3c..07a742dc 100644 --- a/tests/Driver/Delayable/DelayablePheanstalkDriverTest.php +++ b/tests/Driver/Delayable/DelayablePheanstalkDriverTest.php @@ -1,25 +1,27 @@ pheanstalk = $this->getMockBuilder('Pheanstalk\Pheanstalk') - ->setMethods(array( - 'putInTube' - )) + $this->pheanstalk = $this->getMockBuilder('Pheanstalk\PheanstalkInterface') + ->setMethods([ + 'putInTube', + ]) ->disableOriginalConstructor() ->getMock(); $this->driver = new DelayablePheanstalkDriver($this->pheanstalk); } - public function testItPushesMessagesWithDelay() + public function testItPushesMessagesWithDelay(): void { $this->pheanstalk ->expects($this->once()) @@ -27,7 +29,7 @@ public function testItPushesMessagesWithDelay() ->with( $this->equalTo('my-queue'), $this->equalTo('This is a message'), - $this->equalTo(PheanstalkInterface::DEFAULT_PRIORITY), + $this->equalTo(DelayablePheanstalkDriver::DEFAULT_PRIORITY), $this->equalTo(10) ); diff --git a/tests/Driver/AbstractDoctrineDriverTest.php b/tests/Driver/Doctrine/AbstractDriverTest.php similarity index 60% rename from tests/Driver/AbstractDoctrineDriverTest.php rename to tests/Driver/Doctrine/AbstractDriverTest.php index 1ec32747..f7761711 100644 --- a/tests/Driver/AbstractDoctrineDriverTest.php +++ b/tests/Driver/Doctrine/AbstractDriverTest.php @@ -1,13 +1,15 @@ markTestSkipped('Doctrine have incompatibility issues with HHVM.'); } @@ -30,10 +32,10 @@ public function setUp() } $this->setUpDatabase(); - $this->driver = new DoctrineDriver($this->connection); + $this->driver = new Driver($this->connection); } - protected function tearDown() + protected function tearDown(): void { if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) { $this->connection->exec('SET FOREIGN_KEY_CHECKS = 0'); @@ -49,36 +51,36 @@ protected function tearDown() } } - public function testPopMessageWithInterval() + public function testPopMessageWithInterval(): void { $microtime = microtime(true); $this->driver->popMessage('non-existent-queue', 0.001); - $this->assertTrue((microtime(true) - $microtime) >= 0.001); + $this->assertGreaterThanOrEqual(0.001, microtime(true) - $microtime); } - public function testCreateAndRemoveQueue() + public function testCreateAndRemoveQueue(): void { // Duplicates are not taking into account. $this->driver->createQueue('import-users'); $this->driver->createQueue('send-newsletter'); $this->driver->createQueue('import-users'); - $this->assertEquals(array('import-users', 'send-newsletter'), $this->driver->listQueues()); + $this->assertEquals(['import-users', 'send-newsletter'], $this->driver->listQueues()); $this->driver->removeQueue('import-users'); - $this->assertEquals(array('send-newsletter'), $this->driver->listQueues()); + $this->assertEquals(['send-newsletter'], $this->driver->listQueues()); } - public function testPushMessageLazilyCreatesQueue() + public function testPushMessageLazilyCreatesQueue(): void { $this->driver->pushMessage('send-newsletter', 'something'); - $this->assertEquals(array('send-newsletter'), $this->driver->listQueues()); + $this->assertEquals(['send-newsletter'], $this->driver->listQueues()); } - public function testRemoveQueueRemovesMessages() + public function testRemoveQueueRemovesMessages(): void { $this->driver->pushMessage('send-newsletter', 'something'); $this->assertEquals(1, $this->driver->countMessages('send-newsletter')); @@ -88,14 +90,15 @@ public function testRemoveQueueRemovesMessages() $this->assertEquals(0, $this->driver->countMessages('send-newsletter')); } - public function testItIsAQueue() + public function testItIsAQueue(): void { $messages = array_map(function ($i) { $this->driver->pushMessage('send-newsletter', $message = 'my-message-'.$i); + return $message; }, range(1, 6)); - $assertPeek = function (array $peeked, $expectedCount) use ($messages) { + $assertPeek = function (array $peeked, $expectedCount) use ($messages): void { self::assertCount($expectedCount, $peeked); foreach ($peeked as $peek) { self::assertContains($peek, $messages); @@ -110,16 +113,16 @@ public function testItIsAQueue() $this->assertEquals([], $this->driver->peekQueue('import-users')); // popping - list($message, $id) = $this->driver->popMessage('send-newsletter'); + [$message, $id] = $this->driver->popMessage('send-newsletter'); self::assertContains($message, $messages); - list($message, $id) = $this->driver->popMessage('send-newsletter'); + [$message, $id] = $this->driver->popMessage('send-newsletter'); self::assertContains($message, $messages); // No messages when all are invisible - $this->assertInternalType('null', $this->driver->popMessage('import-users', 0.0001)); + $this->assertNull($this->driver->popMessage('import-users', 0.0001)); } - public function testCountMessages() + public function testCountMessages(): void { $this->assertEquals(0, $this->driver->countMessages('import-users')); @@ -127,21 +130,36 @@ public function testCountMessages() $this->driver->pushMessage('send-newsletter', 'my-message-2'); $this->assertEquals(2, $this->driver->countMessages('send-newsletter')); - list($message, $id) = $this->driver->popMessage('send-newsletter'); + [$message, $id] = $this->driver->popMessage('send-newsletter'); $this->driver->acknowledgeMessage('send-newsletter', $id); $this->assertEquals(1, $this->driver->countMessages('send-newsletter')); } - public function testListQueues() + public function testPeekMessagesExcludesPoppedMessages(): void + { + $this->driver->pushMessage('send-newsletter', 'my-message-1'); + $this->driver->pushMessage('send-newsletter', 'my-message-2'); + $this->driver->pushMessage('send-newsletter', 'my-message-3'); + + $this->assertCount(3, $this->driver->peekQueue('send-newsletter')); + $this->assertEquals(3, $this->driver->countMessages('send-newsletter')); + + $this->driver->popMessage('send-newsletter'); + + $this->assertCount(2, $this->driver->peekQueue('send-newsletter')); + $this->assertEquals(2, $this->driver->countMessages('send-newsletter')); + } + + public function testListQueues(): void { $this->driver->pushMessage('import', 'message1'); $this->driver->pushMessage('send-newsletter', 'message2'); - $this->assertEquals(array('import', 'send-newsletter'), $this->driver->listQueues()); + $this->assertEquals(['import', 'send-newsletter'], $this->driver->listQueues()); } - public function testRemoveQueue() + public function testRemoveQueue(): void { $this->driver->pushMessage('import', 'message1'); $this->driver->pushMessage('import', 'message2'); @@ -152,29 +170,29 @@ public function testRemoveQueue() $this->assertEquals(0, $this->driver->countMessages('import')); } - protected function insertMessage($queue, $message) + protected function insertMessage($queue, $message): void { $this->connection->insert('messages', compact('queue', 'message')); } - protected function setUpDatabase() + protected function setUpDatabase(): void { $this->connection = $this->createConnection(); - $schema = new Schema; + $schema = new Schema(); MessagesSchema::create($schema); - array_map(array($this->connection, 'executeQuery'), $schema->toSql($this->connection->getDatabasePlatform())); + array_map([$this->connection, 'executeQuery'], $schema->toSql($this->connection->getDatabasePlatform())); } /** * @return \Doctrine\DBAL\Connection */ - protected abstract function createConnection(); + abstract protected function createConnection(); /** * @return bool */ - protected abstract function isSupported(); + abstract protected function isSupported(); } diff --git a/tests/Command/Doctrine/AbstractCommandTest.php b/tests/Driver/Doctrine/Command/AbstractCommandTest.php similarity index 78% rename from tests/Command/Doctrine/AbstractCommandTest.php rename to tests/Driver/Doctrine/Command/AbstractCommandTest.php index 570e9733..92e8542f 100644 --- a/tests/Command/Doctrine/AbstractCommandTest.php +++ b/tests/Driver/Doctrine/Command/AbstractCommandTest.php @@ -1,15 +1,17 @@ getMockBuilder('Doctrine\\DBAL\\Connection') ->disableOriginalConstructor()->getMock(); @@ -19,9 +21,9 @@ public function setUp() $helper ->expects($this->any()) ->method('getConnection') - ->will($this->returnValue($connection)); + ->willReturn($connection); - $this->command = $this->getMockBuilder('Bernard\\Command\\Doctrine\\AbstractCommand') + $this->command = $this->getMockBuilder(AbstractCommand::class) ->setMethods(['getSql', 'applySql', 'getHelper']) ->setConstructorArgs(['abstract']) ->getMock(); @@ -29,10 +31,10 @@ public function setUp() ->expects($this->any()) ->method('getHelper') ->with('connection') - ->will($this->returnValue($helper)); + ->willReturn($helper); } - public function testExecuteWithDumpSql() + public function testExecuteWithDumpSql(): void { $this->command ->expects($this->once()) @@ -41,7 +43,7 @@ public function testExecuteWithDumpSql() $this->isInstanceOf('Doctrine\\DBAL\\Schema\\Synchronizer\\SingleDatabaseSynchronizer'), $this->isInstanceOf('Doctrine\\DBAL\\Schema\\Schema') ) - ->will($this->returnValue([])); + ->willReturn([]); $tester = new CommandTester($this->command); $tester->execute([ @@ -49,7 +51,7 @@ public function testExecuteWithDumpSql() ]); } - public function testExecuteWithoutDumpSql() + public function testExecuteWithoutDumpSql(): void { $this->command ->expects($this->once()) diff --git a/tests/Command/Doctrine/BaseCommandTest.php b/tests/Driver/Doctrine/Command/BaseCommandTest.php similarity index 80% rename from tests/Command/Doctrine/BaseCommandTest.php rename to tests/Driver/Doctrine/Command/BaseCommandTest.php index f8441a1f..2b0c630b 100644 --- a/tests/Command/Doctrine/BaseCommandTest.php +++ b/tests/Driver/Doctrine/Command/BaseCommandTest.php @@ -1,6 +1,8 @@ getMockBuilder('Doctrine\\DBAL\\Connection') ->disableOriginalConstructor()->getMock(); @@ -23,9 +25,9 @@ public function setUp() $helper ->expects($this->any()) ->method('getConnection') - ->will($this->returnValue($connection)); + ->willReturn($connection); - $this->command = $this->getMockBuilder('Bernard\\Command\\Doctrine\\' . $this->getShortClassName()) + $this->command = $this->getMockBuilder('Bernard\\Driver\\Doctrine\\Command\\'.$this->getShortClassName()) ->setMethods(['getSynchronizer', 'getHelper']) ->setConstructorArgs([$connection]) ->getMock(); @@ -33,21 +35,21 @@ public function setUp() ->expects($this->any()) ->method('getSynchronizer') ->with($connection) - ->will($this->returnValue($this->sync)); + ->willReturn($this->sync); $this->command ->expects($this->any()) ->method('getHelper') ->with('connection') - ->will($this->returnValue($helper)); + ->willReturn($helper); } - public function testExecuteWithDumpSql() + public function testExecuteWithDumpSql(): void { $this->sync ->expects($this->once()) ->method($this->getSqlMethod()) ->with($this->isInstanceOf('Doctrine\\DBAL\\Schema\\Schema')) - ->will($this->returnValue([])); + ->willReturn([]); $tester = new CommandTester($this->command); $tester->execute([ @@ -55,7 +57,7 @@ public function testExecuteWithDumpSql() ]); } - public function testExecuteWithoutDumpSql() + public function testExecuteWithoutDumpSql(): void { $this->sync ->expects($this->once()) diff --git a/tests/Command/Doctrine/CreateCommandTest.php b/tests/Driver/Doctrine/Command/CreateCommandTest.php similarity index 84% rename from tests/Command/Doctrine/CreateCommandTest.php rename to tests/Driver/Doctrine/Command/CreateCommandTest.php index 95929b83..cbc5ed32 100644 --- a/tests/Command/Doctrine/CreateCommandTest.php +++ b/tests/Driver/Doctrine/Command/CreateCommandTest.php @@ -1,6 +1,8 @@ connection = $this->prophesize('Doctrine\DBAL\Connection'); $this->listener = new ConnectionListener($this->connection->reveal()); } - public function testPing() + public function testPing(): void { $this->connection->isConnected()->willReturn(true); @@ -29,14 +32,14 @@ public function testPing() $this->listener->onPing(); } - public function testPingOnNotConnectedConnection() + public function testPingOnNotConnectedConnection(): void { $this->connection->isConnected()->willReturn(false); $this->listener->onPing(); } - public function testCloseConnectionIfPingFails() + public function testCloseConnectionIfPingFails(): void { $this->connection->isConnected()->willReturn(true); $this->connection->query('SELECT 1')->willThrow(new DBALException()); diff --git a/tests/Driver/Doctrine/MySQLDriverTest.php b/tests/Driver/Doctrine/MySQLDriverTest.php new file mode 100644 index 00000000..896da1d6 --- /dev/null +++ b/tests/Driver/Doctrine/MySQLDriverTest.php @@ -0,0 +1,40 @@ + 'pdo_mysql', + 'host' => '127.0.0.1', + 'dbname' => 'bernard_test', + ]; + + $this->assertEquals($params, $this->driver->info()); + } + + protected function createConnection() + { + return DriverManager::getConnection([ + 'driver' => 'pdo_mysql', + 'host' => '127.0.0.1', + 'user' => 'root', + 'dbname' => 'bernard_test', + 'password' => '', + ]); + } +} diff --git a/tests/Driver/Doctrine/PostgreSQLDriverTest.php b/tests/Driver/Doctrine/PostgreSQLDriverTest.php new file mode 100644 index 00000000..d4b78b84 --- /dev/null +++ b/tests/Driver/Doctrine/PostgreSQLDriverTest.php @@ -0,0 +1,38 @@ + 'pdo_pgsql', + 'dbname' => 'bernard_test', + ]; + + $this->assertEquals($params, $this->driver->info()); + } + + protected function createConnection() + { + return DriverManager::getConnection([ + 'driver' => 'pdo_pgsql', + 'user' => 'postgres', + 'dbname' => 'bernard_test', + 'password' => '', + ]); + } +} diff --git a/tests/Driver/Doctrine/SQLiteDriverTest.php b/tests/Driver/Doctrine/SQLiteDriverTest.php new file mode 100644 index 00000000..4de20e71 --- /dev/null +++ b/tests/Driver/Doctrine/SQLiteDriverTest.php @@ -0,0 +1,36 @@ + true, + 'driver' => 'pdo_sqlite', + ]; + + $this->assertEquals($params, $this->driver->info()); + } + + protected function createConnection() + { + return DriverManager::getConnection([ + 'memory' => true, + 'driver' => 'pdo_sqlite', + ]); + } +} diff --git a/tests/Driver/FlatFileDriverTest.php b/tests/Driver/FlatFile/DriverTest.php similarity index 57% rename from tests/Driver/FlatFileDriverTest.php rename to tests/Driver/FlatFile/DriverTest.php index f1125334..f7672a22 100644 --- a/tests/Driver/FlatFileDriverTest.php +++ b/tests/Driver/FlatFile/DriverTest.php @@ -1,20 +1,22 @@ */ -class FlatFileDriverTest extends \PHPUnit\Framework\TestCase +class DriverTest extends \PHPUnit\Framework\TestCase { /** - * @var FlatFileDriver + * @var Driver */ private $driver; - protected function setUp() + protected function setUp(): void { $this->baseDir = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'bernard-flat'; @@ -22,10 +24,10 @@ protected function setUp() mkdir($this->baseDir, 0777, true); } - $this->driver = new FlatFileDriver($this->baseDir); + $this->driver = new Driver($this->baseDir); } - protected function tearDown() + protected function tearDown(): void { if ((strtoupper(substr(\PHP_OS, 0, 3)) === 'WIN')) { system('rd /s /q '.$this->baseDir); @@ -34,25 +36,25 @@ protected function tearDown() } } - public function testCreate() + public function testCreate(): void { $this->driver->createQueue('send-newsletter'); $this->driver->createQueue('send-newsletter'); - $this->assertTrue(is_dir($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter')); + $this->assertDirectoryExists($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter'); } - public function testRemove() + public function testRemove(): void { $this->driver->createQueue('send-newsletter'); $this->driver->pushMessage('send-newsletter', 'test'); $this->driver->removeQueue('send-newsletter'); - $this->assertFalse(is_dir($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter')); + $this->assertDirectoryNotExists($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter'); } - public function testRemoveQueueWithPoppedMessage() + public function testRemoveQueueWithPoppedMessage(): void { $this->driver->createQueue('send-newsletter'); $this->driver->pushMessage('send-newsletter', 'test'); @@ -60,10 +62,10 @@ public function testRemoveQueueWithPoppedMessage() $this->driver->removeQueue('send-newsletter'); - $this->assertFalse(is_dir($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter')); + $this->assertDirectoryNotExists($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter'); } - public function testPushMessage() + public function testPushMessage(): void { $this->driver->createQueue('send-newsletter'); $this->driver->pushMessage('send-newsletter', 'test'); @@ -71,14 +73,14 @@ public function testPushMessage() $this->assertCount(1, glob($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter'.\DIRECTORY_SEPARATOR.'*.job')); } - public function testPushMessagePermissions() + public function testPushMessagePermissions(): void { - $this->driver = new FlatFileDriver($this->baseDir, 0770); + $this->driver = new Driver($this->baseDir, 0770); $this->testPushMessage(); $this->assertEquals('0770', substr(sprintf('%o', fileperms($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter'.\DIRECTORY_SEPARATOR.'1.job')), -4)); } - public function testPopMessage() + public function testPopMessage(): void { $this->driver->createQueue('send-newsletter'); @@ -87,29 +89,50 @@ public function testPopMessage() $this->driver->pushMessage('send-newsletter', 'job #3'); foreach (range(3, 1) as $i) { - list($message, ) = $this->driver->popMessage('send-newsletter'); - $this->assertEquals('job #'.$i, $message); + $driverMessage = $this->driver->popMessage('send-newsletter'); + $this->assertEquals('job #'.$i, $driverMessage->message); + } + } + + public function testPopMessageWhichPushedAfterTheInitialCollect(): void + { + $this->driver->createQueue('send-newsletter'); + + $pid = pcntl_fork(); + + if ($pid === -1) { + $this->fail('Failed to fork the currently running process: '.pcntl_strerror(pcntl_get_last_error())); + } elseif ($pid === 0) { + // Child process pushes a message after the initial collect + sleep(5); + $this->driver->pushMessage('send-newsletter', 'test'); + exit; } + + $driverMessage = $this->driver->popMessage('send-newsletter', 10); + $this->assertSame('test', $driverMessage->message); + + pcntl_waitpid($pid, $status); } - public function testAcknowledgeMessage() + public function testAcknowledgeMessage(): void { $this->driver->createQueue('send-newsletter'); $this->driver->pushMessage('send-newsletter', 'job #1'); - $message = $this->driver->popMessage('send-newsletter'); + $driverMessage = $this->driver->popMessage('send-newsletter'); - $this->driver->acknowledgeMessage('send-newsletter', $message[1]); + $this->driver->acknowledgeMessage('send-newsletter', $driverMessage->receipt); $this->assertCount(0, glob($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter'.\DIRECTORY_SEPARATOR.'*.job')); } - public function testPeekQueue() + public function testPeekQueue(): void { $this->driver->createQueue('send-newsletter'); - for ($i = 0; $i < 10; $i++) { + for ($i = 0; $i < 10; ++$i) { $this->driver->pushMessage('send-newsletter', 'Job #'.$i); } @@ -117,18 +140,18 @@ public function testPeekQueue() $this->assertCount(10, glob($this->baseDir.\DIRECTORY_SEPARATOR.'send-newsletter'.\DIRECTORY_SEPARATOR.'*.job')); } - - public function testListQueues() + + public function testListQueues(): void { $this->driver->createQueue('send-newsletter-1'); - + $this->driver->createQueue('send-newsletter-2'); $this->driver->pushMessage('send-newsletter-2', 'job #1'); - + $this->driver->createQueue('send-newsletter-3'); $this->driver->pushMessage('send-newsletter-3', 'job #1'); $this->driver->pushMessage('send-newsletter-3', 'job #2'); - + $this->assertCount(3, $this->driver->listQueues()); } } diff --git a/tests/Driver/InMemory/DriverTest.php b/tests/Driver/InMemory/DriverTest.php new file mode 100644 index 00000000..f8553fb0 --- /dev/null +++ b/tests/Driver/InMemory/DriverTest.php @@ -0,0 +1,34 @@ + + */ +final class DriverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Driver + */ + private $driver; + + protected function setUp(): void + { + $this->driver = new Driver(); + } + + /** + *@test + */ + public function testItListsQueues(): void + { + $this->driver->createQueue('queue1'); + $this->driver->createQueue('queue2'); + + $this->assertEquals(['queue1', 'queue2'], $this->driver->listQueues()); + } +} diff --git a/tests/Driver/InteropDriverTest.php b/tests/Driver/InteropDriverTest.php deleted file mode 100644 index 0755b919..00000000 --- a/tests/Driver/InteropDriverTest.php +++ /dev/null @@ -1,296 +0,0 @@ -assertInstanceOf('Bernard\Driver', new InteropDriver($this->createInteropContextMock())); - } - - public function testListQueuesMethodDoesNothingAndAlwaysReturnEmptyArray() - { - $driver = new InteropDriver($this->createInteropContextMock()); - - $this->assertSame([], $driver->listQueues()); - } - - public function testCreateQueueMethodDoesNothingAndAlwaysReturnNull() - { - $driver = new InteropDriver($this->createInteropContextMock()); - - $this->assertNull($driver->createQueue('aQueueName')); - } - - public function testPushMessageMethodPublishMessageToQueueUsingInteropProducer() - { - $queue = $this->createMock(PsrQueue::class); - $message = $this->createMock(PsrMessage::class); - - $producer = $this->createMock(PsrProducer::class); - $producer - ->expects($this->once()) - ->method('send') - ->with($this->identicalTo($queue), $this->identicalTo($message)) - ; - - $context = $this->createInteropContextMock(); - $context - ->expects($this->once()) - ->method('createQueue') - ->with('theQueueName') - ->willReturn($queue) - ; - $context - ->expects($this->once()) - ->method('createMessage') - ->with('theBody') - ->willReturn($message) - ; - $context - ->expects($this->once()) - ->method('createProducer') - ->willReturn($producer) - ; - - $driver = new InteropDriver($context); - - $driver->pushMessage('theQueueName', 'theBody'); - } - - public function testPopMessageReturnNullIfInteropConsumerReturnNothingOnReceive() - { - $queue = $this->createMock(PsrQueue::class); - - $consumer = $this->createMock(PsrConsumer::class); - $consumer - ->expects($this->once()) - ->method('receive') - ->with(5000) - ->willReturn(null) - ; - - $context = $this->createInteropContextMock(); - $context - ->expects($this->once()) - ->method('createQueue') - ->with('theQueueName') - ->willReturn($queue) - ; - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer) - ; - - $driver = new InteropDriver($context); - - $this->assertNull($driver->popMessage('theQueueName')); - } - - public function testPopMessageReturnArrayWithBodyAndInteropMessage() - { - $queue = $this->createMock(PsrQueue::class); - $message = $this->createMock(PsrMessage::class); - $message - ->expects($this->once()) - ->method('getBody') - ->willReturn('theBody') - ; - - $consumer = $this->createMock(PsrConsumer::class); - $consumer - ->expects($this->once()) - ->method('receive') - ->with(6789) - ->willReturn($message) - ; - - $context = $this->createInteropContextMock(); - $context - ->expects($this->once()) - ->method('createQueue') - ->with('theQueueName') - ->willReturn($queue) - ; - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer) - ; - - $driver = new InteropDriver($context); - - $this->assertSame( - ['theBody', $message], - $driver->popMessage('theQueueName', 6.789) - ); - } - - public function testAcknowledgeMessage() - { - $queue = $this->createMock(PsrQueue::class); - $message = $this->createMock(PsrMessage::class); - - $consumer = $this->createMock(PsrConsumer::class); - $consumer - ->expects($this->once()) - ->method('receive') - ->willReturn($message) - ; - $consumer - ->expects($this->once()) - ->method('acknowledge') - ->with($this->identicalTo($message)) - ; - - $context = $this->createInteropContextMock(); - $context - ->expects($this->once()) - ->method('createQueue') - ->with('theQueueName') - ->willReturn($queue) - ; - $context - ->expects($this->once()) - ->method('createConsumer') - ->with($this->identicalTo($queue)) - ->willReturn($consumer) - ; - - $driver = new InteropDriver($context); - - $result = $driver->popMessage('theQueueName'); - - //guard - $this->assertSame($message, $result[1]); - - $driver->acknowledgeMessage('theQueueName', $result[1]); - } - - public function testPeekQueueMethodDoesNothingAndAlwaysReturnEmptyArray() - { - $driver = new InteropDriver($this->createInteropContextMock()); - - $this->assertSame([], $driver->peekQueue('aQueueName')); - } - - public function testRemoveQueueMethodDoesNothingAndAlwaysReturnNull() - { - $driver = new InteropDriver($this->createInteropContextMock()); - - $this->assertNull($driver->removeQueue('aQueueName')); - } - - public function testInfoMethodDoesNothingAndAlwaysReturnEmptyArray() - { - $driver = new InteropDriver($this->createInteropContextMock()); - - $this->assertNull($driver->removeQueue('aQueueName')); - } - - public function testCreateQueueMethodShouldDeclareAmqpQueue() - { - $queue = $this->createMock(AmqpQueue::class); - $queue - ->expects($this->once()) - ->method('addFlag') - ->with(AmqpQueue::FLAG_DURABLE) - ; - - $context = $this->createAmqpInteropContextMock(); - $context - ->expects($this->once()) - ->method('createQueue') - ->with('theQueueName') - ->willReturn($queue) - ; - $context - ->expects($this->once()) - ->method('declareQueue') - ->with($this->identicalTo($queue)) - ; - - $driver = new InteropDriver($context); - - $this->assertNull($driver->createQueue('theQueueName')); - } - - public function testDeleteQueueMethodShouldCallDeleteQueueMethodOnAmqpContext() - { - $queue = $this->createMock(AmqpQueue::class); - $queue - ->expects($this->once()) - ->method('addFlag') - ->with(AmqpQueue::FLAG_DURABLE) - ; - - $context = $this->createAmqpInteropContextMock(); - $context - ->expects($this->once()) - ->method('createQueue') - ->with('theQueueName') - ->willReturn($queue) - ; - $context - ->expects($this->once()) - ->method('deleteQueue') - ->with($this->identicalTo($queue)) - ; - - $driver = new InteropDriver($context); - - $this->assertNull($driver->removeQueue('theQueueName')); - } - - public function testCountMessagesMethodShouldUseCountFromAmqpDeclareQueueResult() - { - $queue = $this->createMock(AmqpQueue::class); - - $context = $this->createAmqpInteropContextMock(); - $context - ->expects($this->once()) - ->method('createQueue') - ->with('theQueueName') - ->willReturn($queue) - ; - $context - ->expects($this->once()) - ->method('declareQueue') - ->with($this->identicalTo($queue)) - ->willReturn(123) - ; - - $driver = new InteropDriver($context); - - $this->assertSame(123, $driver->countMessages('theQueueName')); - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject|AmqpContext - */ - private function createAmqpInteropContextMock() - { - return $this->createMock(AmqpContext::class); - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext - */ - private function createInteropContextMock() - { - return $this->createMock(PsrContext::class); - } -} diff --git a/tests/Driver/IronMqDriverTest.php b/tests/Driver/IronMqDriverTest.php deleted file mode 100644 index 6b9a9fb5..00000000 --- a/tests/Driver/IronMqDriverTest.php +++ /dev/null @@ -1,149 +0,0 @@ -ironmq = $this->getMockBuilder('\IronMQ\IronMQ') - ->setMethods(array( - 'getQueue', - 'getQueues', - 'peekMessages', - 'deleteQueue', - 'postMessage', - 'getMessages', - 'deleteMessage', - )) - ->disableOriginalConstructor() - ->getMock(); - - $this->driver = new IronMqDriver($this->ironmq); - } - - public function testItExposesInfo() - { - $driver = new IronMqDriver($this->ironmq, 10); - - $this->assertEquals(array('prefetch' => 10), $driver->info()); - $this->assertEquals(array('prefetch' => 2), $this->driver->info()); - } - - public function testItImplementsDriverInterface() - { - $this->assertInstanceOf('Bernard\Driver\AbstractPrefetchDriver', $this->driver); - } - - public function testItCountsNumberOfMessagesInQueue() - { - $this->ironmq->expects($this->at(0))->method('getQueue') - ->with($this->equalTo('send-newsletter'))->will($this->returnValue((object) array('size' => 4))); - - $this->ironmq->expects($this->at(1))->method('getQueue') - ->with($this->equalTo('non-existant'))->will($this->returnValue(null)); - - $this->assertEquals(4, $this->driver->countMessages('send-newsletter')); - $this->assertEquals(null, $this->driver->countMessages('non-existant')); - } - - public function testItListQueues() - { - $ironmqQueues = array( - (object) array('name' => 'failed'), - (object) array('name' => 'queue1'), - ); - - $this->ironmq->expects($this->once())->method('getQueues') - ->will($this->returnValue($ironmqQueues)); - - $this->assertEquals(array('failed', 'queue1'), $this->driver->listQueues()); - } - - public function testAcknowledgeMessage() - { - $this->ironmq->expects($this->once())->method('deleteMessage') - ->with($this->equalTo('my-queue'), $this->equalTo('receipt1')); - - $this->driver->acknowledgeMessage('my-queue', 'receipt1'); - } - - public function testItPeeksInAQueue() - { - $ironmqMessages = array( - (object) array('body' => 'message1'), - ); - - $this->ironmq->expects($this->at(0))->method('peekMessages') - ->with($this->equalTo('my-queue'), $this->equalTo(10))->will($this->returnValue($ironmqMessages)); - $this->ironmq->expects($this->at(1))->method('peekMessages') - ->with($this->equalTo('my-queue2'), $this->equalTo(20))->will($this->returnValue(null)); - - $this->assertEquals(array('message1'), $this->driver->peekQueue('my-queue', 10, 10)); - $this->assertEquals(array(), $this->driver->peekQueue('my-queue2')); - } - - public function testItRemovesAQueue() - { - $this->ironmq - ->expects($this->once()) - ->method('deleteQueue') - ->with($this->equalTo('my-queue')); - - $this->driver->removeQueue('my-queue'); - } - - public function testItPushesMessages() - { - $this->ironmq - ->expects($this->once()) - ->method('postMessage') - ->with($this->equalTo('my-queue'), $this->equalTo('This is a message')); - - $this->driver->pushMessage('my-queue', 'This is a message'); - } - - public function testItPrefetchesMessages() - { - $ironmqMessages = array( - (object) array('body' => 'message1', 'id' => 1), - (object) array('body' => 'message2', 'id' => 2), - ); - - $this->ironmq->expects($this->once())->method('getMessages') - ->with($this->equalTo('send-newsletter'), $this->equalTo(2)) - ->will($this->returnValue($ironmqMessages)); - - $this->assertEquals(array('message1', 1), $this->driver->popMessage('send-newsletter')); - $this->assertEquals(array('message2', 2), $this->driver->popMessage('send-newsletter')); - } - - public function testItPopMessages() - { - $this->ironmq - ->expects($this->at(0)) - ->method('getMessages') - ->with($this->equalTo('my-queue1'), $this->equalTo(2), $this->equalTo(60)) - ->will($this->returnValue(array( - (object) array('body' => 'message1', 'id' => 1), - ))); - $this->ironmq - ->expects($this->at(1)) - ->method('getMessages') - ->with($this->equalTo('my-queue2'), $this->equalTo(2), $this->equalTo(60)) - ->will($this->returnValue(array( - (object) array('body' => 'message2', 'id' => 2), - ))); - $this->ironmq - ->expects($this->at(1)) - ->method('getMessages') - ->with($this->equalTo('my-queue2'), $this->equalTo(2), $this->equalTo(60)) - ->will($this->returnValue(null)); - - $this->assertEquals(array('message1', 1), $this->driver->popMessage('my-queue1')); - $this->assertEquals(array('message2', 2), $this->driver->popMessage('my-queue2')); - $this->assertEquals(array(null, null), $this->driver->popMessage('my-queue2', 0.01)); - } -} diff --git a/tests/Driver/MongoDBDriverFunctionalTest.php b/tests/Driver/MongoDB/DriverFunctionalTest.php similarity index 67% rename from tests/Driver/MongoDBDriverFunctionalTest.php rename to tests/Driver/MongoDB/DriverFunctionalTest.php index e1e8cd2e..a0e69529 100644 --- a/tests/Driver/MongoDBDriverFunctionalTest.php +++ b/tests/Driver/MongoDB/DriverFunctionalTest.php @@ -1,29 +1,36 @@ markTestSkipped('MongoDB extension is not available.'); } @@ -35,12 +42,12 @@ public function setUp() $this->queues = $mongoClient->selectCollection(self::DATABASE, self::QUEUES); $this->messages = $mongoClient->selectCollection(self::DATABASE, self::MESSAGES); - $this->driver = new MongoDBDriver($this->queues, $this->messages); + $this->driver = new Driver($this->queues, $this->messages); } - public function tearDown() + protected function tearDown(): void { - if ( ! $this->messages instanceof MongoCollection) { + if (!$this->messages instanceof MongoCollection) { return; } @@ -55,7 +62,7 @@ public function tearDown() * @covers ::popMessage() * @covers ::pushMessage() */ - public function testMessageLifecycle() + public function testMessageLifecycle(): void { $this->assertEquals(0, $this->driver->countMessages('foo')); @@ -65,17 +72,17 @@ public function testMessageLifecycle() $this->driver->pushMessage('foo', 'message2'); $this->assertEquals(2, $this->driver->countMessages('foo')); - list($message1, $receipt1) = $this->driver->popMessage('foo'); + [$message1, $receipt1] = $this->driver->popMessage('foo'); $this->assertSame('message1', $message1, 'The first message pushed is popped first'); $this->assertRegExp('/^[a-f\d]{24}$/i', $receipt1, 'The message receipt is an ObjectId'); $this->assertEquals(1, $this->driver->countMessages('foo')); - list($message2, $receipt2) = $this->driver->popMessage('foo'); + [$message2, $receipt2] = $this->driver->popMessage('foo'); $this->assertSame('message2', $message2, 'The second message pushed is popped second'); $this->assertRegExp('/^[a-f\d]{24}$/i', $receipt2, 'The message receipt is an ObjectId'); $this->assertEquals(0, $this->driver->countMessages('foo')); - list($message3, $receipt3) = $this->driver->popMessage('foo', 1); + [$message3, $receipt3] = $this->driver->popMessage('foo', 1); $this->assertNull($message3, 'Null message is returned when popping an empty queue'); $this->assertNull($receipt3, 'Null receipt is returned when popping an empty queue'); @@ -88,16 +95,16 @@ public function testMessageLifecycle() $this->assertEquals(0, $this->messages->count(), 'Acknowledged messages are removed from the database'); } - public function testPeekQueue() + public function testPeekQueue(): void { $this->driver->pushMessage('foo', 'message1'); $this->driver->pushMessage('foo', 'message2'); - $this->assertSame(array('message1', 'message2'), $this->driver->peekQueue('foo')); - $this->assertSame(array('message2'), $this->driver->peekQueue('foo', 1)); - $this->assertSame(array(), $this->driver->peekQueue('foo', 2)); - $this->assertSame(array('message1'), $this->driver->peekQueue('foo', 0, 1)); - $this->assertSame(array('message2'), $this->driver->peekQueue('foo', 1, 1)); + $this->assertSame(['message1', 'message2'], $this->driver->peekQueue('foo')); + $this->assertSame(['message2'], $this->driver->peekQueue('foo', 1)); + $this->assertSame([], $this->driver->peekQueue('foo', 2)); + $this->assertSame(['message1'], $this->driver->peekQueue('foo', 0, 1)); + $this->assertSame(['message2'], $this->driver->peekQueue('foo', 1, 1)); } /** @@ -105,7 +112,7 @@ public function testPeekQueue() * @covers ::listQueues() * @covers ::removeQueue() */ - public function testQueueLifecycle() + public function testQueueLifecycle(): void { $this->driver->createQueue('foo'); $this->driver->createQueue('bar'); @@ -123,7 +130,7 @@ public function testQueueLifecycle() $this->assertContains('bar', $queues); } - public function testRemoveQueueDeletesMessages() + public function testRemoveQueueDeletesMessages(): void { $this->driver->pushMessage('foo', 'message1'); $this->driver->pushMessage('foo', 'message2'); @@ -135,20 +142,20 @@ public function testRemoveQueueDeletesMessages() $this->assertEquals(0, $this->messages->count()); } - public function testCreateQueueWithDuplicateNameIsNoop() + public function testCreateQueueWithDuplicateNameIsNoop(): void { $this->driver->createQueue('foo'); $this->driver->createQueue('foo'); - $this->assertSame(array('foo'), $this->driver->listQueues()); + $this->assertSame(['foo'], $this->driver->listQueues()); } - public function testInfo() + public function testInfo(): void { - $info = array( - 'messages' => self::DATABASE . '.' . self::MESSAGES, - 'queues' => self::DATABASE . '.' . self::QUEUES, - ); + $info = [ + 'messages' => self::DATABASE.'.'.self::MESSAGES, + 'queues' => self::DATABASE.'.'.self::QUEUES, + ]; $this->assertSame($info, $this->driver->info()); } diff --git a/tests/Driver/MongoDBDriverTest.php b/tests/Driver/MongoDB/DriverTest.php similarity index 55% rename from tests/Driver/MongoDBDriverTest.php rename to tests/Driver/MongoDB/DriverTest.php index 3ce34143..ec3bbed6 100644 --- a/tests/Driver/MongoDBDriverTest.php +++ b/tests/Driver/MongoDB/DriverTest.php @@ -1,63 +1,70 @@ markTestSkipped('MongoDB extension is not available.'); } $this->queues = $this->getMockMongoCollection(); $this->messages = $this->getMockMongoCollection(); - $this->driver = new MongoDBDriver($this->queues, $this->messages); + $this->driver = new Driver($this->queues, $this->messages); } - public function testListQueues() + public function testListQueues(): void { $this->queues->expects($this->once()) ->method('distinct') ->with('_id') - ->will($this->returnValue(array('foo', 'bar'))); + ->willReturn(['foo', 'bar']); - $this->assertSame(array('foo', 'bar'), $this->driver->listQueues()); + $this->assertSame(['foo', 'bar'], $this->driver->listQueues()); } - public function testCreateQueue() + public function testCreateQueue(): void { $this->queues->expects($this->once()) ->method('update') - ->with(array('_id' => 'foo'), array('_id' => 'foo'), array('upsert' => true)); + ->with(['_id' => 'foo'], ['_id' => 'foo'], ['upsert' => true]); $this->driver->createQueue('foo'); } - public function testCountMessages() + public function testCountMessages(): void { $this->messages->expects($this->once()) ->method('count') - ->with(array('queue' => 'foo', 'visible' => true)) - ->will($this->returnValue(2)); + ->with(['queue' => 'foo', 'visible' => true]) + ->willReturn(2); $this->assertSame(2, $this->driver->countMessages('foo')); } - public function testPushMessage() + public function testPushMessage(): void { $this->messages->expects($this->once()) ->method('insert') - ->with($this->callback(function($data) { + ->with($this->callback(function ($data) { return $data['queue'] === 'foo' && $data['message'] === 'message1' && $data['sentAt'] instanceof MongoDate && @@ -67,19 +74,19 @@ public function testPushMessage() $this->driver->pushMessage('foo', 'message1'); } - public function testPopMessageWithFoundMessage() + public function testPopMessageWithFoundMessage(): void { $this->messages->expects($this->atLeastOnce()) ->method('findAndModify') ->with( - array('queue' => 'foo', 'visible' => true), - array('$set' => array('visible' => false)), - array('message' => 1), - array('sort' => array('sentAt' => 1)) + ['queue' => 'foo', 'visible' => true], + ['$set' => ['visible' => false]], + ['message' => 1], + ['sort' => ['sentAt' => 1]] ) - ->will($this->returnValue(array('message' => 'message1', '_id' => '000000000000000000000000'))); + ->willReturn(['message' => 'message1', '_id' => '000000000000000000000000']); - list($message, $receipt) = $this->driver->popMessage('foo'); + [$message, $receipt] = $this->driver->popMessage('foo'); $this->assertSame('message1', $message); $this->assertSame('000000000000000000000000', $receipt); } @@ -87,28 +94,28 @@ public function testPopMessageWithFoundMessage() /** * @medium */ - public function testPopMessageWithMissingMessage() + public function testPopMessageWithMissingMessage(): void { $this->messages->expects($this->atLeastOnce()) ->method('findAndModify') ->with( - array('queue' => 'foo', 'visible' => true), - array('$set' => array('visible' => false)), - array('message' => 1), - array('sort' => array('sentAt' => 1)) + ['queue' => 'foo', 'visible' => true], + ['$set' => ['visible' => false]], + ['message' => 1], + ['sort' => ['sentAt' => 1]] ) - ->will($this->returnValue(false)); + ->willReturn(false); - list($message, $receipt) = $this->driver->popMessage('foo', 1); + [$message, $receipt] = $this->driver->popMessage('foo', 1); $this->assertNull($message); $this->assertNull($receipt); } - public function testAcknowledgeMessage() + public function testAcknowledgeMessage(): void { $this->messages->expects($this->once()) ->method('remove') - ->with($this->callback(function($query) { + ->with($this->callback(function ($query) { return $query['_id'] instanceof MongoId && (string) $query['_id'] === '000000000000000000000000' && $query['queue'] === 'foo'; @@ -117,7 +124,7 @@ public function testAcknowledgeMessage() $this->driver->acknowledgeMessage('foo', '000000000000000000000000'); } - public function testPeekQueue() + public function testPeekQueue(): void { $cursor = $this->getMockBuilder('MongoCursor') ->disableOriginalConstructor() @@ -125,59 +132,59 @@ public function testPeekQueue() $this->messages->expects($this->once()) ->method('find') - ->with(array('queue' => 'foo', 'visible' => true), array('_id' => 0, 'message' => 1)) - ->will($this->returnValue($cursor)); + ->with(['queue' => 'foo', 'visible' => true], ['_id' => 0, 'message' => 1]) + ->willReturn($cursor); $cursor->expects($this->at(0)) ->method('sort') - ->with(array('sentAt' => 1)) - ->will($this->returnValue($cursor)); + ->with(['sentAt' => 1]) + ->willReturn($cursor); $cursor->expects($this->at(1)) ->method('limit') ->with(20) - ->will($this->returnValue($cursor)); + ->willReturn($cursor); /* Rather than mock MongoCursor's iterator interface, take advantage of * the final fluent method call and return an ArrayIterator. */ $cursor->expects($this->at(2)) ->method('skip') ->with(0) - ->will($this->returnValue(new ArrayIterator(array( - array('message' => 'message1'), - array('message' => 'message2'), - )))); + ->willReturn(new ArrayIterator([ + ['message' => 'message1'], + ['message' => 'message2'], + ])); - $this->assertSame(array('message1', 'message2'), $this->driver->peekQueue('foo')); + $this->assertSame(['message1', 'message2'], $this->driver->peekQueue('foo')); } - public function testRemoveQueue() + public function testRemoveQueue(): void { $this->queues->expects($this->once()) ->method('remove') - ->with(array('_id' => 'foo')); + ->with(['_id' => 'foo']); $this->messages->expects($this->once()) ->method('remove') - ->with(array('queue' => 'foo')); + ->with(['queue' => 'foo']); $this->driver->removeQueue('foo'); } - public function testInfo() + public function testInfo(): void { $this->queues->expects($this->once()) ->method('__toString') - ->will($this->returnValue('db.queues')); + ->willReturn('db.queues'); $this->messages->expects($this->once()) ->method('__toString') - ->will($this->returnValue('db.messages')); + ->willReturn('db.messages'); - $info = array( + $info = [ 'messages' => 'db.messages', 'queues' => 'db.queues', - ); + ]; $this->assertSame($info, $this->driver->info()); } diff --git a/tests/Driver/MySQLDoctrineDriverTest.php b/tests/Driver/MySQLDoctrineDriverTest.php deleted file mode 100644 index 05ce3c21..00000000 --- a/tests/Driver/MySQLDoctrineDriverTest.php +++ /dev/null @@ -1,42 +0,0 @@ - 'pdo_mysql', - 'host' => '127.0.0.1', - 'dbname' => 'bernard_test', - ); - - $this->assertEquals($params, $this->driver->info()); - } - - protected function createConnection() - { - return DriverManager::getConnection(array( - 'driver' => 'pdo_mysql', - 'host' => '127.0.0.1', - 'user' => 'root', - 'dbname' => 'bernard_test', - 'password' => '', - )); - } -} diff --git a/tests/Driver/PheanstalkDriverTest.php b/tests/Driver/PheanstalkDriverTest.php deleted file mode 100644 index 3545f9c0..00000000 --- a/tests/Driver/PheanstalkDriverTest.php +++ /dev/null @@ -1,110 +0,0 @@ -pheanstalk = $this->getMockBuilder('Pheanstalk\Pheanstalk') - ->setMethods(array( - 'listTubes', - 'statsTube', - 'putInTube', - 'reserveFromTube', - 'delete', - 'stats', - )) - ->disableOriginalConstructor() - ->getMock(); - - $this->driver = new PheanstalkDriver($this->pheanstalk); - } - - public function testItExposesInfo() - { - $driver = new PheanstalkDriver($this->pheanstalk); - - $info = new \ArrayObject(array('info' => true)); - - $this->pheanstalk->expects($this->once())->method('stats') - ->will($this->returnValue($info)); - - $this->assertEquals(array('info' => true), $driver->info()); - } - - public function testItImplementsDriverInterface() - { - $this->assertInstanceOf('Bernard\Driver', $this->driver); - } - - public function testItCountsNumberOfMessagesInQueue() - { - $this->pheanstalk->expects($this->once())->method('statsTube') - ->with($this->equalTo('send-newsletter'))->will($this->returnValue(array('current-jobs-ready' => 4))); - - $this->assertEquals(4, $this->driver->countMessages('send-newsletter')); - } - - public function testItListQueues() - { - $queues = array( - 'failed', - 'queue1', - ); - - $this->pheanstalk->expects($this->once())->method('listTubes') - ->will($this->returnValue($queues)); - - $this->assertEquals($queues, $this->driver->listQueues()); - } - - public function testAcknowledgeMessage() - { - $this->pheanstalk->expects($this->once())->method('delete') - ->with($this->isInstanceOf('Pheanstalk\Job')); - - $this->driver->acknowledgeMessage('my-queue', new Job(1, null)); - } - - public function testItPeeksInAQueue() - { - $this->assertEquals(array(), $this->driver->peekQueue('my-queue2')); - } - - public function testItPushesMessages() - { - $this->pheanstalk - ->expects($this->once()) - ->method('putInTube') - ->with($this->equalTo('my-queue'), $this->equalTo('This is a message')); - - $this->driver->pushMessage('my-queue', 'This is a message'); - } - - public function testItPopMessages() - { - $this->pheanstalk - ->expects($this->at(0)) - ->method('reserveFromTube') - ->with($this->equalTo('my-queue1'), $this->equalTo(5)) - ->will($this->returnValue($job1 = new Job(1, 'message1'))); - $this->pheanstalk - ->expects($this->at(1)) - ->method('reserveFromTube') - ->with($this->equalTo('my-queue2'), $this->equalTo(5)) - ->will($this->returnValue($job2 = new Job(2, 'message2'))); - $this->pheanstalk - ->expects($this->at(1)) - ->method('reserveFromTube') - ->with($this->equalTo('my-queue2'), $this->equalTo(5)) - ->will($this->returnValue(null)); - - $this->assertEquals(array('message1', $job1), $this->driver->popMessage('my-queue1')); - $this->assertEquals(array('message2', $job2), $this->driver->popMessage('my-queue2')); - $this->assertEquals(array(null, null), $this->driver->popMessage('my-queue2')); - } -} diff --git a/tests/Driver/PhpAmqpDriverTest.php b/tests/Driver/PhpAmqpDriverTest.php deleted file mode 100644 index ee899067..00000000 --- a/tests/Driver/PhpAmqpDriverTest.php +++ /dev/null @@ -1,160 +0,0 @@ -phpAmqpChannel = $this->getMockBuilder('\PhpAmqpLib\Channel\AMQPChannel') - ->setMethods(array( - 'basic_publish', - 'basic_get', - 'basic_ack', - 'exchange_declare', - 'queue_declare', - 'queue_bind' - )) - ->disableOriginalConstructor() - ->getMock(); - - $this->phpAmqpConnection = $this->getMockBuilder('\PhpAmqpLib\Connection\AMQPStreamConnection') - ->setMethods(array( - 'channel', - )) - ->disableOriginalConstructor() - ->getMock(); - - $this->phpAmqpConnection - ->expects($this->any()) - ->method('channel') - ->willReturn($this->phpAmqpChannel); - - $this->driver = new PhpAmqpDriver($this->phpAmqpConnection, self::EXCHANGE_NAME); - } - - public function testItImplementsDriverInterface() - { - $this->assertInstanceOf('Bernard\Driver', $this->driver); - } - - public function testItCreatesQueue() - { - $this->phpAmqpChannel - ->expects($this->once()) - ->method('exchange_declare') - ->with(self::EXCHANGE_NAME, 'direct', false, true, false) - ; - $this->phpAmqpChannel - ->expects($this->once()) - ->method('queue_declare') - ->with('foo-queue', false, true, false, false) - ; - $this->phpAmqpChannel - ->expects($this->once()) - ->method('queue_bind') - ->with('foo-queue', self::EXCHANGE_NAME, 'foo-queue') - ; - - $this->driver->createQueue('foo-queue'); - } - - public function testItPushesMessages() - { - $this->phpAmqpChannel - ->expects($this->once()) - ->method('basic_publish') - ->with( - $this->callback(function(AMQPMessage $message) { - return $message->body == 'dummy push message'; - }), - self::EXCHANGE_NAME, - 'not-relevant' - ); - $this->driver->pushMessage('not-relevant', 'dummy push message'); - } - - public function testItUsesDefaultParameters() - { - $this->driver = new PhpAmqpDriver( - $this->phpAmqpConnection, - self::EXCHANGE_NAME, - array('delivery_mode' => 2) - ); - - $this->phpAmqpChannel - ->expects($this->once()) - ->method('basic_publish') - ->with( - $this->callback(function(AMQPMessage $message) { - return $message->get('delivery_mode') === 2; - }), - self::EXCHANGE_NAME, - 'not-relevant' - ); - $this->driver->pushMessage('not-relevant', 'dummy push message'); - } - - public function testItPopsMessages() - { - $amqpMessage = new AMQPMessage('bar'); - $amqpMessage->delivery_info['delivery_tag'] = 'alright'; - - $this->phpAmqpChannel - ->expects($this->once()) - ->method('basic_get') - ->with($this->equalTo('foo-queue')) - ->willReturn($amqpMessage); - - $this->assertEquals(['bar', 'alright'], $this->driver->popMessage('foo-queue')); - } - - public function testItPopsArrayWithNullsWhenThereAreNoMessages() - { - $startTime = microtime(true); - - $this->phpAmqpChannel - ->expects($this->any()) - ->method('basic_get') - ->with($this->equalTo('foo-queue')) - ->willReturn(null); - - $result = $this->driver->popMessage('foo-queue', 0.1); - $duration = microtime(true) - $startTime; - - $this->assertEquals([null, null], $result); - $this->assertGreaterThan(0.1, $duration); - } - - public function testItAcknowledgesMessage() - { - $this->phpAmqpChannel - ->expects($this->once()) - ->method('basic_ack') - ->with('delivery-tag'); - - $this->driver->acknowledgeMessage('irrelevant', 'delivery-tag'); - } -} diff --git a/tests/Driver/PhpRedisDriverTest.php b/tests/Driver/PhpRedisDriverTest.php deleted file mode 100644 index d0c7efc4..00000000 --- a/tests/Driver/PhpRedisDriverTest.php +++ /dev/null @@ -1,100 +0,0 @@ -markTestSkipped('"redis" extension is not loaded.'); - } - - $this->redis = $this->getMockBuilder('Redis')->setMethods(array( - 'lLen', - 'sMembers', - 'lRange', - 'blPop', - 'sRemove', - 'del', - 'sAdd', - 'sContains', - 'rPush', - 'sRem', - ))->getMock(); - - $this->connection = new PhpRedisDriver($this->redis); - } - - public function testItImplementsDriverInterface() - { - $this->assertInstanceOf('Bernard\Driver', $this->connection); - } - - public function testItCountsNumberOfMessagesInQueue() - { - $this->redis->expects($this->once())->method('lLen')->with($this->equalTo('queue:send-newsletter')) - ->will($this->returnValue(4)); - - $this->assertEquals(4, $this->connection->countMessages('send-newsletter')); - } - - public function testItGetsAllKeys() - { - $this->redis->expects($this->once())->method('sMembers')->with($this->equalTo('queues')) - ->will($this->returnValue(array('failed', 'queue1'))); - - $this->assertEquals(array('failed', 'queue1'), $this->connection->listQueues()); - } - - public function testItPeeksInAQueue() - { - $this->redis->expects($this->at(0))->method('lRange') - ->with($this->equalTo('queue:my-queue'), $this->equalTo(10), $this->equalTo(19)) - ->will($this->returnValue(array('message1'))); - - $this->redis->expects($this->at(1))->method('lRange') - ->with($this->equalTo('queue:send-newsletter'), $this->equalTo(0), $this->equalTo(19)) - ->will($this->returnValue(array('message2'))); - - $this->assertEquals(array('message1'), $this->connection->peekQueue('my-queue', 10, 10)); - $this->assertEquals(array('message2'), $this->connection->peekQueue('send-newsletter')); - - } - - public function testItRemovesAQueue() - { - $this->redis->expects($this->once())->method('del')->with($this->equalTo('queue:name')); - $this->redis->expects($this->once())->method('srem')->with($this->equalTo('queues'), $this->equalTo('name')); - - $this->connection->removeQueue('name'); - } - - public function testItCreatesAQueue() - { - $this->redis->expects($this->once())->method('sAdd')->with($this->equalTo('queues'), $this->equalTo('send-newsletter')); - - $this->connection->createQueue('send-newsletter'); - } - - public function testItPushesMessages() - { - $this->redis->expects($this->once())->method('rPush')->with($this->equalTo('queue:send-newsletter'), $this->equalTo('This is a message')); - - $this->connection->pushMessage('send-newsletter', 'This is a message'); - } - - public function testItPopMessages() - { - $this->redis->expects($this->at(0))->method('blPop')->with($this->equalTo(array('queue:send-newsletter'))) - ->will($this->returnValue(array('my-queue', 'message1'))); - - $this->redis->expects($this->at(1))->method('blPop')->with($this->equalTo(array('queue:ask-forgiveness')), $this->equalTo(30)) - ->will($this->returnValue(array('my-queue2', 'message2'))); - - $this->assertEquals(array('message1', null), $this->connection->popMessage('send-newsletter')); - $this->assertEquals(array('message2', null), $this->connection->popMessage('ask-forgiveness', 30)); - } -} diff --git a/tests/Driver/PostgreSQLDoctrineDriverTest.php b/tests/Driver/PostgreSQLDoctrineDriverTest.php deleted file mode 100644 index 6e0b60a6..00000000 --- a/tests/Driver/PostgreSQLDoctrineDriverTest.php +++ /dev/null @@ -1,40 +0,0 @@ - 'pdo_pgsql', - 'dbname' => 'bernard_test', - ); - - $this->assertEquals($params, $this->driver->info()); - } - - protected function createConnection() - { - return DriverManager::getConnection(array( - 'driver' => 'pdo_pgsql', - 'user' => 'postgres', - 'dbname' => 'bernard_test', - 'password' => '', - )); - } -} diff --git a/tests/Driver/PredisDriverTest.php b/tests/Driver/PredisDriverTest.php deleted file mode 100644 index b8330edf..00000000 --- a/tests/Driver/PredisDriverTest.php +++ /dev/null @@ -1,40 +0,0 @@ -redis = $this->getMockBuilder('Predis\Client')->setMethods(array( - 'lLen', - 'sMembers', - 'lRange', - 'blPop', - 'sRemove', - 'del', - 'sAdd', - 'sContains', - 'rPush', - 'sRem', - ))->getMock(); - - $this->connection = new PredisDriver($this->redis); - } - - public function testItPopMessages() - { - $this->redis->expects($this->at(0))->method('blPop')->with($this->equalTo('queue:send-newsletter')) - ->will($this->returnValue(array('my-queue', 'message1'))); - - $this->redis->expects($this->at(1))->method('blPop')->with($this->equalTo('queue:ask-forgiveness'), $this->equalTo(30)) - ->will($this->returnValue(array('my-queue2', 'message2'))); - - $this->assertEquals(array('message1', null), $this->connection->popMessage('send-newsletter')); - $this->assertEquals(array('message2', null), $this->connection->popMessage('ask-forgiveness', 30)); - } -} diff --git a/tests/Driver/PrefetchMessageCacheTest.php b/tests/Driver/PrefetchMessageCacheTest.php index eb329ad0..93a595a5 100644 --- a/tests/Driver/PrefetchMessageCacheTest.php +++ b/tests/Driver/PrefetchMessageCacheTest.php @@ -1,17 +1,21 @@ push('my-queue', array('message1', 'r0')); + $driverMessage = new \Bernard\Driver\Message('message1', 'r0'); + + $cache = new PrefetchMessageCache(); + $cache->push('my-queue', $driverMessage); - $this->assertEquals(array('message1', 'r0'), $cache->pop('my-queue')); - $this->assertInternalType('null', $cache->pop('my-queue')); + $this->assertEquals($driverMessage, $cache->pop('my-queue')); + $this->assertNull($cache->pop('my-queue')); } } diff --git a/tests/Driver/SQLiteDoctrineDriverTest.php b/tests/Driver/SQLiteDoctrineDriverTest.php deleted file mode 100644 index f12fb422..00000000 --- a/tests/Driver/SQLiteDoctrineDriverTest.php +++ /dev/null @@ -1,38 +0,0 @@ - true, - 'driver' => 'pdo_sqlite', - ); - - $this->assertEquals($params, $this->driver->info()); - } - - protected function createConnection() - { - return DriverManager::getConnection(array( - 'memory' => true, - 'driver' => 'pdo_sqlite', - )); - } -} diff --git a/tests/Driver/SqsDriverTest.php b/tests/Driver/SqsDriverTest.php deleted file mode 100644 index 29b665a8..00000000 --- a/tests/Driver/SqsDriverTest.php +++ /dev/null @@ -1,300 +0,0 @@ -sqs = $this->getMockBuilder('Aws\Sqs\SqsClient') - ->disableOriginalConstructor() - ->setMethods(array( - 'createQueue', - 'deleteQueue', - 'getQueueUrl', - 'getQueueAttributes', - 'listQueues', - 'sendMessage', - 'receiveMessage', - 'deleteMessage', - )) - ->getMock(); - - $this->driver = new SqsDriver($this->sqs, ['send-newsletter' => 'url']); - } - - public function testItExposesInfo() - { - $driver = new SqsDriver($this->sqs, [], 10); - - $this->assertEquals(['prefetch' => 10], $driver->info()); - $this->assertEquals(['prefetch' => 2], $this->driver->info()); - } - - public function testItImplementsDriverInterface() - { - $this->assertInstanceOf('Bernard\Driver\AbstractPrefetchDriver', $this->driver); - } - - public function testItCreatesQueue() - { - $this->sqs - ->expects($this->once()) - ->method('createQueue') - ->with($this->equalTo(['QueueName' => self::DUMMY_QUEUE_NAME])) - ->will($this->returnValue( - $this->wrapResult([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX, - ]) - )); - - // Calling this twice asserts that if queue exists - // there won't be attempt to create it. - $this->driver->createQueue(self::DUMMY_QUEUE_NAME); - $this->driver->createQueue(self::DUMMY_QUEUE_NAME); - } - - public function testItCreatesFifoQueue() - { - $this->sqs - ->expects($this->once()) - ->method('createQueue') - ->with($this->equalTo([ - 'QueueName' => self::DUMMY_FIFO_QUEUE_NAME, - 'Attributes' => [ - 'FifoQueue' => 'true', - ] - ])) - ->will($this->returnValue( - $this->wrapResult([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX, - ]) - )); - - // Calling this twice asserts that if queue exists - // there won't be attempt to create it. - $this->driver->createQueue(self::DUMMY_FIFO_QUEUE_NAME); - $this->driver->createQueue(self::DUMMY_FIFO_QUEUE_NAME); - } - - public function testItDeletesQueue() - { - $this->assertSqsQueueUrl(); - $this->sqs - ->expects($this->once()) - ->method('deleteQueue') - ->with($this->equalTo(['QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX. '/'. self::DUMMY_QUEUE_NAME])); - - $this->driver->removeQueue(self::DUMMY_QUEUE_NAME); - } - - public function testItCountsNumberOfMessagesInQueue() - { - $this->assertSqsQueueUrl(); - $this->sqs - ->expects($this->once()) - ->method('getQueueAttributes') - ->with($this->equalTo([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX. '/'. self::DUMMY_QUEUE_NAME, - 'AttributeNames' => array('ApproximateNumberOfMessages'), - ])) - ->will($this->returnValue( - $this->wrapResult([ - 'Attributes' => ['ApproximateNumberOfMessages' => 4], - ]) - )); - - $this->assertEquals(4, $this->driver->countMessages(self::DUMMY_QUEUE_NAME)); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Queue "unknown" cannot be resolved to an url. - */ - public function testUnresolveableQueueNameThrowsException() - { - $this->driver->popMessage('unknown'); - } - - public function testItGetsAllQueues() - { - $driver = new SqsDriver($this->sqs, [ - 'import-users' => 'alreadyknowurl/import_users_prod' - ]); - - $this->sqs - ->expects($this->once()) - ->method('listQueues') - ->will($this->returnValue($this->wrapResult([ - 'QueueUrls' => [ - 'https://sqs.eu-west-1.amazonaws.com/123123/failed', - 'https://sqs.eu-west-1.amazonaws.com/123123/queue1', - 'alreadyknowurl/import_users_prod', - ] - ]))); - - $queues = array('import-users', 'failed', 'queue1'); - - $this->assertEquals($queues, $driver->listQueues()); - } - - public function testItPrefetchesMessages() - { - $query = array( - 'QueueUrl' => 'url', - 'MaxNumberOfMessages' => 2, - 'WaitTimeSeconds' => 5, - ); - - $sqsMessages = $this->wrapResult([ - 'Messages' => [ - ['Body' => 'message0', 'ReceiptHandle' => 'r0'], - ['Body' => 'message1', 'ReceiptHandle' => 'r1'], - ], - ]); - - $this->sqs->expects($this->once())->method('receiveMessage') - ->with($this->equalTo($query))->will($this->returnValue($sqsMessages)); - - $this->assertEquals(['message0', 'r0'], $this->driver->popMessage('send-newsletter')); - $this->assertEquals(['message1', 'r1'], $this->driver->popMessage('send-newsletter')); - } - - public function testItPushesMessages() - { - $message = 'This is a message'; - - $this->assertSqsQueueUrl(); - $this->sqs - ->expects($this->once()) - ->method('sendMessage') - ->with($this->equalTo([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX. '/'. self::DUMMY_QUEUE_NAME, - 'MessageBody' => $message - ])); - $this->driver->pushMessage(self::DUMMY_QUEUE_NAME, $message); - } - - public function testItPushesMessagesToFifoQueue() - { - $message = 'This is a message'; - - $this->assertSqsFifoQueueUrl(); - $this->sqs - ->expects($this->once()) - ->method('sendMessage') - ->with($this->equalTo([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX. '/'. self::DUMMY_FIFO_QUEUE_NAME, - 'MessageBody' => $message, - 'MessageGroupId' => \Bernard\Driver\SqsDriver::class . '::pushMessage', - 'MessageDeduplicationId' => md5($message), - - ])); - $this->driver->pushMessage(self::DUMMY_FIFO_QUEUE_NAME, $message); - } - - public function testItPopMessages() - { - $this->sqs - ->expects($this->at(0)) - ->method('getQueueUrl') - ->with($this->equalTo([ - 'QueueName' => self::DUMMY_QUEUE_NAME. '0', - ])) - ->will($this->returnValue($this->wrapResult([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX - . '/'. self::DUMMY_QUEUE_NAME. '0', - ]))); - $this->sqs - ->expects($this->at(1)) - ->method('receiveMessage') - ->with($this->equalTo([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX. '/'. self::DUMMY_QUEUE_NAME. '0', - 'MaxNumberOfMessages' => 2, - 'WaitTimeSeconds' => 5, - ])) - ->will($this->returnValue($this->wrapResult([ - 'Messages' => [ - ['Body' => 'message0', 'ReceiptHandle' => 'r0'] - ] - ]))); - - $this->sqs - ->expects($this->at(2)) - ->method('getQueueUrl') - ->with($this->equalTo([ - 'QueueName' => self::DUMMY_QUEUE_NAME. '1' - ])) - ->will($this->returnValue($this->wrapResult([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX . '/my-queue1', - ]))); - $this->sqs - ->expects($this->at(3)) - ->method('receiveMessage') - ->with($this->equalTo([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX. '/my-queue1', - 'MaxNumberOfMessages' => 2, - 'WaitTimeSeconds' => 30, - ])) - ->will($this->returnValue($this->wrapResult([ - 'Messages' => [ - ['Body' => 'message1', 'ReceiptHandle' => 'r1'], - ], - ]))); - - $this->assertEquals(['message0', 'r0'], $this->driver->popMessage('my-queue0')); - $this->assertEquals(['message1', 'r1'], $this->driver->popMessage('my-queue1', 30)); - $this->assertEquals([null, null], $this->driver->popMessage('send-newsletter')); - } - - public function testAcknowledgeMessage() - { - $this->sqs->expects($this->once())->method('deleteMessage') - ->with($this->equalTo([ - 'QueueUrl' => 'url', - 'ReceiptHandle' => 'r0', - ])); - - $this->driver->acknowledgeMessage('send-newsletter', 'r0'); - } - - private function assertSqsQueueUrl() - { - $this->sqs - ->expects($this->once()) - ->method('getQueueUrl') - ->with($this->equalTo(['QueueName' => self::DUMMY_QUEUE_NAME])) - ->will($this->returnValue($this->wrapResult([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX - . '/'. self::DUMMY_QUEUE_NAME, - ]))); - } - - private function assertSqsFifoQueueUrl() - { - $this->sqs - ->expects($this->once()) - ->method('getQueueUrl') - ->with($this->equalTo(['QueueName' => self::DUMMY_FIFO_QUEUE_NAME])) - ->will($this->returnValue($this->wrapResult([ - 'QueueUrl' => self::DUMMY_QUEUE_URL_PREFIX - . '/'. self::DUMMY_FIFO_QUEUE_NAME, - ]))); - } - - private function wrapResult($data = []) - { - return class_exists('Aws\Common\Aws') - ? new Model($data) - : new Result($data); - } -} diff --git a/tests/EnvelopeTest.php b/tests/EnvelopeTest.php index 48d57068..f244aeae 100644 --- a/tests/EnvelopeTest.php +++ b/tests/EnvelopeTest.php @@ -1,43 +1,44 @@ assertEquals(time(), $envelope->getTimestamp()); - $this->assertEquals('Bernard\Message\PlainMessage', $envelope->getClass()); + $this->assertEquals(PlainMessage::class, $envelope->getClass()); $this->assertEquals('SendNewsletter', $envelope->getName()); $this->assertSame($message, $envelope->getMessage()); } - public function testNotDelayedMetadata() + public function testNotDelayedMetadata(): void { - $envelope = new Envelope($message = new DefaultMessage('SendNewsletter')); + $envelope = new Envelope($message = new PlainMessage('SendNewsletter')); $this->assertFalse($envelope->isDelayed()); $this->assertEquals(0, $envelope->getDelay()); } - public function testDelayedMetadata() + public function testDelayedMetadata(): void { - $envelope = new Envelope($message = new DefaultMessage('SendNewsletter'), 10); + $envelope = new Envelope($message = new PlainMessage('SendNewsletter'), 10); $this->assertTrue($envelope->isDelayed()); $this->assertEquals(10, $envelope->getDelay()); } - /** - * @expectedException Bernard\Exception\InvalidOperationException - * @expectedExceptionMessage Delay must be greater or equal than zero - */ - public function testNegativeDelay() + public function testNegativeDelay(): void { - $envelope = new Envelope($message = new DefaultMessage('SendNewsletter'), -10); + $this->expectException(InvalidOperationException::class); + $this->expectExceptionMessage('Delay must be greater or equal than zero'); + + new Envelope(new PlainMessage('SendNewsletter'), -10); } } diff --git a/tests/Event/EnvelopeEventTest.php b/tests/Event/EnvelopeEventTest.php index 898a69c6..6583eb22 100644 --- a/tests/Event/EnvelopeEventTest.php +++ b/tests/Event/EnvelopeEventTest.php @@ -1,24 +1,30 @@ envelope = $this->getMockBuilder('Bernard\Envelope')->disableOriginalConstructor() + $message = $this->getMockBuilder(Message::class)->disableOriginalConstructor() ->getMock(); + $this->envelope = new Envelope($message); $this->queue = $this->createMock('Bernard\Queue'); } - public function testIsEvent() + public function testIsEvent(): void { - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', new EnvelopeEvent($this->envelope, $this->queue)); + $this->assertInstanceOf(Event::class, new EnvelopeEvent($this->envelope, $this->queue)); } - public function hasEnvelopeAndQueue() + public function hasEnvelopeAndQueue(): void { $event = new EnvelopeEvent($this->envelope, $this->queue); diff --git a/tests/Event/RejectEnvelopeEventTest.php b/tests/Event/RejectEnvelopeEventTest.php index 52241b08..3c95d433 100644 --- a/tests/Event/RejectEnvelopeEventTest.php +++ b/tests/Event/RejectEnvelopeEventTest.php @@ -1,8 +1,12 @@ envelope = $this->getMockBuilder('Bernard\Envelope') - ->disableOriginalConstructor()->getMock(); + $message = $this->getMockBuilder(Message::class)->disableOriginalConstructor() + ->getMock(); + $this->envelope = new Envelope($message); $this->queue = $this->createMock('Bernard\Queue'); } - public function testExtendsEnvelopeEvent() + public function testExtendsEnvelopeEvent(): void { $event = new RejectEnvelopeEvent($this->envelope, $this->queue, new \Exception()); $this->assertInstanceOf('Bernard\Event\EnvelopeEvent', $event); } - public function testRetrieveException() + public function testRetrieveException(): void { $e = new \Exception(); $event = new RejectEnvelopeEvent($this->envelope, $this->queue, $e); @@ -41,7 +46,7 @@ public function testRetrieveException() /** * @requires PHP 7.0 */ - public function testCanContainThrowable() + public function testCanContainThrowable(): void { $error = new \TypeError(); $event = new RejectEnvelopeEvent($this->envelope, $this->queue, $error); diff --git a/tests/EventListener/ErrorLogSubscriberTest.php b/tests/EventListener/ErrorLogSubscriberTest.php index 2c9a7df4..848c9f41 100644 --- a/tests/EventListener/ErrorLogSubscriberTest.php +++ b/tests/EventListener/ErrorLogSubscriberTest.php @@ -1,12 +1,17 @@ markTestSkipped("HHVM does not support `ini_set('error_log', '/path/to/log')`"); } - $this->envelope = $this->getMockBuilder('Bernard\Envelope') - ->disableOriginalConstructor()->getMock(); + $this->message = $this->getMockBuilder(Message::class)->disableOriginalConstructor() + ->getMock(); + $this->envelope = new Envelope($this->message); $this->queue = $this->createMock('Bernard\Queue'); $this->producer = $this->getMockBuilder('Bernard\Producer')->disableOriginalConstructor()->getMock(); $this->subscriber = new ErrorLogSubscriber($this->producer, 'failures'); - $this->iniErrorLog = ini_get('error_log'); + $this->iniErrorLog = \ini_get('error_log'); $this->errorLogFile = tempnam(sys_get_temp_dir(), 'phpunit'); ini_set('error_log', $this->errorLogFile); ini_set('error_log', $this->errorLogFile); } - public function tearDown() + protected function tearDown(): void { ini_set('error_log', $this->iniErrorLog); unlink($this->errorLogFile); } - public function testGetSubscribedEvents() + public function testGetSubscribedEvents(): void { $expected = [ 'bernard.reject' => ['onReject'], @@ -46,10 +52,9 @@ public function testGetSubscribedEvents() $this->assertEquals($expected, $actual); } - public function testOnRejectException() + public function testOnRejectException(): void { - - $this->envelope->expects($this->once()) + $this->message->expects($this->once()) ->method('getName') ->willReturn('foo'); $error = new \Exception('bar'); @@ -63,9 +68,9 @@ public function testOnRejectException() /** * @requires PHP 7.0 */ - public function testOnRejectError() + public function testOnRejectError(): void { - $this->envelope->expects($this->once()) + $this->message->expects($this->once()) ->method('getName') ->willReturn('foo'); $error = new \TypeError('bar'); @@ -76,12 +81,12 @@ public function testOnRejectError() $this->assertStringEndsWith($expected, $actual); } - public function testOnRejectObject() + public function testOnRejectObject(): void { - $this->envelope->expects($this->once()) + $this->message->expects($this->once()) ->method('getName') ->willReturn('foo'); - $error = new \stdClass; + $error = new \stdClass(); $event = new RejectEnvelopeEvent($this->envelope, $this->queue, $error); $expected = ' [bernard] caught unknown error type stdClass while processing foo.'; $this->subscriber->onReject($event); diff --git a/tests/EventListener/FailureSubscriberTest.php b/tests/EventListener/FailureSubscriberTest.php index b69a2814..6f92d397 100644 --- a/tests/EventListener/FailureSubscriberTest.php +++ b/tests/EventListener/FailureSubscriberTest.php @@ -1,11 +1,13 @@ producer = $this->getMockBuilder('Bernard\Producer')->disableOriginalConstructor()->getMock(); $this->subscriber = new FailureSubscriber($this->producer, 'failures'); } - public function testAcknowledgeMessageAndEnqueue() + public function testAcknowledgeMessageAndEnqueue(): void { - $envelope = new Envelope($message = new DefaultMessage('bar')); + $envelope = new Envelope($message = new PlainMessage('bar')); $this->producer->expects($this->once()) ->method('produce') ->with($message, 'failures'); - $this->subscriber->onReject(new RejectEnvelopeEvent($envelope, new InMemoryQueue('foo'), new \Exception)); - + $this->subscriber->onReject(new RejectEnvelopeEvent($envelope, new InMemoryQueue('foo'), new \Exception())); } } diff --git a/tests/EventListener/LoggerSubscriberTest.php b/tests/EventListener/LoggerSubscriberTest.php new file mode 100644 index 00000000..a0a7c557 --- /dev/null +++ b/tests/EventListener/LoggerSubscriberTest.php @@ -0,0 +1,56 @@ +getMockBuilder(LoggerInterface::class)->getMock(); + $loggerMock->expects($this->once())->method('info'); + + $subscriber = new LoggerSubscriber($loggerMock); + $subscriber->onProduce($this->prophesize(EnvelopeEvent::class)->reveal()); + } + + public function testsLogsInfoOnInvoke(): void + { + $loggerMock = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $loggerMock->expects($this->once())->method('info'); + + $subscriber = new LoggerSubscriber($loggerMock); + $subscriber->onInvoke($this->prophesize(EnvelopeEvent::class)->reveal()); + } + + public function testLogsErrorOnReject(): void + { + $loggerMock = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $loggerMock->expects($this->once())->method('error'); + + $subscriber = new LoggerSubscriber($loggerMock); + $subscriber->onReject($this->prophesize(RejectEnvelopeEvent::class)->reveal()); + } + + public function testGetSubscribedEvents(): void + { + $events = LoggerSubscriber::getSubscribedEvents(); + $expectedEvents = [ + 'bernard.produce' => ['onProduce'], + 'bernard.invoke' => ['onInvoke'], + 'bernard.reject' => ['onReject'], + ]; + + $this->assertIsArray($events); + $this->assertEquals($expectedEvents, $events); + } +} diff --git a/tests/Exception/ExceptionTest.php b/tests/Exception/ExceptionTest.php deleted file mode 100644 index 3b136ae0..00000000 --- a/tests/Exception/ExceptionTest.php +++ /dev/null @@ -1,34 +0,0 @@ -assertInstanceOf('\Bernard\Exception\Exception', $e); - $this->assertInstanceOf($exception, $e); - $this->assertInstanceOf($base, $e); - return; - } - $this->fail("Exception not caught"); - } - - public function exceptionProvider() - { - return [ - ['\Bernard\Exception\InvalidArgumentException', '\InvalidArgumentException'], - ['\Bernard\Exception\InvalidOperationException', '\Exception'], - ['\Bernard\Exception\NotImplementedException', '\BadMethodCallException'], - ['\Bernard\Exception\QueueNotFoundException', '\RuntimeException'], - ['\Bernard\Exception\ReceiverNotFoundException', '\RuntimeException'], - ['\Bernard\Exception\ServiceUnavailableException', '\RuntimeException'], - ]; - } -} diff --git a/tests/Fixtures/PushTask.php b/tests/Fixtures/PushTask.php deleted file mode 100644 index a8e0c190..00000000 --- a/tests/Fixtures/PushTask.php +++ /dev/null @@ -1,28 +0,0 @@ -url_path = $url_path; - $this->query_data = $query_data; - $this->options = $options; - } - - public function add($queueName = 'default') - { - static::$messages[$queueName][] = $this; - } -} diff --git a/tests/Fixtures/SendNewsletterMessage.php b/tests/Fixtures/SendNewsletterMessage.php index 179f2483..85195257 100644 --- a/tests/Fixtures/SendNewsletterMessage.php +++ b/tests/Fixtures/SendNewsletterMessage.php @@ -1,31 +1,36 @@ newsletterId = $data['newsletterId']; } diff --git a/tests/Fixtures/Service.php b/tests/Fixtures/Service.php deleted file mode 100644 index 4a5c994d..00000000 --- a/tests/Fixtures/Service.php +++ /dev/null @@ -1,29 +0,0 @@ -importUsers = true; - } - - public function createFile() - { - touch(__DIR__ . '/create_file.test'); - } - - public function importReport(Report $report) - { - // note: the class hinted on this method does not exist on purpose, as calling this method should cause a - // Throwable to be thrown - } -} diff --git a/tests/Message/AbstractMessageTest.php b/tests/Message/AbstractMessageTest.php deleted file mode 100644 index 164366e7..00000000 --- a/tests/Message/AbstractMessageTest.php +++ /dev/null @@ -1,21 +0,0 @@ -assertInstanceOf('Bernard\Message', new Fixtures\SendNewsletterMessage); - $this->assertInstanceOf('Bernard\Message\AbstractMessage', new Fixtures\SendNewsletterMessage); - } - - public function testItUsesClassNameAsNameAndQueueNameNormalized() - { - $message = new Fixtures\SendNewsletterMessage(); - $this->assertEquals('SendNewsletter', $message->getName()); - $this->assertEquals('send-newsletter', $message->getQueue()); - } -} diff --git a/tests/Message/DefaultMessageTest.php b/tests/Message/DefaultMessageTest.php deleted file mode 100644 index 49afdb1d..00000000 --- a/tests/Message/DefaultMessageTest.php +++ /dev/null @@ -1,13 +0,0 @@ -createMessage('SendNewsletter'); + $message = new PlainMessage('SendNewsletter', [ + 'key1' => 1, + 'key2' => [1, 2, 3, 4], + 'key3' => null, + ]); - $this->assertEquals('SendNewsletter', $message->getName()); + $this->assertArrayHasKey('key1', $message); + + $this->assertEquals(1, $message['key1']); + $this->assertEquals([1, 2, 3, 4], $message['key2']); + $this->assertNull($message['key3']); + + $this->assertTrue(isset($message->key1)); + + $this->assertEquals(1, $message->key1); + $this->assertEquals([1, 2, 3, 4], $message->key2); + $this->assertNull($message->key3); } - public function testItHasArguments() + public function testItImplementsArrayAccess(): void { - $message = $this->createMessage('SendNewsletter', array( - 'key1' => 1, - 'key2' => array(1,2,3,4), - 'key3' => null, - )); + $this->assertInstanceOf(\ArrayAccess::class, new PlainMessage('SendNewsletter')); + } - $this->assertTrue(isset($message['key1'])); - $this->assertTrue(isset($message['key1'])); + public function testItIsImmutableToMagicSet(): void + { + $this->expectException(\LogicException::class); - $this->assertEquals(1, $message['key1']); - $this->assertEquals(array(1,2,3,4), $message['key2']); - $this->assertInternalType('null', $message['key3']); + $message = new PlainMessage('SendNewsletter'); + $message->key1 = 1; + } + public function testItIsImmutableToMagicUnset(): void + { + $this->expectException(\LogicException::class); + + $message = new PlainMessage('SendNewsletter', ['key1' => 1]); + unset($message->key1); } - public function testItImplementsArrayAccess() + public function testItIsImmutableToOffsetSet(): void { - $this->assertInstanceOf('ArrayAccess', $this->createMessage('SendNewsletter')); + $this->expectException(\LogicException::class); + + $message = new PlainMessage('SendNewsletter'); + $message['key1'] = 1; } - protected function createMessage($name, array $data = []) + public function testItIsImmutableToOffsetUnset(): void { - return new PlainMessage($name, $data); + $this->expectException(\LogicException::class); + + $message = new PlainMessage('SendNewsletter', ['key1' => 1]); + unset($message['key1']); } } diff --git a/tests/Normalizer/PlainMessageNormalizerTest.php b/tests/Normalizer/PlainMessageNormalizerTest.php index 30993af0..6a404b2d 100644 --- a/tests/Normalizer/PlainMessageNormalizerTest.php +++ b/tests/Normalizer/PlainMessageNormalizerTest.php @@ -1,16 +1,29 @@ normalize(new PlainMessage('foobar')); + + $this->assertEquals(['name' => 'foobar', 'arguments' => []], $normalized); + } + + public function testItDenormalizesANormalizedMessage(): void { - $sut = new PlainMessageNormalizer(); - $normalized = $sut->denormalize(['name' => 'foobar', 'arguments' => []], null); - $this->assertTrue($normalized instanceof PlainMessage); + $normalizer = new PlainMessageNormalizer(); + + $denormalized = $normalizer->denormalize(['name' => 'foobar', 'arguments' => []], null); + + $this->assertInstanceOf(PlainMessage::class, $denormalized); } } diff --git a/tests/ProducerTest.php b/tests/ProducerTest.php index 620a0007..b57a0ab7 100644 --- a/tests/ProducerTest.php +++ b/tests/ProducerTest.php @@ -1,28 +1,29 @@ queues = new InMemoryFactory; - $this->dispatcher = new EventDispatcher; + $this->queues = new InMemoryFactory(); + $this->dispatcher = new EventDispatcher(); $this->producer = new Producer($this->queues, $this->dispatcher); } - public function testDispatchesEvent() + public function testDispatchesEvent(): void { - $args = array(); + $args = []; - $this->dispatcher->addListener('bernard.produce', function ($event) use (&$args) { - $args = array('envelope' => $event->getEnvelope(), 'queue' => $event->getQueue()); + $this->dispatcher->addListener('bernard.produce', function ($event) use (&$args): void { + $args = ['envelope' => $event->getEnvelope(), 'queue' => $event->getQueue()]; }); $message = new PlainMessage('Message'); @@ -33,7 +34,7 @@ public function testDispatchesEvent() $this->assertSame($this->queues->create('my-queue'), $args['queue']); } - public function testItDelegatesMessagesToQueue() + public function testItDelegatesMessagesToQueue(): void { $message = new PlainMessage('SendNewsletter'); @@ -44,21 +45,20 @@ public function testItDelegatesMessagesToQueue() $this->assertSame($message, $envelope->getMessage()); } - public function testWithDelay() + public function testItUsesGivenQueueName(): void { - $message = new DefaultMessage('SendNewsletter'); + $message = new PlainMessage('SendNewsletter'); - $this->producer->produce($message, null, 10); + $this->producer->produce($message, 'something-else'); - $envelope = $this->queues->create('send-newsletter')->dequeue(); + $envelope = $this->queues->create('something-else')->dequeue(); - $this->assertTrue($envelope->isDelayed()); - $this->assertEquals(10, $envelope->getDelay()); + $this->assertSame($message, $envelope->getMessage()); } - public function testItUsesGivenQueueName() + public function testWithDelay(): void { - $message = new DefaultMessage('SendNewsletter'); + $message = new PlainMessage('SendNewsletter'); $this->producer->produce($message, null, 10); diff --git a/tests/Queue/AbstractQueueTest.php b/tests/Queue/AbstractQueueTest.php index b9f0afaa..5a9e9f2e 100644 --- a/tests/Queue/AbstractQueueTest.php +++ b/tests/Queue/AbstractQueueTest.php @@ -1,5 +1,7 @@ expectException(\Bernard\Exception\InvalidOperationException::class); + $this->expectExceptionMessage('Queue "send-newsletter" is closed.'); + $queue = $this->createQueue('send-newsletter'); $queue->close(); - call_user_func_array(array($queue, $method), $arguments); + \call_user_func_array([$queue, $method], $arguments); } - public function testNameAsToString() + public function testNameAsToString(): void { $queue = $this->createQueue('long-name'); @@ -29,17 +32,17 @@ public function testNameAsToString() public function dataClosedMethods() { - return array( - array('peek', array(0, 10)), - array('count'), - array('dequeue'), - array('enqueue', array( - new Envelope($this->createMock('Bernard\Message')) - )), - array('acknowledge', array( - new Envelope($this->createMock('Bernard\Message')) - )), - ); + return [ + ['peek', [0, 10]], + ['count'], + ['dequeue'], + ['enqueue', [ + new Envelope($this->createMock('Bernard\Message')), + ]], + ['acknowledge', [ + new Envelope($this->createMock('Bernard\Message')), + ]], + ]; } abstract protected function createQueue($name); diff --git a/tests/Queue/InMemoryQueueTest.php b/tests/Queue/InMemoryQueueTest.php index fe8eda59..505bce94 100644 --- a/tests/Queue/InMemoryQueueTest.php +++ b/tests/Queue/InMemoryQueueTest.php @@ -1,5 +1,7 @@ createMock('Bernard\Message')); @@ -17,25 +19,25 @@ public function testDequeue() $this->assertCount(1, $queue); $this->assertSame($envelope, $queue->dequeue()); $this->assertCount(0, $queue); - $this->assertInternalType('null', $queue->dequeue()); + $this->assertNull($queue->dequeue()); } - public function testPeek() + public function testPeek(): void { $queue = new InMemoryQueue('send-newsletter'); - $this->assertEquals(array(), $queue->peek(0, 10)); + $this->assertEquals([], $queue->peek(0, 10)); - $queue->enqueue($envelope = $this->getEnvelope()); + $queue->enqueue($envelope = $this->getEnvelope()); $queue->enqueue($envelope1 = $this->getEnvelope()); $queue->enqueue($envelope2 = $this->getEnvelope()); $queue->enqueue($envelope3 = $this->getEnvelope()); $this->assertCount(4, $queue); - $this->assertSame(array( + $this->assertSame([ $envelope1, $envelope2, - ), $queue->peek(1, 2)); + ], $queue->peek(1, 2)); $this->assertCount(4, $queue); } diff --git a/tests/Queue/PersistentQueueTest.php b/tests/Queue/PersistentQueueTest.php index 4c5dba87..3dfe02bc 100644 --- a/tests/Queue/PersistentQueueTest.php +++ b/tests/Queue/PersistentQueueTest.php @@ -1,24 +1,27 @@ driver = $this->createMock('Bernard\Driver'); $this->serializer = $this->createMock('Bernard\Serializer'); } - public function testEnqueue() + public function testEnqueue(): void { $envelope = new Envelope($this->createMock('Bernard\Message')); $this->serializer->expects($this->once())->method('serialize')->with($this->equalTo($envelope)) - ->will($this->returnValue('serialized message')); + ->willReturn('serialized message'); $this->driver->expects($this->once())->method('pushMessage') ->with($this->equalTo('send-newsletter'), $this->equalTo('serialized message')); @@ -26,33 +29,7 @@ public function testEnqueue() $queue->enqueue($envelope); } - /** - * @expectedException Bernard\Exception\InvalidOperationException - * @expectedExceptionMessage This driver can't manage delayed messages - */ - public function testEnqueueDelayedWithNotDelayableDriver() - { - $envelope = new Envelope($this->createMock('Bernard\Message'), 10); - - $queue = $this->createQueue('send-newsletter'); - $queue->enqueue($envelope); - } - - public function testEnqueueDelayed() - { - $this->driver = $this->createMock('Bernard\DelayableDriver'); - $envelope = new Envelope($this->createMock('Bernard\Message'), 10); - - $this->serializer->expects($this->once())->method('serialize')->with($this->equalTo($envelope)) - ->will($this->returnValue('serialized message')); - $this->driver->expects($this->once())->method('pushMessageWithDelay') - ->with($this->equalTo('send-newsletter'), $this->equalTo('serialized message'), $this->equalTo(10)); - - $queue = $this->createQueue('send-newsletter'); - $queue->enqueue($envelope); - } - - public function testAcknowledge() + public function testAcknowledge(): void { $envelope = new Envelope($this->createMock('Bernard\Message')); @@ -60,17 +37,17 @@ public function testAcknowledge() ->with($this->equalTo('send-newsletter'), $this->equalTo('receipt')); $this->driver->expects($this->once())->method('popMessage')->with($this->equalTo('send-newsletter')) - ->will($this->returnValue(array('message', 'receipt'))); + ->willReturn(new \Bernard\Driver\Message('message', 'receipt')); $this->serializer->expects($this->once())->method('unserialize') - ->will($this->returnValue($envelope)); + ->willReturn($envelope); $queue = $this->createQueue('send-newsletter'); $envelope = $queue->dequeue(); $queue->acknowledge($envelope); } - public function testAcknowledgeOnlyIfReceipt() + public function testAcknowledgeOnlyIfReceipt(): void { $envelope = new Envelope($this->createMock('Bernard\Message')); @@ -80,46 +57,46 @@ public function testAcknowledgeOnlyIfReceipt() $queue->acknowledge($envelope); } - public function testCount() + public function testCount(): void { $this->driver->expects($this->once())->method('countMessages')->with($this->equalTo('send-newsletter')) - ->will($this->returnValue(10)); + ->willReturn(10); $queue = $this->createQueue('send-newsletter'); $this->assertEquals(10, $queue->count()); } - public function testDequeue() + public function testDequeue(): void { $messageWrapper = new Envelope($this->createMock('Bernard\Message')); $this->driver->expects($this->at(1))->method('popMessage')->with($this->equalTo('send-newsletter')) - ->will($this->returnValue(array('serialized', null))); + ->willReturn(new \Bernard\Driver\Message('serialized', null)); $this->driver->expects($this->at(2))->method('popMessage')->with($this->equalTo('send-newsletter')) - ->will($this->returnValue(null)); + ->willReturn(null); $this->serializer->expects($this->once())->method('unserialize')->with($this->equalTo('serialized')) - ->will($this->returnValue($messageWrapper)); + ->willReturn($messageWrapper); $queue = $this->createQueue('send-newsletter'); $this->assertSame($messageWrapper, $queue->dequeue()); - $this->assertInternalType('null', $queue->dequeue()); + $this->assertNull($queue->dequeue()); } /** * @dataProvider peekDataProvider */ - public function testPeekDserializesMessages($index, $limit) + public function testPeekDserializesMessages($index, $limit): void { $this->serializer->expects($this->at(0))->method('unserialize')->with($this->equalTo('message1')); $this->serializer->expects($this->at(1))->method('unserialize')->with($this->equalTo('message2')); $this->serializer->expects($this->at(2))->method('unserialize')->with($this->equalTo('message3')); $this->driver->expects($this->once())->method('peekQueue')->with($this->equalTo('send-newsletter'), $this->equalTo($index), $this->equalTo($limit)) - ->will($this->returnValue(array('message1', 'message2', 'message3'))); + ->willReturn(['message1', 'message2', 'message3']); $queue = $this->createQueue('send-newsletter'); $queue->peek($index, $limit); @@ -128,22 +105,47 @@ public function testPeekDserializesMessages($index, $limit) public function dataClosedMethods() { $methods = parent::dataClosedMethods(); - $methods[] = array('register', array()); + $methods[] = ['register', []]; return $methods; } public function peekDataProvider() { - return array( - array(0, 20), - array(1, 10), - array(20, 100), - ); + return [ + [0, 20], + [1, 10], + [20, 100], + ]; } protected function createQueue($name) { return new PersistentQueue($name, $this->driver, $this->serializer); } + + public function testEnqueueDelayedWithNotDelayableDriver(): void + { + $this->expectException(InvalidOperationException::class); + $this->expectExceptionMessage('This driver can\'t manage delayed messages'); + + $envelope = new Envelope($this->createMock('Bernard\Message'), 10); + + $queue = $this->createQueue('send-newsletter'); + $queue->enqueue($envelope); + } + + public function testEnqueueDelayed(): void + { + $this->driver = $this->createMock('Bernard\DelayableDriver'); + $envelope = new Envelope($this->createMock('Bernard\Message'), 10); + + $this->serializer->expects($this->once())->method('serialize')->with($this->equalTo($envelope)) + ->willReturn($this->returnValue('serialized message')); + $this->driver->expects($this->once())->method('pushMessageWithDelay') + ->with($this->equalTo('send-newsletter'), $this->equalTo('serialized message'), $this->equalTo(10)); + + $queue = $this->createQueue('send-newsletter'); + $queue->enqueue($envelope); + } } diff --git a/tests/Queue/RoundRobinQueueTest.php b/tests/Queue/RoundRobinQueueTest.php index c2bb6eff..6fdd6b7b 100644 --- a/tests/Queue/RoundRobinQueueTest.php +++ b/tests/Queue/RoundRobinQueueTest.php @@ -1,5 +1,7 @@ queues = [ new InMemoryQueue('1'), @@ -30,28 +32,27 @@ public function setUp() $this->round = new RoundRobinQueue($this->queues); } - /** - * @expectedException \DomainException - * @expectedExceptionMessage Unrecognized queue specified: foo - */ - public function testEnqueueWithUnrecognizedQueue() + public function testEnqueueWithUnrecognizedQueue(): void { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage('Unrecognized queue specified: foo'); + $this->round->enqueue($this->getEnvelope('foo')); } - public function testEnqueueWithRecognizedQueue() + public function testEnqueueWithRecognizedQueue(): void { $envelope = $this->getEnvelope('2'); $this->round->enqueue($envelope); $this->assertSame($envelope, $this->round->dequeue()); } - public function testDequeueWithEmptyQueue() + public function testDequeueWithEmptyQueue(): void { $this->assertNull($this->round->dequeue()); } - public function testDequeueRoundRobin() + public function testDequeueRoundRobin(): void { foreach ([ $envelope_1_1 = $this->getEnvelope('1'), @@ -65,11 +66,11 @@ public function testDequeueRoundRobin() $this->assertSame($envelope_1_2, $this->round->dequeue()); } - public function testClose() + public function testClose(): void { $builder = $this->getMockBuilder('Bernard\\Queue\\InMemoryQueue')->setMethods(['close']); $queues = []; - for ($name = 1; $name <= 3; $name++) { + for ($name = 1; $name <= 3; ++$name) { $queue = $builder->setConstructorArgs([$name])->getMock(); $queue ->expects($this->once()) @@ -81,7 +82,7 @@ public function testClose() $round->close(); } - public function testPeek() + public function testPeek(): void { foreach ([ $envelope_1_1 = $this->getEnvelope('1'), @@ -93,12 +94,11 @@ public function testPeek() $this->assertSame([$envelope_3_1], $this->round->peek(1, 1)); } - /** - * @expectedException \DomainException - * @expectedExceptionMessage Unrecognized queue specified: foo - */ - public function testAcknowledgeWithUnrecognizedQueue() + public function testAcknowledgeWithUnrecognizedQueue(): void { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage('Unrecognized queue specified: foo'); + $envelope = $this->getEnvelope('foo'); $this->round->enqueue($envelope); $dequeued = $this->round->dequeue($envelope); @@ -106,7 +106,7 @@ public function testAcknowledgeWithUnrecognizedQueue() $this->round->acknowledge($dequeued); } - public function testAcknowledgeWithRecognizedQueue() + public function testAcknowledgeWithRecognizedQueue(): void { $builder = $this->getMockBuilder('Bernard\\Queue\\InMemoryQueue')->setMethods(['acknowledge']); $envelope = $this->getEnvelope('2'); @@ -128,7 +128,7 @@ public function testAcknowledgeWithRecognizedQueue() $round->acknowledge($envelope); } - public function testToString() + public function testToString(): void { $this->round->enqueue($this->getEnvelope('1')); $this->round->enqueue($this->getEnvelope('2')); @@ -143,7 +143,7 @@ public function testToString() $this->assertSame('3', (string) $this->round); } - public function testCount() + public function testCount(): void { $this->round->enqueue($this->getEnvelope('1')); $this->round->enqueue($this->getEnvelope('2')); diff --git a/tests/QueueFactory/InMemoryFactoryTest.php b/tests/QueueFactory/InMemoryFactoryTest.php index 13da820a..27a6d5d9 100644 --- a/tests/QueueFactory/InMemoryFactoryTest.php +++ b/tests/QueueFactory/InMemoryFactoryTest.php @@ -1,5 +1,7 @@ factory = new InMemoryFactory(); } - public function testImplementsQueueFactory() + public function testImplementsQueueFactory(): void { $this->assertInstanceOf('Bernard\QueueFactory', $this->factory); } - /** - * @expectedException \Bernard\Exception\InvalidOperationException - */ - public function testRemoveClosesQueue() + public function testRemoveClosesQueue(): void { + $this->expectException(\Bernard\Exception\InvalidOperationException::class); + $queue = $this->factory->create('queue'); $this->assertTrue($this->factory->exists('queue')); @@ -35,7 +36,7 @@ public function testRemoveClosesQueue() $queue->peek(0, 1); } - public function testItCanCreateQueues() + public function testItCanCreateQueues(): void { $this->assertCount(0, $this->factory); diff --git a/tests/QueueFactory/PersistentFactoryTest.php b/tests/QueueFactory/PersistentFactoryTest.php index ab9594fb..b5fce6ad 100644 --- a/tests/QueueFactory/PersistentFactoryTest.php +++ b/tests/QueueFactory/PersistentFactoryTest.php @@ -1,5 +1,7 @@ connection = $this->getMockBuilder('Bernard\Driver') ->disableOriginalConstructor()->getMock(); @@ -17,12 +19,12 @@ public function setUp() $this->factory = new PersistentFactory($this->connection, $this->createMock('Bernard\Serializer')); } - public function testImplementsQueueFactory() + public function testImplementsQueueFactory(): void { $this->assertInstanceOf('Bernard\QueueFactory', $this->factory); } - public function testItSavesQueueObjects() + public function testItSavesQueueObjects(): void { $this->connection->expects($this->once())->method('createQueue') ->with($this->equalTo('send-newsletter')); @@ -32,11 +34,13 @@ public function testItSavesQueueObjects() $this->assertSame($queue, $this->factory->create('send-newsletter')); } - /** - * @expectedException \Bernard\Exception\InvalidOperationException - */ - public function testRemoveClosesQueue() + public function testRemoveClosesQueue(): void { + $this->expectException(\Bernard\Exception\InvalidOperationException::class); + + $this->connection->expects($this->once())->method('listQueues') + ->willReturn([]); + $queue = $this->factory->create('send-newsletter'); $this->assertTrue($this->factory->exists('send-newsletter')); @@ -47,25 +51,25 @@ public function testRemoveClosesQueue() $queue->peek(0, 1); } - public function testItLazyCreatesQueuesAndAttaches() + public function testItLazyCreatesQueuesAndAttaches(): void { $this->connection->expects($this->once())->method('createQueue')->with($this->equalTo('send-newsletter')); $this->assertInstanceOf('Bernard\Queue\PersistentQueue', $this->factory->create('send-newsletter')); } - public function testItsCountable() + public function testItsCountable(): void { $this->connection->expects($this->once())->method('listQueues') - ->will($this->returnValue(array('failed', 'something', 'queue-ness'))); + ->willReturn(['failed', 'something', 'queue-ness']); $this->assertCount(3, $this->factory); } - public function testItGetsAllQueues() + public function testItGetsAllQueues(): void { $this->connection->expects($this->once())->method('listQueues') - ->will($this->returnValue(array('queue1', 'queue2'))); + ->willReturn(['queue1', 'queue2']); $all = $this->factory->all(); diff --git a/tests/Router/ContainerAwareRouterTest.php b/tests/Router/ContainerAwareRouterTest.php deleted file mode 100644 index 3a6243fb..00000000 --- a/tests/Router/ContainerAwareRouterTest.php +++ /dev/null @@ -1,49 +0,0 @@ -container = new Container; - $this->container->set('my.service', function () { - return 'var_dump'; - }); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException - */ - public function testUndefinedServicesAreNotAccepted() - { - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $router = new ContainerAwareRouter($this->container); - $router->map($envelope); - } - - public function testAcceptsInConstructor() - { - $router = new ContainerAwareRouter($this->container, array('SendNewsletter' => 'my.service')); - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $this->assertSame($this->container->get('my.service'), $router->map($envelope)); - } - - public function testAcceptsContainerServiceAsReceiver() - { - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $router = new ContainerAwareRouter($this->container, array( - 'SendNewsletter' => 'my.service', - )); - - $this->assertSame($this->container->get('my.service'), $router->map($envelope)); - } -} diff --git a/tests/Router/LeagueContainerAwareRouterTest.php b/tests/Router/LeagueContainerAwareRouterTest.php deleted file mode 100644 index 30ad101d..00000000 --- a/tests/Router/LeagueContainerAwareRouterTest.php +++ /dev/null @@ -1,38 +0,0 @@ -container = new Container; - $this->container->add('my.service', function () { - return 'var_dump'; - }); - } - - /** - * @expectedException \League\Container\Exception\NotFoundException - */ - public function testUndefinedServicesAreNotAccepted() - { - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $router = new LeagueContainerAwareRouter($this->container); - $router->map($envelope); - } - - public function testAcceptsInConstructor() - { - $router = new LeagueContainerAwareRouter($this->container, array('SendNewsletter' => 'my.service')); - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $this->assertSame($this->container->get('my.service'), $router->map($envelope)); - } -} diff --git a/tests/Router/PimpleAwareRouterTest.php b/tests/Router/PimpleAwareRouterTest.php deleted file mode 100644 index 889d24ea..00000000 --- a/tests/Router/PimpleAwareRouterTest.php +++ /dev/null @@ -1,48 +0,0 @@ -pimple = new Pimple; - $this->pimple['my.service'] = $this->pimple->share(function () { - return 'var_dump'; - }); - - $this->router = new PimpleAwareRouter($this->pimple); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testUndefinedServicesAreNotAccepted() - { - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $this->router->map($envelope); - } - - public function testAcceptsInConstructor() - { - $router = new PimpleAwareRouter($this->pimple, array('SendNewsletter' => 'my.service')); - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $this->assertSame($this->pimple['my.service'], $router->map($envelope)); - } - - public function testAcceptsPimpleServiceAsReceiver() - { - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $this->router->add('SendNewsletter', 'my.service'); - - $this->assertSame($this->pimple['my.service'], $this->router->map($envelope)); - } -} diff --git a/tests/Router/SimpleRouterTest.php b/tests/Router/SimpleRouterTest.php deleted file mode 100644 index 04e677d9..00000000 --- a/tests/Router/SimpleRouterTest.php +++ /dev/null @@ -1,69 +0,0 @@ -router = new SimpleRouter(); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testThrowsExceptionWhenReceiverIsNotSupported() - { - $this->router->add('SendNewsletter', 1); - } - - /** - * @expectedException \Bernard\Exception\ReceiverNotFoundException - */ - public function testThrowsExceptionWhenNothingMatches() - { - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $this->router->map($envelope); - } - - public function testReceiversAreAddedThroughConstructor() - { - $callable = function () {}; - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $router = new SimpleRouter(array( - 'SendNewsletter' => $callable, - )); - - $this->assertSame($callable, $router->map($envelope)); - } - - /** - * @dataProvider provideCallable - */ - public function testItReturnsCallable($given, $expected) - { - $this->router->add('SendNewsletter', $given); - - $envelope = new Envelope(new PlainMessage('SendNewsletter')); - - $this->assertEquals($expected, $this->router->map($envelope)); - } - - public function provideCallable() - { - $callable = function () {}; - - return array( - array('Bernard\Tests\Fixtures\Service', array('Bernard\Tests\Fixtures\Service', 'sendNewsletter')), - array('var_dump', 'var_dump'), - array($callable, $callable), - array(new \stdClass, array(new \stdClass, 'sendNewsletter')) - ); - } -} diff --git a/tests/travis.sh b/tests/travis.sh deleted file mode 100755 index 81fcb231..00000000 --- a/tests/travis.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -if [ $TRAVIS_PHP_VERSION != "hhvm" ] && [ $TRAVIS_PHP_VERSION != "7.0" ]; then - echo "extension=mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; -fi - -if [ $TRAVIS_PHP_VERSION != "hhvm" ] && [ $TRAVIS_PHP_VERSION != "5.6" ]; then - echo "extension=mongodb.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; -fi - -if [ $TRAVIS_PHP_VERSION = "hhvm" ]; then - composer require --dev mongofill/mongofill=dev-master --no-update; - composer update --no-progress --no-plugins; -else - phpenv config-rm xdebug.ini; - composer update --no-progress --no-plugins $COMPOSER_OPTS; - echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; -fi - -mysql -e "CREATE DATABASE bernard_test;" -psql -c 'CREATE DATABASE bernard_test;' -U postgres