feat: add event dispatcher destructor and implement subscriber interface with tests (#23)
Some checks failed
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Failing after 2m2s
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Failing after 1m52s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Failing after 1m58s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 2m30s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Successful in 2m29s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in 2m24s

Reviewed-on: #23
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
This commit was merged in pull request #23.
This commit is contained in:
2025-11-30 20:28:22 +00:00
committed by Siteworx Pro Gitea
parent 721008bdfc
commit eaff49b6a4
6 changed files with 241 additions and 2 deletions

View File

@@ -45,6 +45,16 @@ class Dispatcher implements DispatcherContract, Arrayable
$this->registerListeners();
}
/**
* @throws \Throwable
*/
public function __destruct()
{
foreach ($this->pushed as $event => $payload) {
$this->dispatch($event, $payload);
}
}
/**
* Register event listeners based on the ListensFor attribute.
*

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Subscribers;
use Illuminate\Contracts\Support\Arrayable;
abstract class Subscriber implements SubscriberInterface, Arrayable
{
public function toArray(): array
{
return get_object_vars($this);
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Subscribers;
interface SubscriberInterface
{
public function handle(string $eventName, mixed $payload): mixed;
}

View File

@@ -0,0 +1,177 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\Tests\Events;
use Illuminate\Contracts\Container\BindingResolutionException;
use Siteworxpro\Tests\Unit;
class DispatcherTest extends Unit
{
/**
* @throws \Throwable
* @throws BindingResolutionException
*/
public function testRegistersListeners(): void
{
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$eventFired = false;
$dispatcher->listen('TestEvent', function ($event) use (&$eventFired) {
$this->assertEquals('TestEvent', $event);
$eventFired = true;
});
$dispatcher->dispatch('TestEvent');
$this->assertTrue($eventFired, 'The TestEvent listener was not fired.');
}
/**
* @throws BindingResolutionException
*/
public function testPushesEvents()
{
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$eventsFired = 0;
$dispatcher->listen('PushedEvent1', function ($event) use (&$eventsFired) {
$eventsFired++;
$this->assertEquals('PushedEvent1', $event);
});
$dispatcher->listen('PushedEvent2', function ($event) use (&$eventsFired) {
$eventsFired++;
$this->assertEquals('PushedEvent2', $event);
});
$dispatcher->push('PushedEvent1');
$dispatcher->push('PushedEvent2');
unset($dispatcher); // Trigger destructor
$this->assertEquals(2, $eventsFired);
}
/**
* @throws BindingResolutionException
* @throws \Throwable
*/
public function testFlushEvent(): void
{
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$eventFired = false;
$dispatcher->listen('FlushEvent', function ($event) use (&$eventFired) {
$this->assertEquals('FlushEvent', $event);
$eventFired = true;
});
$dispatcher->push('FlushEvent');
$dispatcher->flush('FlushEvent');
$this->assertTrue($eventFired, 'The FlushEvent listener was not fired.');
}
/**
* @throws BindingResolutionException
*/
public function testHasListeners(): void
{
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$this->assertFalse(
$dispatcher->hasListeners(
'NonExistentEvent'
),
'Expected no listeners for NonExistentEvent.'
);
$dispatcher->listen('ExistingEvent', function () {
// Listener logic
});
$this->assertTrue(
$dispatcher->hasListeners(
'ExistingEvent'
),
'Expected listeners for ExistingEvent.'
);
}
/**
* @throws BindingResolutionException
* @throws \Throwable
*/
public function testForgetEvent(): void
{
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$eventFired = false;
$dispatcher->listen('ForgetEvent', function ($event) use (&$eventFired) {
$this->assertEquals('ForgetEvent', $event);
$eventFired = true;
});
$dispatcher->push('ForgetEvent');
$dispatcher->forget('ForgetEvent');
unset($dispatcher); // Trigger destructor
$this->assertFalse($eventFired, 'The ForgetEvent listener was fired but should have been forgotten.');
}
/**
* @throws BindingResolutionException
*/
public function testForgetPushed()
{
$this->expectNotToPerformAssertions();
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$dispatcher->listen('EventToForget', function () {
$this->fail('The EventToForget listener was fired but should have been forgotten.');
});
$dispatcher->push('EventToForget');
$dispatcher->forgetPushed();
unset($dispatcher); // Trigger destructor
}
/**
* @throws BindingResolutionException
*/
public function testToArray(): void
{
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$dispatcher->listen('ArrayEvent', function () {
// Listener logic
});
$arrayRepresentation = $dispatcher->toArray();
$this->assertArrayHasKey('ArrayEvent', $arrayRepresentation);
}
/**
* @throws BindingResolutionException
* @throws \Throwable
*/
public function testSubscriber()
{
$subscriber = $this->getMockBuilder('Siteworxpro\App\Events\Subscribers\Subscriber')
->onlyMethods(['handle'])
->getMock();
$subscriber->expects($this->once())
->method('handle')
->with('SubscribedEvent', [])
->willReturn(null);
$dispatcher = $this->getContainer()->make('Siteworxpro\App\Events\Dispatcher');
$dispatcher->subscribe($subscriber);
$dispatcher->dispatch('SubscribedEvent');
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\Tests\ServiceProviders;
use Siteworxpro\App\Services\ServiceProviders\DispatcherServiceProvider;
class DispatcherServiceProviderTest extends AbstractServiceProvider
{
protected function getProviderClass(): string
{
return DispatcherServiceProvider::class;
}
}

View File

@@ -13,13 +13,25 @@ use Siteworxpro\App\Services\Facades\Config;
abstract class Unit extends TestCase
{
protected function getContainer(): Container
{
$container = Facade::getFacadeContainer();
if ($container === null) {
$container = new Container();
Facade::setFacadeContainer($container);
return $container;
}
return $container;
}
/**
* @throws \ReflectionException
*/
protected function setUp(): void
{
$container = new Container();
Facade::setFacadeContainer($container);
$container = $this->getContainer();
$container->bind(SWConfig::class, function () {
return SWConfig::load(__DIR__ . '/../config.php');