Merge pull request #465 from acelaya/feature/external-event-dispatcher

Feature/external event dispatcher
This commit is contained in:
Alejandro Celaya 2019-08-12 21:03:53 +02:00 committed by GitHub
commit a5c96f41b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 3 additions and 851 deletions

View file

@ -13,7 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
#### Changed
* [#450](https://github.com/shlinkio/shlink/issues/450) Added PHP 7.4 to the build matrix, as an allowed-to-fail env.
* [#443](https://github.com/shlinkio/shlink/issues/443) Split some logic into independent modules.
* [#441](https://github.com/shlinkio/shlink/issues/441) and [#443](https://github.com/shlinkio/shlink/issues/443) Split some logic into independent modules.
* [#451](https://github.com/shlinkio/shlink/issues/451) Updated to infection 0.13.
#### Deprecated

View file

@ -34,6 +34,7 @@
"phly/phly-event-dispatcher": "^1.0",
"predis/predis": "^1.1",
"shlinkio/shlink-common": "^1.0",
"shlinkio/shlink-event-dispatcher": "^1.0",
"shlinkio/shlink-installer": "^1.2.1",
"shlinkio/shlink-ip-geolocation": "^1.0",
"symfony/console": "^4.3",
@ -76,12 +77,8 @@
"Shlinkio\\Shlink\\CLI\\": "module/CLI/src",
"Shlinkio\\Shlink\\Rest\\": "module/Rest/src",
"Shlinkio\\Shlink\\Core\\": "module/Core/src",
"Shlinkio\\Shlink\\EventDispatcher\\": "module/EventDispatcher/src",
"Shlinkio\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/src/"
},
"files": [
"module/EventDispatcher/functions/functions.php"
]
}
},
"autoload-dev": {
"psr-4": {
@ -92,7 +89,6 @@
"module/Core/test",
"module/Core/test-db"
],
"ShlinkioTest\\Shlink\\EventDispatcher\\": "module/EventDispatcher/test",
"ShlinkioTest\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/test"
}
},

View file

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2019 Alejandro Celaya
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,13 +0,0 @@
# Shlink Event Dispatcher
This library provides a PSR-14 EventDispatcher which is capable of dispatching both regular listeners and async listeners which are run using [swoole]'s task system.
Most of the elements it provides require a [PSR-11] container, and it's easy to integrate on [expressive] applications thanks to the `ConfigProvider` it includes.
## Install
Install this library using composer:
composer require shlinkio/shlink-event-dispatcher
> This library is also an expressive module which provides its own `ConfigProvider`. Add it to your configuration to get everything automatically set up.

View file

@ -1,39 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher;
use Phly\EventDispatcher as Phly;
use Psr\EventDispatcher as Psr;
use Zend\ServiceManager\Proxy\LazyServiceFactory;
return [
'events' => [
'regular' => [],
'async' => [],
],
'dependencies' => [
'factories' => [
Phly\EventDispatcher::class => Phly\EventDispatcherFactory::class,
Psr\ListenerProviderInterface::class => Listener\ListenerProviderFactory::class,
],
'aliases' => [
Psr\EventDispatcherInterface::class => Phly\EventDispatcher::class,
],
'delegators' => [
// The listener provider has to be lazy, because it uses the Swoole server to generate AsyncEventListeners
// Without making this lazy, CLI commands which depend on the EventDispatcher fail
Psr\ListenerProviderInterface::class => [
LazyServiceFactory::class,
],
],
'lazy_services' => [
'class_map' => [
Psr\ListenerProviderInterface::class => Psr\ListenerProviderInterface::class,
],
],
],
];

View file

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher;
use Swoole\Http\Server as HttpServer;
return [
'dependencies' => [
'factories' => [
Async\TaskRunner::class => Async\TaskRunnerFactory::class,
],
'delegators' => [
HttpServer::class => [
Async\TaskRunnerDelegator::class,
],
],
],
];

View file

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher;
use Swoole\Http\Server as HttpServer;
function asyncListener(HttpServer $server, string $regularListenerName): Listener\AsyncEventListener
{
return new Listener\AsyncEventListener($server, $regularListenerName);
}

View file

@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher\Async;
use Psr\Container\ContainerInterface;
interface TaskInterface
{
public function run(ContainerInterface $container): void;
public function toString(): string;
}

View file

