reactphp-kernel/tests/KernelTest.php

108 lines
3.4 KiB
PHP
Raw Permalink Normal View History

<?php
declare(strict_types=1);
namespace Blax\ReactPhpKernel\Tests;
use Blax\ReactPhpKernel\Contracts\Server;
use Blax\ReactPhpKernel\Kernel;
use Blax\ReactPhpKernel\Tests\Fixtures\RecordingServer;
use Blax\ReactPhpKernel\Tests\Fixtures\SpyLogger;
use PHPUnit\Framework\TestCase;
use React\EventLoop\LoopInterface;
use React\EventLoop\StreamSelectLoop;
final class KernelTest extends TestCase
{
public function test_uses_the_injected_loop(): void
{
$loop = new StreamSelectLoop;
$this->assertSame($loop, (new Kernel($loop))->loop());
}
public function test_boots_and_shuts_down_registered_servers_with_hooks(): void
{
$loop = new StreamSelectLoop;
$kernel = new Kernel($loop);
$server = new RecordingServer;
// Stop the kernel on the next tick so run() returns instead of blocking.
$server->onBoot = fn (LoopInterface $l) => $l->futureTick(fn () => $kernel->stop());
$bootHook = false;
$shutdownHook = false;
$kernel
->register($server)
->onBoot(function () use (&$bootHook) {
$bootHook = true;
})
->onShutdown(function () use (&$shutdownHook) {
$shutdownHook = true;
})
->run();
$this->assertTrue($server->booted, 'server should have been booted');
$this->assertTrue($server->wasShutDown, 'server should have been shut down');
$this->assertTrue($bootHook, 'onBoot hook should have fired');
$this->assertTrue($shutdownHook, 'onShutdown hook should have fired');
$this->assertFalse($kernel->isRunning());
}
public function test_stop_is_idempotent_and_safe_before_run(): void
{
$kernel = new Kernel(new StreamSelectLoop);
$kernel->stop(); // never ran — must be a harmless no-op
$kernel->stop();
$this->assertFalse($kernel->isRunning());
}
public function test_a_failing_server_shutdown_does_not_block_the_others(): void
{
$loop = new StreamSelectLoop;
$kernel = new Kernel($loop);
$throwing = new class implements Server
{
public function name(): string
{
return 'throwing';
}
public function boot(LoopInterface $loop): void {}
public function shutdown(): void
{
throw new \RuntimeException('teardown failed');
}
};
$good = new RecordingServer;
$good->onBoot = fn (LoopInterface $l) => $l->futureTick(fn () => $kernel->stop());
$kernel->register($throwing)->register($good)->run();
$this->assertTrue($good->wasShutDown, 'a throwing teardown must not stop the others');
}
public function test_emits_lifecycle_logs_to_the_injected_logger(): void
{
$loop = new StreamSelectLoop;
$logger = new SpyLogger;
$kernel = new Kernel($loop, $logger);
$server = new RecordingServer('lifecycle');
$server->onBoot = fn (LoopInterface $l) => $l->futureTick(fn () => $kernel->stop());
$kernel->register($server)->run();
$this->assertSame($logger, $kernel->logger());
$this->assertTrue($logger->hasMessageContaining('server booted'));
$this->assertTrue($logger->hasMessageContaining('running'));
$this->assertTrue($logger->hasMessageContaining('shutting down'));
}
}