orbit/tests/src/Orbit/ServerTest.php
2020-09-06 02:32:56 -05:00

281 lines
8.7 KiB
PHP

<?php declare(strict_types=1);
namespace Orbit\Tests;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use Orbit\Cert;
use Orbit\Config;
use Orbit\Server;
use Orbit\Request;
use Orbit\Response;
final class ServerTest extends TestCase
{
public function makeTestLogger(): Logger
{
$logger = new Logger('test-orbit');
$logger->pushHandler(new \Monolog\Handler\TestHandler());
return $logger;
}
public function getTestLogRecords($logger): array
{
return $logger->getHandlers()[0]->getRecords();
}
public function getTestLogMessages($logger): array
{
$messages = [];
foreach ($this->getTestLogRecords($logger) as $message) {
$messages[] = $message['level_name'] . ": " . $message['message'];
}
return $messages;
}
public function testConstructFailsWithoutConfig(): void
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage("Missing required cert file");
$config = new Config();
$server = new Server($config);
}
public function testConstructAutoCert(): void
{
$config = new Config(true);
$server = new Server($config);
$this->assertInstanceOf(Server::class, $server);
$this->assertEquals(1, count($server->getLogger()->getHandlers()));
}
public function testConstructSetLogger(): void
{
$config = new Config(true);
$logger = new Logger('orbit-test');
$server = new Server($config, null, $logger);
$l = $server->getLogger();
$this->assertSame($l, $logger);
$this->assertEquals(0, count($l->getHandlers()));
}
public function testConstructSetCert(): void
{
$config = new Config(true);
$cert = new Cert($config);
$server = new Server($config, $cert);
$this->assertSame($cert, $server->cert);
}
public function testConstructSetsTimeout(): void
{
ini_set("default_socket_timeout", "32");
$server = new Server(new Config(true));
$this->assertEquals(32, $server->timeout);
}
public function testIntakeConnectionsMustBeResource(): void
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage("Stream server must be resource object");
$server = new Server(new Config(true));
$s = new \stdClass();
$result = $server->intakeConnections($s);
}
public function testValidateRequestMustContainHost(): void
{
$request = new Request('');
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertFalse($is_valid);
$this->assertSame(Response::STATUS_BAD_REQUEST, $response->status);
$this->assertStringContainsString("Bad request", $response->meta);
}
public function testValidateRequestMustHaveCorrectScheme(): void
{
$request = new Request('https://foo.bar/');
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertFalse($is_valid);
$this->assertSame(Response::STATUS_PROXY_REQUEST_REFUSED, $response->status);
$this->assertStringContainsString("unsupported scheme", $response->meta);
}
public function testValidateRequestMustHaveCorrectHost(): void
{
$request = new Request('gemini://superfly.com/');
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertFalse($is_valid);
$this->assertSame(Response::STATUS_PROXY_REQUEST_REFUSED, $response->status);
$this->assertStringContainsString("invalid host", $response->meta);
}
public function testValidateRequestMustHaveCorrectPort(): void
{
$request = new Request('gemini://localhost:8080/');
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertFalse($is_valid);
$this->assertSame(Response::STATUS_PROXY_REQUEST_REFUSED, $response->status);
$this->assertStringContainsString("invalid port", $response->meta);
}
public function testValidateRequestCanIncludePort(): void
{
$request = new Request('gemini://localhost:1965/');
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertTrue($is_valid);
$this->assertSame('', $response->status);
}
public function testValidateRequestMustNotBeMoreThanLimit(): void
{
$url = 'gemini://localhost/';
$request = new Request($url . str_repeat("x", 1025 - strlen($url)));
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertFalse($is_valid);
$this->assertSame(Response::STATUS_BAD_REQUEST, $response->status);
$this->assertStringContainsString("too long", $response->meta);
}
public function testValidateRequestCanBeAtLimit(): void
{
$url = 'gemini://localhost/';
$request = new Request($url . str_repeat("x", 1024 - strlen($url)));
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertTrue($is_valid);
$this->assertSame('', $response->status);
}
public function testValidateRequestMustNotContainNonUtf8Bytes(): void
{
$request = new Request('gemini://localhost/' . chr(240) . chr(159) . chr(144) . chr(152));
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertFalse($is_valid);
$this->assertSame(Response::STATUS_BAD_REQUEST, $response->status);
$this->assertStringContainsString("non-UTF8", $response->meta);
}
public function testValidateRequestLocalhostIsValid(): void
{
$request = new Request('gemini://localhost/');
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertTrue($is_valid);
$this->assertSame('', $response->status);
}
public function testValidateRequestLoopbackAddrIsValid(): void
{
$request = new Request('gemini://127.0.0.1/');
$server = new Server(new Config(true));
[$is_valid, $response] = $server->validateRequest($request);
$this->assertTrue($is_valid);
$this->assertSame('', $response->status);
}
public function testValidateRequestMatchesConfigHostname(): void
{
$request = new Request('gemini://dogdays.dev/');
$config = new Config(true);
$config->hostname = 'dogdays.dev';
$server = new Server($config);
[$is_valid, $response] = $server->validateRequest($request);
$this->assertTrue($is_valid);
$this->assertSame('', $response->status);
@unlink("certs/dogdays.dev.cert.pem");
@unlink("certs/dogdays.dev.key.pem");
}
public function testValidateRequestMatchesConfigPort(): void
{
$request = new Request('gemini://localhost:1972/');
$config = new Config(true);
$config->port = '1972';
$server = new Server($config);
[$is_valid, $response] = $server->validateRequest($request);
$this->assertTrue($is_valid);
$this->assertSame('', $response->status);
}
public function testOnWarningTimedOut(): void
{
$logger = $this->makeTestLogger();
$server = new Server(new Config(true), null, $logger);
$result = $server->onWarning(1, 'Connection timed out');
$this->assertTrue($result);
$messages = $this->getTestLogMessages($logger);
$this->assertNotContains('Error while accepting', $messages);
}
public function testOnWarningError(): void
{
$logger = $this->makeTestLogger();
$server = new Server(new Config(true), null, $logger);
$result = $server->onWarning(1, 'Something terrible happened');
$this->assertTrue($result);
$messages = $this->getTestLogMessages($logger);
$this->assertStringContainsString('Error while accepting connection', $messages[3]);
}
public function testGetListenAddress(): void
{
$config = new Config(true);
$config->host = '127.0.0.1';
$config->port = 1888;
$server = new Server($config);
$address = $server->getListenAddress();
$this->assertSame('tls://127.0.0.1:1888', $address);
}
public function tearDown(): void
{
@unlink("certs/localhost.cert.pem");
@unlink("certs/localhost.key.pem");
}
}