@ -1,54 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher\Async;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Swoole\Http\Server as HttpServer;
use Throwable;
use function get_class;
use function gettype;
use function is_object;
class TaskRunner
{
/** @var LoggerInterface */
private $logger;
/** @var ContainerInterface */
private $container;
public function __construct(LoggerInterface $logger, ContainerInterface $container)
{
$this->logger = $logger;
$this->container = $container;
}
public function __invoke(HttpServer $server, int $taskId, int $fromId, $task): void
{
if (! $task instanceof TaskInterface) {
$this->logger->warning('Invalid task provided to task worker: {type}. Task ignored', [
'type' => is_object($task) ? get_class($task) : gettype($task),
]);
$server->finish('');
return;
}
$this->logger->notice('Starting work on task {taskId}: {task}', [
'taskId' => $taskId,
'task' => $task->toString(),
]);
try {
$task->run($this->container);
} catch (Throwable $e) {
$this->logger->error('Error processing task {taskId}: {e}', [
'taskId' => $taskId,
'e' => $e,
]);
} finally {
$server->finish('');
}
}
}

View file

@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher\Async;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Swoole\Http\Server as HttpServer;
class TaskRunnerDelegator
{
public function __invoke(ContainerInterface $container, $name, callable $callback): HttpServer
{
/** @var HttpServer $server */
$server = $callback();
$logger = $container->get(LoggerInterface::class);
$server->on('task', $container->get(TaskRunner::class));
$server->on('finish', function (HttpServer $server, int $taskId) use ($logger) {
$logger->notice('Task #{taskId} has finished processing', ['taskId' => $taskId]);
});
return $server;
}
}

View file

@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher\Async;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
class TaskRunnerFactory
{
public function __invoke(ContainerInterface $container): TaskRunner
{
$logger = $container->get(LoggerInterface::class);
return new TaskRunner($logger, $container);
}
}

View file

@ -1,14 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher;
use function Shlinkio\Shlink\Common\loadConfigFromGlob;
class ConfigProvider
{
public function __invoke()
{
return loadConfigFromGlob(__DIR__ . '/../config/{,*.}config.php');
}
}

View file

@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher\Listener;
use Swoole\Http\Server as HttpServer;
class AsyncEventListener
{
/** @var string */
private $regularListenerName;
/** @var HttpServer */
private $server;
public function __construct(HttpServer $server, string $regularListenerName)
{
$this->regularListenerName = $regularListenerName;
$this->server = $server;
}
public function __invoke(object $event): void
{
$this->server->task(new EventListenerTask($this->regularListenerName, $event));
}
}

View file

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher\Listener;
use Psr\Container\ContainerInterface;
use Shlinkio\Shlink\EventDispatcher\Async\TaskInterface;
use function get_class;
use function sprintf;
class EventListenerTask implements TaskInterface
{
/** @var string */
private $listenerName;
/** @var object */
private $event;
public function __construct(string $listenerName, object $event)
{
$this->listenerName = $listenerName;
$this->event = $event;
}
public function run(ContainerInterface $container): void
{
($container->get($this->listenerName))($this->event);
}
public function toString(): string
{
return sprintf('Listener -> "%s", Event -> "%s"', $this->listenerName, get_class($this->event));
}
}

View file

@ -1,52 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\EventDispatcher\Listener;
use Phly\EventDispatcher\ListenerProvider\AttachableListenerProvider;
use Psr\Container\ContainerInterface;
use Swoole\Http\Server as HttpServer;
use function Phly\EventDispatcher\lazyListener;
use function Shlinkio\Shlink\EventDispatcher\asyncListener;
class ListenerProviderFactory
{
public function __invoke(ContainerInterface $container)
{
$config = $container->has('config') ? $container->get('config') : [];
$events = $config['events'] ?? [];
$provider = new AttachableListenerProvider();
$this->registerListeners($events['regular'] ?? [], $container, $provider);
$this->registerListeners($events['async'] ?? [], $container, $provider, true);
return $provider;
}
private function registerListeners(
array $events,
ContainerInterface $container,
AttachableListenerProvider $provider,
bool $isAsync = false
): void {
if (empty($events)) {
return;
}
// Avoid registering async event listeners when the swoole server is not registered
if ($isAsync && ! $container->has(HttpServer::class)) {
return;
}
foreach ($events as $eventName => $listeners) {
foreach ($listeners as $listenerName) {
$eventListener = $isAsync
? asyncListener($container->get(HttpServer::class), $listenerName)
: lazyListener($container, $listenerName);
$provider->listen($eventName, $eventListener);
}
}
}
}

