2021-12-08 16:53:43 +01:00
|
|
|
<?php
|
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\API\Request;
|
|
|
|
use Core\Objects\Context;
|
|
|
|
use Core\Objects\DatabaseEntity\User;
|
2021-12-08 16:53:43 +01:00
|
|
|
|
|
|
|
function __new_header_impl(string $line) {
|
|
|
|
if (preg_match("/^HTTP\/([0-9.]+) (\d+) (.*)$/", $line, $m)) {
|
|
|
|
RequestTest::$SENT_STATUS_CODE = intval($m[2]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$key = $line;
|
|
|
|
$value = "";
|
|
|
|
$index = strpos($key, ": ");
|
|
|
|
if ($index !== false) {
|
|
|
|
$key = substr($line, 0, $index);
|
|
|
|
$value = substr($line, $index + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
RequestTest::$SENT_HEADERS[$key] = $value;
|
|
|
|
}
|
|
|
|
|
2022-06-01 12:28:50 +02:00
|
|
|
function __new_http_response_code_impl($code) {
|
2021-12-08 16:53:43 +01:00
|
|
|
RequestTest::$SENT_STATUS_CODE = $code;
|
|
|
|
}
|
|
|
|
|
|
|
|
function __new_die_impl($content) {
|
|
|
|
RequestTest::$SENT_CONTENT = $content;
|
|
|
|
}
|
|
|
|
|
|
|
|
class RequestTest extends \PHPUnit\Framework\TestCase {
|
|
|
|
|
|
|
|
const FUNCTION_OVERRIDES = ["header", "http_response_code"];
|
|
|
|
static User $USER;
|
|
|
|
static User $USER_LOGGED_IN;
|
2022-06-20 19:52:31 +02:00
|
|
|
static Context $CONTEXT;
|
2021-12-08 16:53:43 +01:00
|
|
|
|
|
|
|
static ?string $SENT_CONTENT;
|
|
|
|
static array $SENT_HEADERS;
|
|
|
|
static ?int $SENT_STATUS_CODE;
|
|
|
|
|
|
|
|
public static function setUpBeforeClass(): void {
|
|
|
|
|
2022-06-20 19:52:31 +02:00
|
|
|
RequestTest::$CONTEXT = new Context();
|
|
|
|
if (!RequestTest::$CONTEXT->initSQL()) {
|
2021-12-08 16:53:43 +01:00
|
|
|
throw new Exception("Could not establish database connection");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!function_exists("runkit7_function_rename") || !function_exists("runkit7_function_remove")) {
|
|
|
|
throw new Exception("Request Unit Test requires runkit7 extension");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ini_get("runkit.internal_override") !== "1") {
|
|
|
|
throw new Exception("Request Unit Test requires runkit7 with internal_override enabled to function properly");
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
|
|
|
runkit7_function_rename($functionName, "__orig_${functionName}_impl");
|
|
|
|
runkit7_function_rename("__new_${functionName}_impl", $functionName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function tearDownAfterClass(): void {
|
2022-06-20 19:52:31 +02:00
|
|
|
RequestTest::$CONTEXT->getSQL()?->close();
|
2021-12-08 16:53:43 +01:00
|
|
|
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
|
|
|
runkit7_function_remove($functionName);
|
|
|
|
runkit7_function_rename("__orig_${functionName}_impl", $functionName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function simulateRequest(Request $request, string $method, array $get = [], array $post = [], array $headers = []): bool {
|
|
|
|
|
2022-06-20 19:52:31 +02:00
|
|
|
if (!self::$CONTEXT->isCLI()) {
|
2021-12-08 16:53:43 +01:00
|
|
|
self::throwException(new \Exception("Cannot simulate request outside cli"));
|
|
|
|
}
|
|
|
|
|
|
|
|
$_SERVER = [];
|
|
|
|
$_SERVER["REQUEST_METHOD"] = $method;
|
|
|
|
self::$SENT_HEADERS = [];
|
|
|
|
self::$SENT_STATUS_CODE = null;
|
|
|
|
self::$SENT_CONTENT = null;
|
|
|
|
|
|
|
|
foreach ($headers as $key => $value) {
|
|
|
|
$key = "HTTP_" . preg_replace("/\s/", "_", strtoupper($key));
|
|
|
|
$_SERVER[$key] = $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
$_GET = $get;
|
|
|
|
$_POST = $post;
|
|
|
|
|
|
|
|
return $request->execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testAllMethods() {
|
|
|
|
// all methods allowed
|
2022-06-20 19:52:31 +02:00
|
|
|
$allMethodsAllowed = new RequestAllMethods(RequestTest::$CONTEXT, true);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "GET"), $allMethodsAllowed->getLastError());
|
|
|
|
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "POST"), $allMethodsAllowed->getLastError());
|
|
|
|
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "PUT"), $allMethodsAllowed->getLastError());
|
|
|
|
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "DELETE"), $allMethodsAllowed->getLastError());
|
|
|
|
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "OPTIONS"), $allMethodsAllowed->getLastError());
|
|
|
|
$this->assertEquals(204, self::$SENT_STATUS_CODE);
|
|
|
|
$this->assertEquals(["Allow" => "OPTIONS, GET, POST"], self::$SENT_HEADERS);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testOnlyPost() {
|
|
|
|
// only post allowed
|
2022-06-20 19:52:31 +02:00
|
|
|
$onlyPostAllowed = new RequestOnlyPost(RequestTest::$CONTEXT, true);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->assertFalse($this->simulateRequest($onlyPostAllowed, "GET"));
|
|
|
|
$this->assertEquals("This method is not allowed", $onlyPostAllowed->getLastError(), $onlyPostAllowed->getLastError());
|
|
|
|
$this->assertEquals(405, self::$SENT_STATUS_CODE);
|
|
|
|
$this->assertTrue($this->simulateRequest($onlyPostAllowed, "POST"), $onlyPostAllowed->getLastError());
|
|
|
|
$this->assertTrue($this->simulateRequest($onlyPostAllowed, "OPTIONS"), $onlyPostAllowed->getLastError());
|
|
|
|
$this->assertEquals(204, self::$SENT_STATUS_CODE);
|
|
|
|
$this->assertEquals(["Allow" => "OPTIONS, POST"], self::$SENT_HEADERS);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testPrivate() {
|
|
|
|
// private method
|
2022-06-20 19:52:31 +02:00
|
|
|
$privateExternal = new RequestPrivate(RequestTest::$CONTEXT, true);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->assertFalse($this->simulateRequest($privateExternal, "GET"));
|
|
|
|
$this->assertEquals("This function is private.", $privateExternal->getLastError());
|
|
|
|
$this->assertEquals(403, self::$SENT_STATUS_CODE);
|
|
|
|
|
2022-06-20 19:52:31 +02:00
|
|
|
$privateInternal = new RequestPrivate(RequestTest::$CONTEXT, false);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->assertTrue($privateInternal->execute());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testDisabled() {
|
|
|
|
// disabled method
|
2022-06-20 19:52:31 +02:00
|
|
|
$disabledMethod = new RequestDisabled(RequestTest::$CONTEXT, true);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->assertFalse($this->simulateRequest($disabledMethod, "GET"));
|
|
|
|
$this->assertEquals("This function is currently disabled.", $disabledMethod->getLastError(), $disabledMethod->getLastError());
|
|
|
|
$this->assertEquals(503, self::$SENT_STATUS_CODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testLoginRequired() {
|
2022-06-20 19:52:31 +02:00
|
|
|
$loginRequired = new RequestLoginRequired(RequestTest::$CONTEXT, true);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->assertFalse($this->simulateRequest($loginRequired, "GET"));
|
|
|
|
$this->assertEquals("You are not logged in.", $loginRequired->getLastError(), $loginRequired->getLastError());
|
|
|
|
$this->assertEquals(401, self::$SENT_STATUS_CODE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract class TestRequest extends Request {
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false, $params = []) {
|
|
|
|
parent::__construct($context, $externalCall, $params);
|
2021-12-08 16:53:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function _die(string $data = ""): bool {
|
|
|
|
__new_die_impl($data);
|
|
|
|
return true;
|
|
|
|
}
|
2022-05-31 16:14:49 +02:00
|
|
|
|
|
|
|
protected function _execute(): bool {
|
|
|
|
return true;
|
|
|
|
}
|
2021-12-08 16:53:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class RequestAllMethods extends TestRequest {
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false) {
|
|
|
|
parent::__construct($context, $externalCall, []);
|
2021-12-08 16:53:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RequestOnlyPost extends TestRequest {
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false) {
|
|
|
|
parent::__construct($context, $externalCall, []);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->forbidMethod("GET");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RequestPrivate extends TestRequest {
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false) {
|
|
|
|
parent::__construct($context, $externalCall, []);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->isPublic = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RequestDisabled extends TestRequest {
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false) {
|
|
|
|
parent::__construct($context, $externalCall, []);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->isDisabled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RequestLoginRequired extends TestRequest {
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false) {
|
|
|
|
parent::__construct($context, $externalCall, []);
|
2021-12-08 16:53:43 +01:00
|
|
|
$this->loginRequired = true;
|
|
|
|
}
|
|
|
|
}
|