From ed5a9c6c129bcc014ce11d0f548e618133cb1974 Mon Sep 17 00:00:00 2001 From: sunxyw <31698606+sunxyw@users.noreply.github.com> Date: Sun, 25 Dec 2022 19:29:44 +0800 Subject: [PATCH] add HttpEventListener test (#200) --- composer.json | 2 +- phpstan.neon | 3 + tests/TestCase.php | 16 +-- tests/Trait/HasLogger.php | 10 +- .../Event/Listener/HttpEventListenerTest.php | 112 ++++++++++++++++++ .../ZM/Event/Listener/SignalListenerTest.php | 37 ++++++ tests/ZMResultPrinter.php | 2 +- 7 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 tests/ZM/Event/Listener/HttpEventListenerTest.php create mode 100644 tests/ZM/Event/Listener/SignalListenerTest.php diff --git a/composer.json b/composer.json index dc8330f8..0373fe34 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "jangregor/phpstan-prophecy": "^1.0", "jetbrains/phpstorm-attributes": "^1.0", "mikey179/vfsstream": "^1.6", - "phpspec/prophecy": "1.x-dev", + "phpspec/prophecy-phpunit": "^2.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", diff --git a/phpstan.neon b/phpstan.neon index bc5de909..5e555e8a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,6 +10,9 @@ parameters: - '#Unsafe usage of new static#' - '#Call to method initTableList\(\) of deprecated class ZM\\DB\\DB#' - '#class Fiber#' + - # Ignore Prophesize deprecation bug: https://github.com/phpstan/phpstan-deprecation-rules/issues/76 + message: '#^Call to deprecated method prophesize\(\) of class Tests\\TestCase#' + path: tests dynamicConstantNames: - SWOOLE_VERSION - ZM_TEST_LOG_DEBUG diff --git a/tests/TestCase.php b/tests/TestCase.php index 7185cc2c..0fe501a5 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,24 +4,12 @@ declare(strict_types=1); namespace Tests; -use Prophecy\Prophet; +use Prophecy\PhpUnit\ProphecyTrait; /** * @internal */ class TestCase extends \PHPUnit\Framework\TestCase { - protected Prophet $prophet; - - protected function setUp(): void - { - parent::setUp(); - $this->prophet = new Prophet(); - } - - protected function tearDown(): void - { - parent::tearDown(); - $this->prophet->checkPredictions(); - } + use ProphecyTrait; } diff --git a/tests/Trait/HasLogger.php b/tests/Trait/HasLogger.php index 8d4d6774..6fa32f12 100644 --- a/tests/Trait/HasLogger.php +++ b/tests/Trait/HasLogger.php @@ -5,13 +5,11 @@ declare(strict_types=1); namespace Tests\Trait; use Prophecy\Argument; -use Prophecy\Prophet; use Psr\Log\AbstractLogger; use Psr\Log\LogLevel; /** * 模拟 Logger 行为 - * @property Prophet $prophet */ trait HasLogger { @@ -37,7 +35,7 @@ trait HasLogger private function startMockLogger(): void { - $logger = $this->prophet->prophesize(AbstractLogger::class); + $logger = $this->prophesize(AbstractLogger::class); $levels = [ LogLevel::EMERGENCY, LogLevel::ALERT, @@ -50,9 +48,11 @@ trait HasLogger ]; $log_it = fn (...$args) => $this->mockLog(...$args); foreach ($levels as $level) { - $logger->{$level}(Argument::type('string'), Argument::any())->will(fn ($args) => $log_it($level, ...$args)); + $logger->{$level}(Argument::type('string'), Argument::any()) + ->will(fn ($args) => $log_it($level, ...$args)); } - $logger->log(Argument::in($levels), Argument::type('string'), Argument::any())->will(fn ($args) => $log_it(...$args)); + $logger->log(Argument::in($levels), Argument::type('string'), Argument::any()) + ->will(fn ($args) => $log_it(...$args)); ob_logger_register($logger->reveal()); } } diff --git a/tests/ZM/Event/Listener/HttpEventListenerTest.php b/tests/ZM/Event/Listener/HttpEventListenerTest.php new file mode 100644 index 00000000..6963c2ea --- /dev/null +++ b/tests/ZM/Event/Listener/HttpEventListenerTest.php @@ -0,0 +1,112 @@ +addRoute($this->mockHandler(false), 'fakeHandler'); + + $event = $this->mockRequestEvent(new ServerRequest('DELETE', '/test/get'), true); + HttpEventListener::getInstance()->onRequest999($event); + } + + public function testHandleNotFoundRoute(): void + { + $this->addRoute($this->mockHandler(false), 'fakeHandler'); + + $event = $this->mockRequestEvent(new ServerRequest('GET', '/test/not-found'), false); + HttpEventListener::getInstance()->onRequest999($event); + } + + public function testHandleFoundRoute(): void + { + $this->addRoute('', [$this->mockHandler(true), 'fakeHandler']); + + $event = $this->mockRequestEvent(new ServerRequest('GET', '/test/get'), true); + HttpEventListener::getInstance()->onRequest999($event); + } + + public function testHandleFoundRouteWithException(): void + { + $this->addRoute('', [$this->mockHandler(true, fn () => null), 'fakeHandler']); + + $event = $this->mockRequestEvent(new ServerRequest('GET', '/test/get'), true); + HttpEventListener::getInstance()->onRequest999($event); + } + + public function testHandleStaticFile(): void + { + $this->setUpVfs('static', [ + 'test.txt' => 'Hello, world!', + ]); + $event = $this->mockRequestEvent(new ServerRequest('GET', '/test.txt'), true); + + $old_conf = config('global.file_server.document_root'); + config(['global.file_server.document_root' => $this->vfs->url()]); + HttpEventListener::getInstance()->onRequest1($event); + config(['global.file_server.document_root' => $old_conf]); + } + + private function addRoute($class, $method): void + { + HttpUtil::getRouteCollection()->remove('test.get'); + $route = new Route('/test/get', ['_class' => $class, '_method' => $method], methods: ['GET']); + HttpUtil::getRouteCollection()->add('test.get', $route); + } + + private function mockRequestEvent(ServerRequest $request, bool $should_have_response): \HttpRequestEvent + { + $event = $this->prophesize(\HttpRequestEvent::class); + $event->getRequest()->willReturn($request); + $event->getResponse()->willReturn(null); + if ($should_have_response) { + $event->withResponse(Argument::type(ResponseInterface::class)) + ->will(function ($args) use ($event) { + $event->getResponse()->willReturn($args[0]); + return $event->reveal(); + }) + ->shouldBeCalledOnce(); + } else { + $event->withResponse(Argument::type(ResponseInterface::class))->shouldNotBeCalled(); + } + return $event->reveal(); + } + + private function mockHandler(bool $should_be_called, callable $callback = null): self + { + $handler = $this->prophesize(self::class); + if ($should_be_called) { + $handler->fakeHandler( + Argument::type('array'), + Argument::type(ServerRequest::class), + Argument::type(\HttpRequestEvent::class) + )->will(fn () => $callback ? $callback() : 'OK!')->shouldBeCalledOnce(); + } else { + $handler->fakeHandler(Argument::cetera())->shouldNotBeCalled(); + } + return $handler->reveal(); + } +} diff --git a/tests/ZM/Event/Listener/SignalListenerTest.php b/tests/ZM/Event/Listener/SignalListenerTest.php new file mode 100644 index 00000000..d20fdc70 --- /dev/null +++ b/tests/ZM/Event/Listener/SignalListenerTest.php @@ -0,0 +1,37 @@ +startMockLogger(); + } + + /** + * @requires extension pcntl + */ + public function testListenWorkerSignal(): void + { + $l = new SignalListener(); + $l->signalWorker(); + // 检查信号处理器是否被设置 + /** @noinspection PhpComposerExtensionStubsInspection */ + $h = pcntl_signal_get_handler(SIGINT); + $this->assertIsCallable($h); + $this->assertEquals([$l, 'onWorkerInt'], $h); + } +} diff --git a/tests/ZMResultPrinter.php b/tests/ZMResultPrinter.php index a00410f8..dbfa7687 100644 --- a/tests/ZMResultPrinter.php +++ b/tests/ZMResultPrinter.php @@ -190,7 +190,7 @@ class ZMResultPrinter extends CliTestDoxPrinter $out = ''; if ($message) { - $out .= $this->prefixLines($prefix['message'], strtolower($message) . PHP_EOL) . PHP_EOL; + $out .= $this->prefixLines($prefix['message'], $message . PHP_EOL) . PHP_EOL; } if ($diff) {