View file

@ -1,46 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\EventDispatcher\Async;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunner;
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunnerDelegator;
use Swoole\Http\Server as HttpServer;
class TaskRunnerDelegatorTest extends TestCase
{
/** @var TaskRunnerDelegator */
private $delegator;
public function setUp(): void
{
$this->delegator = new TaskRunnerDelegator();
}
/** @test */
public function serverIsFetchedFromCallbackAndDecorated(): void
{
$server = $this->createMock(HttpServer::class);
$server
->expects($this->exactly(2))
->method('on');
$callback = function () use ($server) {
return $server;
};
$container = $this->prophesize(ContainerInterface::class);
$getTaskRunner = $container->get(TaskRunner::class)->willReturn($this->prophesize(TaskRunner::class)->reveal());
$getLogger = $container->get(LoggerInterface::class)->willReturn(
$this->prophesize(LoggerInterface::class)->reveal()
);
$result = ($this->delegator)($container->reveal(), '', $callback);
$this->assertSame($server, $result);
$getTaskRunner->shouldHaveBeenCalledOnce();
$getLogger->shouldHaveBeenCalledOnce();
}
}

View file

@ -1,48 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\EventDispatcher\Async;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use ReflectionObject;
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunner;
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunnerFactory;
class TaskRunnerFactoryTest extends TestCase
{
/** @var TaskRunnerFactory */
private $factory;
public function setUp(): void
{
$this->factory = new TaskRunnerFactory();
}
/** @test */
public function properlyCreatesService(): void
{
$loggerMock = $this->prophesize(LoggerInterface::class);
$logger = $loggerMock->reveal();
$containerMock = $this->prophesize(ContainerInterface::class);
$getLogger = $containerMock->get(LoggerInterface::class)->willReturn($logger);
$container = $containerMock->reveal();
$taskRunner = ($this->factory)($container, '');
$loggerProp = $this->getPropertyFromTaskRunner($taskRunner, 'logger');
$containerProp = $this->getPropertyFromTaskRunner($taskRunner, 'container');
$this->assertSame($container, $containerProp);
$this->assertSame($logger, $loggerProp);
$getLogger->shouldHaveBeenCalledOnce();
}
private function getPropertyFromTaskRunner(TaskRunner $taskRunner, string $propertyName)
{
$ref = new ReflectionObject($taskRunner);
$prop = $ref->getProperty($propertyName);
$prop->setAccessible(true);
return $prop->getValue($taskRunner);
}
}

View file

@ -1,107 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\EventDispatcher\Async;
use Exception;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\EventDispatcher\Async\TaskInterface;
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunner;
use Swoole\Http\Server as HttpServer;
class TaskRunnerTest extends TestCase
{
/** @var TaskRunner */
private $taskRunner;
/** @var ObjectProphecy */
private $logger;
/** @var ObjectProphecy */
private $container;
/** @var HttpServer */
private $server;
/** @var ObjectProphecy */
private $task;
public function setUp(): void
{
$this->logger = $this->prophesize(LoggerInterface::class);
$this->container = $this->prophesize(ContainerInterface::class);
$this->task = $this->prophesize(TaskInterface::class);
$this->server = $this->createMock(HttpServer::class);
$this->server
->expects($this->once())
->method('finish')
->with('');
$this->taskRunner = new TaskRunner($this->logger->reveal(), $this->container->reveal());
}
/** @test */
public function warningIsLoggedWhenProvidedTaskIsInvalid(): void
{
$logWarning = $this->logger->warning('Invalid task provided to task worker: {type}. Task ignored', [
'type' => 'string',
]);
$logInfo = $this->logger->info(Argument::cetera());
$logError = $this->logger->error(Argument::cetera());
($this->taskRunner)($this->server, 1, 1, 'invalid_task');
$logWarning->shouldHaveBeenCalledOnce();
$logInfo->shouldNotHaveBeenCalled();
$logError->shouldNotHaveBeenCalled();
}
/** @test */
public function properTasksAreRun(): void
{
$logWarning = $this->logger->warning(Argument::cetera());
$logInfo = $this->logger->notice('Starting work on task {taskId}: {task}', [
'taskId' => 1,
'task' => 'The task',
]);
$logError = $this->logger->error(Argument::cetera());
$taskToString = $this->task->toString()->willReturn('The task');
$taskRun = $this->task->run($this->container->reveal())->will(function () {
});
($this->taskRunner)($this->server, 1, 1, $this->task->reveal());
$logWarning->shouldNotHaveBeenCalled();
$logInfo->shouldHaveBeenCalledOnce();
$logError->shouldNotHaveBeenCalled();
$taskToString->shouldHaveBeenCalledOnce();
$taskRun->shouldHaveBeenCalledOnce();
}
/** @test */
public function errorIsLoggedWhenTasksFail(): void
{
$e = new Exception('Error');
$logWarning = $this->logger->warning(Argument::cetera());
$logInfo = $this->logger->notice('Starting work on task {taskId}: {task}', [
'taskId' => 1,
'task' => 'The task',
]);
$logError = $this->logger->error('Error processing task {taskId}: {e}', [
'taskId' => 1,
'e' => $e,
]);
$taskToString = $this->task->toString()->willReturn('The task');
$taskRun = $this->task->run($this->container->reveal())->willThrow($e);
($this->taskRunner)($this->server, 1, 1, $this->task->reveal());
$logWarning->shouldNotHaveBeenCalled();
$logInfo->shouldHaveBeenCalledOnce();
$logError->shouldHaveBeenCalledOnce();
$taskToString->shouldHaveBeenCalledOnce();
$taskRun->shouldHaveBeenCalledOnce();
}
}

