mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Consolidate creation of synthetic server requests
This commit is contained in:
parent
67bde97e0c
commit
c5337b37b4
8 changed files with 130 additions and 106 deletions
|
@ -10,6 +10,12 @@ namespace JKingWeb\Arsse\Misc;
|
|||
* A collection of functions for manipulating URLs
|
||||
*/
|
||||
class URL {
|
||||
|
||||
/** Returns whether a URL is absolute i.e. has a scheme */
|
||||
public static function absolute(string $url): bool {
|
||||
return (bool) strlen((string) parse_url($url, \PHP_URL_SCHEME));
|
||||
}
|
||||
|
||||
/** Normalizes a URL
|
||||
*
|
||||
* Normalizations performed are:
|
||||
|
@ -137,4 +143,28 @@ class URL {
|
|||
$out = ($absolute ? "/" : "").$out.($index ? "/" : "");
|
||||
return str_replace("//", "/", $out);
|
||||
}
|
||||
|
||||
/** Appends data to a URL's query component
|
||||
*
|
||||
* @param string $url The input URL
|
||||
* @param string $data The data to append. This should already be escaped where necessary and not start with any delimiter
|
||||
* @param string $glue The query subcomponent delimiter, usually "&". If the URL has no query, "?" will be prepended instead
|
||||
*/
|
||||
public static function queryAppend(string $url, string $data, string $glue = "&"): string {
|
||||
if (!strlen($data)) {
|
||||
return $url;
|
||||
}
|
||||
$insPos = strpos($url, "#");
|
||||
$insPos = $insPos === false ? strlen($url) : $insPos;
|
||||
$qPos = strpos($url, "?");
|
||||
$hasQuery = $qPos !== false;
|
||||
$glue = $hasQuery ? $glue : "?";
|
||||
if ($hasQuery && $insPos > 0) {
|
||||
if ($url[$insPos - 1] === $glue || ($insPos - 1) == $qPos) {
|
||||
// if the URL already has excess glue, use it
|
||||
$glue = "";
|
||||
}
|
||||
}
|
||||
return substr($url, 0, $insPos).$glue.$data.substr($url, $insPos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,4 +75,20 @@ class TestURL extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
[" ", "%20"],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider provideQueries */
|
||||
public function testAppendQueryParameters(string $url, string $query, string $exp) {
|
||||
$this->assertSame($exp, URL::queryAppend($url, $query));
|
||||
}
|
||||
|
||||
public function provideQueries() {
|
||||
return [
|
||||
["/", "ook=eek", "/?ook=eek"],
|
||||
["/?", "ook=eek", "/?ook=eek"],
|
||||
["/#ack", "ook=eek", "/?ook=eek#ack"],
|
||||
["/?Huh?", "ook=eek", "/?Huh?&ook=eek"],
|
||||
["/?Eh?&Huh?&", "ook=eek", "/?Eh?&Huh?&ook=eek"],
|
||||
["/#ack", "", "/#ack"],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,33 +145,11 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
return $value;
|
||||
}
|
||||
|
||||
protected function req($dataGet, $dataPost = "", string $method = "POST", string $type = null, string $url = "", string $user = null): ServerRequest {
|
||||
$url = "/fever/".$url;
|
||||
protected function req($dataGet, $dataPost = "", string $method = "POST", string $type = null, string $target = "", string $user = null): ServerRequest {
|
||||
$prefix = "/fever/";
|
||||
$url = $prefix.$target;
|
||||
$type = $type ?? "application/x-www-form-urlencoded";
|
||||
$server = [
|
||||
'REQUEST_METHOD' => $method,
|
||||
'REQUEST_URI' => $url,
|
||||
'HTTP_CONTENT_TYPE' => $type,
|
||||
];
|
||||
$req = new ServerRequest($server, [], $url, $method, "php://memory", ['Content-Type' => $type]);
|
||||
if (!is_array($dataGet)) {
|
||||
parse_str($dataGet, $dataGet);
|
||||
}
|
||||
$req = $req->withRequestTarget($url)->withQueryParams($dataGet);
|
||||
if (is_array($dataPost)) {
|
||||
$req = $req->withParsedBody($dataPost);
|
||||
} else {
|
||||
parse_str($dataPost, $arr);
|
||||
$req = $req->withParsedBody($arr);
|
||||
}
|
||||
if (isset($user)) {
|
||||
if (strlen($user)) {
|
||||
$req = $req->withAttribute("authenticated", true)->withAttribute("authenticatedUser", $user);
|
||||
} else {
|
||||
$req = $req->withAttribute("authenticationFailed", true);
|
||||
}
|
||||
}
|
||||
return $req;
|
||||
return $this->serverRequest($method, $url, $prefix, [], [], $dataPost, $type, $dataGet, $user);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
|
@ -457,7 +435,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
return [
|
||||
'Not an API request' => [$this->req(""), new EmptyResponse(404)],
|
||||
'Wrong method' => [$this->req("api", "", "GET"), new EmptyResponse(405, ['Allow' => "OPTIONS,POST"])],
|
||||
'Wrong content type' => [$this->req("api", "", "POST", "application/json"), new EmptyResponse(415, ['Accept' => "application/x-www-form-urlencoded"])],
|
||||
'Wrong content type' => [$this->req("api", '{"api_key":"validToken"}', "POST", "application/json"), new EmptyResponse(415, ['Accept' => "application/x-www-form-urlencoded"])],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -298,40 +298,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
],
|
||||
];
|
||||
|
||||
protected function req(string $method, string $target, string $data = "", array $headers = []): ResponseInterface {
|
||||
$url = "/index.php/apps/news/api/v1-2".$target;
|
||||
$server = [
|
||||
'REQUEST_METHOD' => $method,
|
||||
'REQUEST_URI' => $url,
|
||||
'PHP_AUTH_USER' => "john.doe@example.com",
|
||||
'PHP_AUTH_PW' => "secret",
|
||||
'REMOTE_USER' => "john.doe@example.com",
|
||||
];
|
||||
if (strlen($data)) {
|
||||
$server['HTTP_CONTENT_TYPE'] = "application/json";
|
||||
}
|
||||
$req = new ServerRequest($server, [], $url, $method, "php://memory");
|
||||
if (Arsse::$user->auth("john.doe@example.com", "secret")) {
|
||||
$req = $req->withAttribute("authenticated", true)->withAttribute("authenticatedUser", "john.doe@example.com");
|
||||
}
|
||||
foreach ($headers as $key => $value) {
|
||||
if (!is_null($value)) {
|
||||
$req = $req->withHeader($key, $value);
|
||||
} else {
|
||||
$req = $req->withoutHeader($key);
|
||||
}
|
||||
}
|
||||
if (strlen($data)) {
|
||||
$body = $req->getBody();
|
||||
$body->write($data);
|
||||
$req = $req->withBody($body);
|
||||
}
|
||||
$q = $req->getUri()->getQuery();
|
||||
if (strlen($q)) {
|
||||
parse_str($q, $q);
|
||||
$req = $req->withQueryParams($q);
|
||||
}
|
||||
$req = $req->withRequestTarget($target);
|
||||
protected function req(string $method, string $target, string $data = "", array $headers = [], bool $authenticated = true): ResponseInterface {
|
||||
$prefix = "/index.php/apps/news/api/v1-2";
|
||||
$url = $prefix.$target;
|
||||
$req = $this->serverRequest($method, $url, $prefix, $headers, [], $data, "application/json", [], $authenticated ? "john.doe@example.com" : "");
|
||||
return $this->h->dispatch($req);
|
||||
}
|
||||
|
||||
|
@ -340,7 +310,6 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
self::setConf();
|
||||
// create a mock user manager
|
||||
Arsse::$user = \Phake::mock(User::class);
|
||||
\Phake::when(Arsse::$user)->auth->thenReturn(true);
|
||||
Arsse::$user->id = "john.doe@example.com";
|
||||
// create a mock database interface
|
||||
Arsse::$db = \Phake::mock(Database::class);
|
||||
|
@ -357,9 +326,8 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
}
|
||||
|
||||
public function testSendAuthenticationChallenge() {
|
||||
\Phake::when(Arsse::$user)->auth->thenReturn(false);
|
||||
$exp = new EmptyResponse(401);
|
||||
$this->assertMessage($exp, $this->req("GET", "/"));
|
||||
$this->assertMessage($exp, $this->req("GET", "/", "", [], false));
|
||||
}
|
||||
|
||||
public function testRespondToInvalidPaths() {
|
||||
|
|
|
@ -19,13 +19,9 @@ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
}
|
||||
|
||||
protected function req(string $method, string $target): ResponseInterface {
|
||||
$url = "/index.php/apps/news/api".$target;
|
||||
$server = [
|
||||
'REQUEST_METHOD' => $method,
|
||||
'REQUEST_URI' => $url,
|
||||
];
|
||||
$req = new ServerRequest($server, [], $url, $method, "php://memory");
|
||||
$req = $req->withRequestTarget($target);
|
||||
$prefix = "/index.php/apps/news/api";
|
||||
$url = $prefix.$target;
|
||||
$req = $this->serverRequest($method, $url, $prefix);
|
||||
return (new Versions)->dispatch($req);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,27 +126,10 @@ LONG_STRING;
|
|||
}
|
||||
|
||||
protected function req($data, string $method = "POST", string $target = "", string $strData = null, string $user = null): ResponseInterface {
|
||||
$url = "/tt-rss/api".$target;
|
||||
$server = [
|
||||
'REQUEST_METHOD' => $method,
|
||||
'REQUEST_URI' => $url,
|
||||
'HTTP_CONTENT_TYPE' => "application/x-www-form-urlencoded",
|
||||
];
|
||||
$req = new ServerRequest($server, [], $url, $method, "php://memory");
|
||||
$body = $req->getBody();
|
||||
if (!is_null($strData)) {
|
||||
$body->write($strData);
|
||||
} else {
|
||||
$body->write(json_encode($data));
|
||||
}
|
||||
$req = $req->withBody($body)->withRequestTarget($target);
|
||||
if (isset($user)) {
|
||||
if (strlen($user)) {
|
||||
$req = $req->withAttribute("authenticated", true)->withAttribute("authenticatedUser", $user);
|
||||
} else {
|
||||
$req = $req->withAttribute("authenticationFailed", true);
|
||||
}
|
||||
}
|
||||
$prefix = "/tt-rss/api";
|
||||
$url = $prefix.$target;
|
||||
$body = $strData ?? json_encode($data);
|
||||
$req = $this->serverRequest($method, $url, $prefix, [], ['HTTP_CONTENT_TYPE' => "application/x-www-form-urlencoded"], $body, "application/json", [], $user);
|
||||
return $this->h->dispatch($req);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,20 +34,9 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
}
|
||||
|
||||
protected function req(string $target, string $method = "GET", string $user = null): ResponseInterface {
|
||||
$url = "/tt-rss/feed-icons/".$target;
|
||||
$server = [
|
||||
'REQUEST_METHOD' => $method,
|
||||
'REQUEST_URI' => $url,
|
||||
];
|
||||
$req = new ServerRequest($server, [], $url, $method, "php://memory");
|
||||
$req = $req->withRequestTarget($target);
|
||||
if (isset($user)) {
|
||||
if (strlen($user)) {
|
||||
$req = $req->withAttribute("authenticated", true)->withAttribute("authenticatedUser", $user);
|
||||
} else {
|
||||
$req = $req->withAttribute("authenticationFailed", true);
|
||||
}
|
||||
}
|
||||
$prefix = "/tt-rss/feed-icons/";
|
||||
$url = $prefix.$target;
|
||||
$req = $this->serverRequest($method, $url, $prefix, [], [], null, "", [], $user);
|
||||
return $this->h->dispatch($req);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,10 +13,12 @@ use JKingWeb\Arsse\Db\Driver;
|
|||
use JKingWeb\Arsse\Db\Result;
|
||||
use JKingWeb\Arsse\Misc\Date;
|
||||
use JKingWeb\Arsse\Misc\ValueInfo;
|
||||
use JKingWeb\Arsse\Misc\URL;
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\Diactoros\Response\XmlResponse;
|
||||
|
||||
|
@ -61,6 +63,68 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
|
|||
Arsse::$conf = (($force ? null : Arsse::$conf) ?? (new Conf))->import($defaults)->import($conf);
|
||||
}
|
||||
|
||||
protected function serverRequest(string $method, string $url, string $urlPrefix, array $headers = [], array $vars = [], $body = null, string $type = "", $params = [], string $user = null): ServerRequestInterface {
|
||||
$server = [
|
||||
'REQUEST_METHOD' => $method,
|
||||
'REQUEST_URI' => $url,
|
||||
];
|
||||
if (strlen($type)) {
|
||||
$server['HTTP_CONTENT_TYPE'] = $type;
|
||||
}
|
||||
if (isset($params)) {
|
||||
if (is_array($params)) {
|
||||
$params = implode("&", array_map(function($v, $k) {
|
||||
return rawurlencode($k).(isset($v) ? "=".rawurlencode($v) : "");
|
||||
}, $params, array_keys($params)));
|
||||
}
|
||||
$url = URL::queryAppend($url, (string) $params);
|
||||
}
|
||||
$q = parse_url($url, \PHP_URL_QUERY);
|
||||
if (strlen($q ?? "")) {
|
||||
parse_str($q, $params);
|
||||
} else {
|
||||
$params = [];
|
||||
}
|
||||
$parsedBody = null;
|
||||
if (isset($body)) {
|
||||
if (is_string($body) && in_array(strtolower($type), ["", "application/x-www-form-urlencoded"])) {
|
||||
parse_str($body, $parsedBody);
|
||||
} elseif (!is_string($body) && in_array(strtolower($type), ["application/json", "text/json"])) {
|
||||
$body = json_encode($body, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
|
||||
} elseif (!is_string($body) && in_array(strtolower($type), ["", "application/x-www-form-urlencoded"])) {
|
||||
$parsedBody = $body;
|
||||
$body = http_build_query($body, "a", "&");
|
||||
}
|
||||
}
|
||||
$server = array_merge($server, $vars);
|
||||
$req = new ServerRequest($server, [], $url, $method, "php://memory", [], [], $params, $parsedBody);
|
||||
if (isset($user)) {
|
||||
if (strlen($user)) {
|
||||
$req = $req->withAttribute("authenticated", true)->withAttribute("authenticatedUser", $user);
|
||||
} else {
|
||||
$req = $req->withAttribute("authenticationFailed", true);
|
||||
}
|
||||
}
|
||||
if (strlen($type) &&strlen($body ?? "")) {
|
||||
$req = $req->withHeader("Content-Type", $type);
|
||||
}
|
||||
foreach ($headers as $key => $value) {
|
||||
if (!is_null($value)) {
|
||||
$req = $req->withHeader($key, $value);
|
||||
} else {
|
||||
$req = $req->withoutHeader($key);
|
||||
}
|
||||
}
|
||||
$target = substr(URL::normalize($url), strlen($urlPrefix));
|
||||
$req = $req->withRequestTarget($target);
|
||||
if (strlen($body ?? "")) {
|
||||
$p = $req->getBody();
|
||||
$p->write($body);
|
||||
$req = $req->withBody($p);
|
||||
}
|
||||
return $req;
|
||||
}
|
||||
|
||||
public function assertException($msg = "", string $prefix = "", string $type = "Exception") {
|
||||
if (func_num_args()) {
|
||||
if ($msg instanceof \JKingWeb\Arsse\AbstractException) {
|
||||
|
|
Loading…
Reference in a new issue