diff --git a/.gitattributes b/.gitattributes
index a0650eb..9426ba0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,7 +2,7 @@
/.* export-ignore
/tests export-ignore
-/phpunit.xml* export-ignore
+/testo.php export-ignore
/psalm.* export-ignore
/psalm-baseline.xml export-ignore
/infection.* export-ignore
diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
deleted file mode 100644
index 3b86822..0000000
--- a/.github/workflows/phpunit.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-on:
- pull_request: null
- push:
- branches:
- - '*.*'
-
-name: phpunit
-
-jobs:
- phpunit:
- uses: spiral/gh-actions/.github/workflows/phpunit.yml@master
- with:
- os: >-
- ['ubuntu-latest']
- stability: >-
- ['prefer-lowest', 'prefer-stable']
diff --git a/.github/workflows/testo.yml b/.github/workflows/testo.yml
new file mode 100644
index 0000000..6fb3b4a
--- /dev/null
+++ b/.github/workflows/testo.yml
@@ -0,0 +1,16 @@
+on:
+ pull_request: null
+ push:
+ branches:
+ - 4.x
+
+name: testo
+
+jobs:
+ testo:
+ uses: spiral/gh-actions/.github/workflows/testo.yml@master
+ with:
+ os: >-
+ ["ubuntu-latest"]
+ dependencies: >-
+ ["lowest","highest"]
diff --git a/README.md b/README.md
index 8cb8559..fc95925 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ vendor/bin/rr get
Ensure that your server is configured with the following PHP versions and extensions:
-- PHP >=8.1
+- PHP >=8.2
- ext-protobuf: This extension is optional but **highly recommended for installation**.
Without it, performance may be up to 50% lower.
- RoadRunner ^3.0
diff --git a/composer.json b/composer.json
index 4bc0db2..cfcd3bb 100644
--- a/composer.json
+++ b/composer.json
@@ -37,7 +37,7 @@
"chat": "https://discord.gg/V6EK4he"
},
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
"ext-json": "*",
"psr/http-factory": "^1.0.1",
"psr/http-message": "^1.0.1 || ^2.0",
@@ -48,12 +48,14 @@
},
"require-dev": {
"jetbrains/phpstorm-attributes": "^1.0",
+ "mockery/mockery": "^1.6.12",
"nyholm/psr7": "^1.3",
- "phpunit/phpunit": "^10.5",
"spiral/code-style": "^2.3.1",
"spiral/dumper": "^3.3",
"symfony/process": "^6.2 || ^7.0",
- "vimeo/psalm": "^6.13"
+ "testo/bridge-mockery": "^0.1.2",
+ "testo/testo": "^0.10.55",
+ "vimeo/psalm": "^6 || ^7"
},
"autoload": {
"psr-4": {
@@ -91,10 +93,12 @@
"psalm": "psalm",
"psalm:baseline": "psalm --set-baseline=psalm-baseline.xml",
"psalm:ci": "psalm --output-format=github --shepherd --show-info=false --stats --threads=4",
- "test": "phpunit --color=always --testdox",
+ "test": "testo",
"test:cc": [
"@putenv XDEBUG_MODE=coverage",
- "phpunit --coverage-clover=runtime/phpunit/logs/clover.xml --color=always"
- ]
+ "testo --coverage --coverage-clover=runtime/logs/clover.xml"
+ ],
+ "test:unit": "testo --suite=Unit",
+ "test:feature": "testo --suite=Feature"
}
}
diff --git a/phpunit.xml b/phpunit.xml
deleted file mode 100644
index 43def94..0000000
--- a/phpunit.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
- tests
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- src
-
-
- tests
-
-
-
diff --git a/testo.php b/testo.php
new file mode 100644
index 0000000..5c390be
--- /dev/null
+++ b/testo.php
@@ -0,0 +1,24 @@
+respond(new Payload('Hello, World!'));
\usleep(100_000);
- self::assertSame('Hello, World!', \trim(ServerRunner::getBuffer()));
+ Assert::same(\trim(ServerRunner::getBuffer()), 'Hello, World!');
}
/**
@@ -50,7 +55,7 @@ public function testStreamResponseWithMultipleFrames(): void
);
\usleep(100_000);
- self::assertSame(\implode("\n", $chunks), \trim(ServerRunner::getBuffer()));
+ Assert::same(\trim(ServerRunner::getBuffer()), \implode("\n", $chunks));
}
public function testStopStreamResponse(): void
@@ -78,14 +83,14 @@ public function testStopStreamResponse(): void
\usleep(100_000);
- self::assertSame(\implode("\n", ['Hel', 'lo,']), \trim(ServerRunner::getBuffer()));
+ Assert::same(\trim(ServerRunner::getBuffer()), \implode("\n", ['Hel', 'lo,']));
}
public function testSend1xxWithBody(): void
{
$httpWorker = $this->makeHttpWorker();
- $this->expectExceptionMessage('Unable to send a body with informational status code');
+ Expect::exception(\Throwable::class)->withMessageContaining('Unable to send a body with informational status code');
$httpWorker->respond(
103,
@@ -114,7 +119,7 @@ public function testExceptionInGenerator(): void
\usleep(100_000);
- self::assertSame(\implode("\n", ['Hel', 'lo,']), \trim(ServerRunner::getBuffer()));
+ Assert::same(\trim(ServerRunner::getBuffer()), \implode("\n", ['Hel', 'lo,']));
}
/**
@@ -136,30 +141,30 @@ public function testStopStreamAfterStreamEnd(): void
})(),
);
- $this->assertFalse($this->getWorker()->hasPayload(\Spiral\RoadRunner\Message\Command\StreamStop::class));
+ Assert::false($this->getWorker()->hasPayload(\Spiral\RoadRunner\Message\Command\StreamStop::class));
$this->sendCommand(new StreamStop());
\usleep(200_000);
- $this->assertSame(\implode("\n", ['Hello', 'World!']), \trim(ServerRunner::getBuffer()));
- $this->assertTrue($this->getWorker()->hasPayload(\Spiral\RoadRunner\Message\Command\StreamStop::class));
+ Assert::same(\trim(ServerRunner::getBuffer()), \implode("\n", ['Hello', 'World!']));
+ Assert::true($this->getWorker()->hasPayload(\Spiral\RoadRunner\Message\Command\StreamStop::class));
$this->getWorker()->getPayload(\Spiral\RoadRunner\Message\Command\StreamStop::class);
$this->getWorker()->getPayload(GetProcessId::class);
- $this->assertFalse($this->getWorker()->hasPayload());
+ Assert::false($this->getWorker()->hasPayload());
}
+ #[BeforeTest]
protected function setUp(): void
{
- parent::setUp();
ServerRunner::start();
ServerRunner::getBuffer();
}
+ #[AfterTest]
protected function tearDown(): void
{
unset($this->relay, $this->worker);
ServerRunner::stop();
- parent::tearDown();
}
private function getRelay(): SocketRelay
@@ -177,7 +182,7 @@ private function makeHttpWorker(): HttpWorker
return new HttpWorker($this->getWorker());
}
- private function sendCommand(BaseCommand $command)
+ private function sendCommand(BaseCommand $command): void
{
$this->getRelay()->send($command->getRequestFrame());
\usleep(500_000);
diff --git a/tests/Unit/GlobalStateTest.php b/tests/Unit/GlobalStateTest.php
index 5ca36d5..e3d4941 100644
--- a/tests/Unit/GlobalStateTest.php
+++ b/tests/Unit/GlobalStateTest.php
@@ -4,12 +4,14 @@
namespace Spiral\RoadRunner\Tests\Http\Unit;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
use Spiral\RoadRunner\Http\GlobalState;
use Spiral\RoadRunner\Http\Request;
+use Testo\Assert;
+use Testo\Data\DataProvider;
+use Testo\Test;
-final class GlobalStateTest extends TestCase
+#[Test]
+final class GlobalStateTest
{
/**
* @return iterable}>
@@ -60,12 +62,11 @@ public function testEnrichServerVars(Request $request, array $expected): void
$server = GlobalState::enrichServerVars($request);
- $this->assertArrayHasKey('REQUEST_TIME', $server);
- $this->assertArrayHasKey('REQUEST_TIME_FLOAT', $server);
+ Assert::array($server)->hasKeys('REQUEST_TIME', 'REQUEST_TIME_FLOAT');
unset($server['REQUEST_TIME']);
unset($server['REQUEST_TIME_FLOAT']);
- $this->assertEquals($server, $expected);
+ Assert::equals($server, $expected);
}
}
diff --git a/tests/Unit/HttpWorkerTest.php b/tests/Unit/HttpWorkerTest.php
index cbbb603..e951d2e 100644
--- a/tests/Unit/HttpWorkerTest.php
+++ b/tests/Unit/HttpWorkerTest.php
@@ -4,8 +4,6 @@
namespace Spiral\RoadRunner\Tests\Http\Unit;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\TestCase;
use RoadRunner\HTTP\DTO\V1\HeaderValue;
use RoadRunner\HTTP\DTO\V1\Response;
use Spiral\Goridge\Frame;
@@ -13,8 +11,14 @@
use Spiral\RoadRunner\Http\Request;
use Spiral\RoadRunner\Payload;
use Spiral\RoadRunner\WorkerInterface;
-
-final class HttpWorkerTest extends TestCase
+use Testo\Assert;
+use Testo\Data\DataProvider;
+use Testo\Expect;
+use Testo\Lifecycle\AfterTest;
+use Testo\Test;
+
+#[Test]
+final class HttpWorkerTest
{
private const REQUIRED_PAYLOAD_DATA = [
'rawQuery' => 'first=value&arr[]=foo+bar&arr[]=baz',
@@ -198,14 +202,12 @@ public static function headersDataProvider(): \Traversable
#[DataProvider('requestDataProvider')]
public function testWaitRequestFromArray(array $header, array $expected): void
{
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn(new Payload('foo', \json_encode($header)));
+ $worker = \Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing();
+ $worker->shouldReceive('waitPayload')->once()->andReturn(new Payload('foo', \json_encode($header)));
$worker = new HttpWorker($worker);
- $this->assertEquals(new Request(...$expected), $worker->waitRequest());
+ Assert::equals($worker->waitRequest(), new Request(...$expected));
}
#[DataProvider('requestDataProvider')]
@@ -213,50 +215,43 @@ public function testWaitRequestFromProto(array $header, array $expected): void
{
$request = self::createProtoRequest($header);
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn(new Payload('foo', $request->serializeToString()));
+ $worker = \Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing();
+ $worker->shouldReceive('waitPayload')->once()->andReturn(new Payload('foo', $request->serializeToString()));
$worker = new HttpWorker($worker);
- $this->assertEquals(new Request(...$expected), $worker->waitRequest());
+ Assert::equals($worker->waitRequest(), new Request(...$expected));
}
#[DataProvider('emptyRequestDataProvider')]
public function testWaitRequestWithEmptyData(?Payload $payload): void
{
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn($payload);
+ $worker = \Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing();
+ $worker->shouldReceive('waitPayload')->once()->andReturn($payload);
$worker = new HttpWorker($worker);
- $this->assertEquals(null, $worker->waitRequest());
+ Assert::equals($worker->waitRequest(), null);
}
public function testEmptyBodyShouldBeConvertedIntoEmptyArrayWithParsedTrue(): void
{
$request = self::createProtoRequest(\array_merge(self::REQUIRED_PAYLOAD_DATA, ['parsed' => true]));
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('waitPayload')
- ->willReturn(new Payload('', $request->serializeToString()));
+ $worker = \Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing();
+ $worker->shouldReceive('waitPayload')->once()->andReturn(new Payload('', $request->serializeToString()));
$worker = new HttpWorker($worker);
$request = $worker->waitRequest();
- $this->assertSame([], $request->getParsedBody());
+ Assert::same($request->getParsedBody(), []);
}
public function testRespondUnableToSendBodyWithInfoStatusException(): void
{
- $worker = new HttpWorker($this->createMock(WorkerInterface::class));
+ $worker = new HttpWorker(\Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing());
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Unable to send a body with informational status code.');
+ Expect::exception(\InvalidArgumentException::class)->withMessageContaining('Unable to send a body with informational status code.');
$worker->respond(100, 'foo');
}
@@ -267,10 +262,8 @@ public function testRespondWithProtoCodec(): void
'headers' => ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])],
]);
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('respond')
- ->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO);
+ $worker = \Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing();
+ $worker->shouldReceive('respond')->once()->with(\Mockery::isEqual(new Payload('foo', $expectedHeader->serializeToString())), Frame::CODEC_PROTO, \Mockery::andAnyOtherArgs());
(new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO);
$worker = new HttpWorker($worker);
@@ -283,10 +276,8 @@ public function testRespondWithProtoCodecWithHeaders(array $headers, array $expe
{
$expectedHeader = new Response(['status' => 200, 'headers' => $expected]);
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('respond')
- ->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO);
+ $worker = \Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing();
+ $worker->shouldReceive('respond')->once()->with(\Mockery::isEqual(new Payload('foo', $expectedHeader->serializeToString())), Frame::CODEC_PROTO, \Mockery::andAnyOtherArgs());
(new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO);
$worker = new HttpWorker($worker);
@@ -296,13 +287,11 @@ public function testRespondWithProtoCodecWithHeaders(array $headers, array $expe
public function testRespondWithJsonCodec(): void
{
- $worker = $this->createMock(WorkerInterface::class);
- $worker->expects($this->once())
- ->method('respond')
- ->with(new Payload('foo', \json_encode([
- 'status' => 200,
- 'headers' => ['Content-Type' => ['application/x-www-form-urlencoded']],
- ])), Frame::CODEC_JSON);
+ $worker = \Mockery::mock(WorkerInterface::class)->shouldIgnoreMissing();
+ $worker->shouldReceive('respond')->once()->with(\Mockery::isEqual(new Payload('foo', \json_encode([
+ 'status' => 200,
+ 'headers' => ['Content-Type' => ['application/x-www-form-urlencoded']],
+ ]))), Frame::CODEC_JSON, \Mockery::andAnyOtherArgs());
(new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_JSON);
$worker = new HttpWorker($worker);
@@ -310,6 +299,7 @@ public function testRespondWithJsonCodec(): void
$worker->respond(200, 'foo', ['Content-Type' => ['application/x-www-form-urlencoded']]);
}
+ #[AfterTest]
protected function tearDown(): void
{
(new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(null);
diff --git a/tests/Unit/PSR7WorkerTest.php b/tests/Unit/PSR7WorkerTest.php
index 52f8f7d..c0a60f4 100644
--- a/tests/Unit/PSR7WorkerTest.php
+++ b/tests/Unit/PSR7WorkerTest.php
@@ -5,19 +5,20 @@
namespace Spiral\RoadRunner\Tests\Http\Unit;
use Nyholm\Psr7\Factory\Psr17Factory;
-use PHPUnit\Framework\Attributes\CoversClass;
-use PHPUnit\Framework\Attributes\RunClassInSeparateProcess;
-use PHPUnit\Framework\TestCase;
use Spiral\Goridge\Frame;
use Spiral\RoadRunner\Http\GlobalState;
use Spiral\RoadRunner\Http\PSR7Worker;
use Spiral\RoadRunner\Tests\Http\Unit\Stub\TestRelay;
use Spiral\RoadRunner\Worker;
+use Testo\Assert;
+use Testo\Codecov\Covers;
+use Testo\Lifecycle\AfterTest;
+use Testo\Test;
-#[CoversClass(PSR7Worker::class)]
-#[CoversClass(GlobalState::class)]
-#[RunClassInSeparateProcess]
-final class PSR7WorkerTest extends TestCase
+#[Covers(PSR7Worker::class)]
+#[Covers(GlobalState::class)]
+#[Test]
+final class PSR7WorkerTest
{
public function testStateServerLeak(): void
{
@@ -84,10 +85,11 @@ public function testStateServerLeak(): void
unset($_SERVER['REQUEST_TIME']);
unset($_SERVER['REQUEST_TIME_FLOAT']);
- self::assertEquals($expectedServer, $_SERVER);
+ Assert::equals($_SERVER, $expectedServer);
}
}
+ #[AfterTest]
protected function tearDown(): void
{
// Clean all extra output buffers
diff --git a/tests/Unit/StreamResponseTest.php b/tests/Unit/StreamResponseTest.php
index fd73172..5fe834f 100644
--- a/tests/Unit/StreamResponseTest.php
+++ b/tests/Unit/StreamResponseTest.php
@@ -4,13 +4,16 @@
namespace Spiral\RoadRunner\Tests\Http\Unit;
-use PHPUnit\Framework\TestCase;
use Spiral\RoadRunner\Http\HttpWorker;
use Spiral\RoadRunner\Payload;
use Spiral\RoadRunner\Tests\Http\Unit\Stub\TestRelay;
use Spiral\RoadRunner\Worker;
+use Testo\Assert;
+use Testo\Lifecycle\AfterTest;
+use Testo\Test;
-final class StreamResponseTest extends TestCase
+#[Test]
+final class StreamResponseTest
{
private TestRelay $relay;
private Worker $worker;
@@ -24,9 +27,9 @@ public function testRegularCase(): void
$this->getRelay()
->addFrame(status: 200, body: 'Hello, World!', headers: ['Content-Type' => 'text/plain'], stream: true);
- self::assertTrue($worker->hasPayload());
- self::assertInstanceOf(Payload::class, $payload = $worker->waitPayload());
- self::assertSame('Hello, World!', $payload->body);
+ Assert::true($worker->hasPayload());
+ Assert::instanceOf($payload = $worker->waitPayload(), Payload::class);
+ Assert::same($payload->body, 'Hello, World!');
}
/**
@@ -44,8 +47,8 @@ public function testStreamResponseWithMultipleFrames(): void
yield '!';
})());
- self::assertFalse($this->worker->hasPayload());
- self::assertSame('Hello, World!', $this->getRelay()->getReceivedBody());
+ Assert::false($this->worker->hasPayload());
+ Assert::same($this->getRelay()->getReceivedBody(), 'Hello, World!');
}
public function testStopStreamResponse(): void
@@ -65,9 +68,10 @@ public function testStopStreamResponse(): void
yield '!';
})());
- self::assertSame('Hello,', $this->getRelay()->getReceivedBody());
+ Assert::same($this->getRelay()->getReceivedBody(), 'Hello,');
}
+ #[AfterTest]
protected function tearDown(): void
{
unset($this->relay, $this->worker);