View file

@ -1,27 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\EventDispatcher;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\EventDispatcher\ConfigProvider;
class ConfigProviderTest extends TestCase
{
/** @var ConfigProvider */
private $configProvider;
public function setUp(): void
{
$this->configProvider = new ConfigProvider();
}
/** @test */
public function configIsReturned(): void
{
$config = $this->configProvider->__invoke();
$this->assertArrayHasKey('dependencies', $config);
$this->assertArrayHasKey('events', $config);
}
}

View file

@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\EventDispatcher\Listener;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\EventDispatcher\Listener\AsyncEventListener;
use Shlinkio\Shlink\EventDispatcher\Listener\EventListenerTask;
use stdClass;
use Swoole\Http\Server as HttpServer;
class AsyncEventListenerTest extends TestCase
{
/** @var AsyncEventListener */
private $eventListener;
/** @var HttpServer */
private $server;
/** @var string */
private $regularListenerName;
public function setUp(): void
{
$this->regularListenerName = 'the_regular_listener';
$this->server = $this->createMock(HttpServer::class);
$this->eventListener = new AsyncEventListener($this->server, $this->regularListenerName);
}
/** @test */
public function enqueuesTaskWhenInvoked(): void
{
$event = new stdClass();
$this->server
->expects($this->once())
->method('task')
->with(new EventListenerTask($this->regularListenerName, $event));
($this->eventListener)($event);
}
}

View file

@ -1,58 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\EventDispatcher\Listener;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Shlinkio\Shlink\EventDispatcher\Listener\EventListenerTask;
use stdClass;
use function get_class;
use function sprintf;
class EventListenerTaskTest extends TestCase
{
/** @var EventListenerTask */
private $task;
/** @var object */
private $event;
/** @var string */
private $listenerName;
public function setUp(): void
{
$this->event = new stdClass();
$this->listenerName = 'the_listener';
$this->task = new EventListenerTask($this->listenerName, $this->event);
}
/** @test */
public function toStringReturnsTheStringRepresentation(): void
{
$this->assertEquals(
sprintf('Listener -> "%s", Event -> "%s"', $this->listenerName, get_class($this->event)),
$this->task->toString()
);
}
/** @test */
public function runInvokesContainerAndListenerWithEvent(): void
{
$invoked = false;
$container = $this->prophesize(ContainerInterface::class);
$listener = function (object $event) use (&$invoked) {
$invoked = true;
Assert::assertSame($event, $this->event);
};
$getListener = $container->get($this->listenerName)->willReturn($listener);
$this->task->run($container->reveal());
$this->assertTrue($invoked);
$getListener->shouldHaveBeenCalledOnce();
}
}

View file

@ -1,176 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\EventDispatcher\Listener;
use Phly\EventDispatcher\ListenerProvider\AttachableListenerProvider;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use ReflectionObject;
use Shlinkio\Shlink\EventDispatcher\Listener\ListenerProviderFactory;
use Swoole\Http\Server as HttpServer;
use function Phly\EventDispatcher\lazyListener;
use function Shlinkio\Shlink\EventDispatcher\asyncListener;
class ListenerProviderFactoryTest extends TestCase
{
/** @var ListenerProviderFactory */
private $factory;
public function setUp(): void
{
$this->factory = new ListenerProviderFactory();
}
/**
* @test
* @dataProvider provideContainersWithoutEvents
*/
public function noListenersAreAttachedWhenNoConfigOrEventsAreRegistered(ContainerInterface $container): void
{
$provider = ($this->factory)($container, '');
$listeners = $this->getListenersFromProvider($provider);
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
$this->assertEmpty($listeners);
}
public function provideContainersWithoutEvents(): iterable
{
yield 'no config' => [(function () {
$container = $this->prophesize(ContainerInterface::class);
$container->has('config')->willReturn(false);
return $container->reveal();
})()];
yield 'no events' => [(function () {
$container = $this->prophesize(ContainerInterface::class);
$container->has('config')->willReturn(true);
$container->get('config')->willReturn([]);
return $container->reveal();
})()];
}
/** @test */
public function configuredRegularEventsAreProperlyAttached(): void
{
$containerMock = $this->prophesize(ContainerInterface::class);
$containerMock->has('config')->willReturn(true);
$containerMock->get('config')->willReturn([
'events' => [
'regular' => [
'foo' => [
'bar',
'baz',
],
'something' => [
'some_listener',
'another_listener',
'foobar',
],
],
],
]);
$container = $containerMock->reveal();
$provider = ($this->factory)($container, '');
$listeners = $this->getListenersFromProvider($provider);
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
$this->assertEquals([
'foo' => [
lazyListener($container, 'bar'),
lazyListener($container, 'baz'),
],
'something' => [
lazyListener($container, 'some_listener'),
lazyListener($container, 'another_listener'),
lazyListener($container, 'foobar'),
],
], $listeners);
}
/** @test */
public function configuredAsyncEventsAreProperlyAttached(): void
{
$server = $this->createMock(HttpServer::class); // Some weird errors are thrown if prophesize is used
$containerMock = $this->prophesize(ContainerInterface::class);
$containerMock->has('config')->willReturn(true);
$containerMock->get('config')->willReturn([
'events' => [
'async' => [
'foo' => [
'bar',
'baz',
],
'something' => [
'some_listener',
'another_listener',
'foobar',
],
],
],
]);
$containerMock->has(HttpServer::class)->willReturn(true);
$containerMock->get(HttpServer::class)->willReturn($server);
$container = $containerMock->reveal();
$provider = ($this->factory)($container, '');
$listeners = $this->getListenersFromProvider($provider);
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
$this->assertEquals([
'foo' => [
asyncListener($server, 'bar'),
asyncListener($server, 'baz'),
],
'something' => [
asyncListener($server, 'some_listener'),
asyncListener($server, 'another_listener'),
asyncListener($server, 'foobar'),
],
], $listeners);
}
/** @test */
public function ignoresAsyncEventsWhenServerIsNotRegistered(): void
{
$containerMock = $this->prophesize(ContainerInterface::class);
$containerMock->has('config')->willReturn(true);
$containerMock->get('config')->willReturn([
'events' => [
'async' => [
'foo' => [
'bar',
'baz',
],
'something' => [
'some_listener',
'another_listener',
'foobar',
],
],
],
]);
$containerMock->has(HttpServer::class)->willReturn(false);
$container = $containerMock->reveal();
$provider = ($this->factory)($container, '');
$listeners = $this->getListenersFromProvider($provider);
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
$this->assertEmpty($listeners);
}
private function getListenersFromProvider($provider): array
{
$ref = new ReflectionObject($provider);
$prop = $ref->getProperty('listeners');
$prop->setAccessible(true);
return $prop->getValue($provider);
}
}

View file

@ -15,9 +15,6 @@
<testsuite name="CLI">
<directory>./module/CLI/test</directory>
</testsuite>
<testsuite name="EventDispatcher">
<directory>./module/EventDispatcher/test</directory>
</testsuite>
<testsuite name="PreviewGenerator">
<directory>./module/PreviewGenerator/test</directory>
</testsuite>