mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
First tests for Miniflux
This commit is contained in:
parent
f6cd2b87ce
commit
06dee77bac
13 changed files with 328 additions and 33 deletions
|
@ -400,7 +400,7 @@ class Database {
|
|||
* @param \DateTimeInterface|null $expires An optional expiry date and time for the token
|
||||
* @param string $data Application-specific data associated with a token
|
||||
*/
|
||||
public function tokenCreate(string $user, string $class, string $id = null, \DateTimeInterface $expires = null, string $data = null): string {
|
||||
public function tokenCreate(string $user, string $class, string $id = null, ?\DateTimeInterface $expires = null, string $data = null): string {
|
||||
if (!$this->userExists($user)) {
|
||||
throw new User\ExceptionConflict("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
|
||||
}
|
||||
|
@ -418,7 +418,7 @@ class Database {
|
|||
* @param string $class The class of the token e.g. the protocol name
|
||||
* @param string|null $id The ID of a specific token, or null for all tokens in the class
|
||||
*/
|
||||
public function tokenRevoke(string $user, string $class, string $id = null): bool {
|
||||
public function tokenRevoke(string $user, string $class, ?string $id = null): bool {
|
||||
if (is_null($id)) {
|
||||
$out = $this->db->prepare("DELETE FROM arsse_tokens where \"user\" = ? and class = ?", "str", "str")->run($user, $class)->changes();
|
||||
} else {
|
||||
|
@ -436,6 +436,11 @@ class Database {
|
|||
return $out;
|
||||
}
|
||||
|
||||
/** List tokens associated with a user */
|
||||
public function tokenList(string $user, string $class): Db\Result {
|
||||
return $this->db->prepare("SELECT id,created,expires,data from arsse_tokens where class = ? and user = ? and (expires is null or expires > CURRENT_TIMESTAMP)", "str", "str")->run($class, $user);
|
||||
}
|
||||
|
||||
/** Deletes expires tokens from the database, returning the number of deleted tokens */
|
||||
public function tokenCleanup(): int {
|
||||
return $this->db->query("DELETE FROM arsse_tokens where expires < CURRENT_TIMESTAMP")->changes();
|
||||
|
|
12
lib/REST.php
12
lib/REST.php
|
@ -42,9 +42,19 @@ class REST {
|
|||
],
|
||||
'miniflux' => [ // Miniflux https://miniflux.app/docs/api.html
|
||||
'match' => '/v1/',
|
||||
'strip' => '/v1',
|
||||
'strip' => '',
|
||||
'class' => REST\Miniflux\V1::class,
|
||||
],
|
||||
'miniflux-version' => [ // Miniflux version report
|
||||
'match' => '/version',
|
||||
'strip' => '',
|
||||
'class' => REST\Miniflux\Status::class,
|
||||
],
|
||||
'miniflux-healthcheck' => [ // Miniflux health check
|
||||
'match' => '/healthcheck',
|
||||
'strip' => '',
|
||||
'class' => REST\Miniflux\Status::class,
|
||||
],
|
||||
// Other candidates:
|
||||
// Microsub https://indieweb.org/Microsub
|
||||
// Google Reader http://feedhq.readthedocs.io/en/latest/api/index.html
|
||||
|
|
19
lib/REST/Miniflux/ErrorResponse.php
Normal file
19
lib/REST/Miniflux/ErrorResponse.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\REST\Miniflux;
|
||||
|
||||
use JKingWeb\Arsse\Arsse;
|
||||
|
||||
class ErrorResponse extends \Laminas\Diactoros\Response\JsonResponse {
|
||||
public function __construct($data, int $status = 400, array $headers = [], int $encodingOptions = self::DEFAULT_JSON_FLAGS) {
|
||||
assert(isset(Arsse::$lang) && Arsse::$lang instanceof \JKingWeb\Arsse\Lang, new \Exception("Language database must be initialized before use"));
|
||||
$data = (array) $data;
|
||||
$msg = array_shift($data);
|
||||
$data = ["error_message" => Arsse::$lang->msg("API.Miniflux.Error.".$msg, $data)];
|
||||
parent::__construct($data, $status, $headers, $encodingOptions);
|
||||
}
|
||||
}
|
37
lib/REST/Miniflux/Status.php
Normal file
37
lib/REST/Miniflux/Status.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\REST\Miniflux;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Laminas\Diactoros\Response\TextResponse;
|
||||
|
||||
class Status extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function dispatch(ServerRequestInterface $req): ResponseInterface {
|
||||
$target = parse_url($req->getRequestTarget())['path'] ?? "";
|
||||
if (!in_array($target, ["/version", "/healthcheck"])) {
|
||||
return new EmptyResponse(404);
|
||||
}
|
||||
$method = $req->getMethod();
|
||||
if ($method === "OPTIONS") {
|
||||
return new EmptyResponse(204, ['Allow' => "HEAD, GET"]);
|
||||
} elseif ($method !== "GET") {
|
||||
return new EmptyResponse(405, ['Allow' => "HEAD, GET"]);
|
||||
}
|
||||
$out = "";
|
||||
if ($target === "/version") {
|
||||
$out = V1::VERSION;
|
||||
} elseif ($target === "/healthcheck") {
|
||||
$out = "OK";
|
||||
}
|
||||
return new TextResponse($out);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ use JKingWeb\Arsse\Misc\HTTP;
|
|||
use JKingWeb\Arsse\REST\Exception;
|
||||
use JKingWeb\Arsse\REST\Exception404;
|
||||
use JKingWeb\Arsse\REST\Exception405;
|
||||
use JKingWeb\Arsse\User\ExceptionConflict as UserException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
|
@ -20,6 +21,8 @@ use Laminas\Diactoros\Response\EmptyResponse;
|
|||
class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||
protected const ACCEPTED_TYPES_OPML = ["text/xml", "application/xml", "text/x-opml"];
|
||||
protected const ACCEPTED_TYPES_JSON = ["application/json", "text/json"];
|
||||
public const VERSION = "2.0.25";
|
||||
|
||||
protected $paths = [
|
||||
'/categories' => ['GET' => "getCategories", 'POST' => "createCategory"],
|
||||
'/categories/1' => ['PUT' => "updateCategory", 'DELETE' => "deleteCategory"],
|
||||
|
@ -35,25 +38,41 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
'/feeds/1/icon' => ['GET' => "getFeedIcon"],
|
||||
'/feeds/1/refresh' => ['PUT' => "refreshFeed"],
|
||||
'/feeds/refresh' => ['PUT' => "refreshAllFeeds"],
|
||||
'/healthcheck' => ['GET' => "healthCheck"],
|
||||
'/import' => ['POST' => "opmlImport"],
|
||||
'/me' => ['GET' => "getCurrentUser"],
|
||||
'/users' => ['GET' => "getUsers", 'POST' => "createUser"],
|
||||
'/users/1' => ['GET' => "getUser", 'PUT' => "updateUser", 'DELETE' => "deleteUser"],
|
||||
'/users/*' => ['GET' => "getUser"],
|
||||
'/version' => ['GET' => "getVersion"],
|
||||
];
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function dispatch(ServerRequestInterface $req): ResponseInterface {
|
||||
// try to authenticate
|
||||
protected function authenticate(ServerRequestInterface $req): bool {
|
||||
// first check any tokens; this is what Miniflux does
|
||||
foreach ($req->getHeader("X-Auth-Token") as $t) {
|
||||
if (strlen($t)) {
|
||||
// a non-empty header is authoritative, so we'll stop here one way or the other
|
||||
try {
|
||||
$d = Arsse::$db->tokenLookup("miniflux.login", $t);
|
||||
} catch (ExceptionInput $e) {
|
||||
return false;
|
||||
}
|
||||
Arsse::$user->id = $d->user;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// next check HTTP auth
|
||||
if ($req->getAttribute("authenticated", false)) {
|
||||
Arsse::$user->id = $req->getAttribute("authenticatedUser");
|
||||
} else {
|
||||
// TODO: Handle X-Auth-Token authentication
|
||||
return new EmptyResponse(401);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function dispatch(ServerRequestInterface $req): ResponseInterface {
|
||||
// try to authenticate
|
||||
if (!$this->authenticate($req)) {
|
||||
return new ErrorResponse("401", 401);
|
||||
}
|
||||
// get the request path only; this is assumed to already be normalized
|
||||
$target = parse_url($req->getRequestTarget())['path'] ?? "";
|
||||
|
@ -65,17 +84,14 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
$func = $this->chooseCall($target, $method);
|
||||
if ($func === "opmlImport") {
|
||||
if (!HTTP::matchType($req, "", ...[self::ACCEPTED_TYPES_OPML])) {
|
||||
return new EmptyResponse(415, ['Accept' => implode(", ", self::ACCEPTED_TYPES_OPML)]);
|
||||
return new ErrorResponse(415, ['Accept' => implode(", ", self::ACCEPTED_TYPES_OPML)]);
|
||||
}
|
||||
$data = (string) $req->getBody();
|
||||
} elseif ($method === "POST" || $method === "PUT") {
|
||||
if (!HTTP::matchType($req, "", ...[self::ACCEPTED_TYPES_JSON])) {
|
||||
return new EmptyResponse(415, ['Accept' => implode(", ", self::ACCEPTED_TYPES_JSON)]);
|
||||
}
|
||||
$data = @json_decode($data, true);
|
||||
if (json_last_error() !== \JSON_ERROR_NONE) {
|
||||
// if the body could not be parsed as JSON, return "400 Bad Request"
|
||||
return new EmptyResponse(400);
|
||||
return new ErrorResponse(["invalidBodyJSON", json_last_error_msg()], 400);
|
||||
}
|
||||
} else {
|
||||
$data = null;
|
||||
|
@ -154,4 +170,20 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
throw new Exception404();
|
||||
}
|
||||
}
|
||||
|
||||
public static function tokenGenerate(string $user, string $label): string {
|
||||
$t = base64_encode(random_bytes(24));
|
||||
return Arsse::$db->tokenCreate($user, "miniflux.login", $t, null, $label);
|
||||
}
|
||||
|
||||
public static function tokenList(string $user): array {
|
||||
if (!Arsse::$db->userExists($user)) {
|
||||
throw new UserException("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
|
||||
}
|
||||
$out = [];
|
||||
foreach (Arsse::$db->tokenList($user, "miniflux.login") as $r) {
|
||||
$out[] = ['label' => $r['data'], 'id' => $r['id']];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,6 @@ class User {
|
|||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
||||
public function remove(string $user): bool {
|
||||
try {
|
||||
|
@ -143,7 +142,7 @@ class User {
|
|||
if (array_key_exists("tz", $data)) {
|
||||
if (!is_string($data['tz'])) {
|
||||
throw new User\ExceptionInput("invalidTimezone", ['field' => "tz", 'value' => ""]);
|
||||
} elseif(!@timezone_open($data['tz'])) {
|
||||
} elseif (!@timezone_open($data['tz'])) {
|
||||
throw new User\ExceptionInput("invalidTimezone", ['field' => "tz", 'value' => $data['tz']]);
|
||||
}
|
||||
$in['tz'] = $data['tz'];
|
||||
|
|
|
@ -7,6 +7,9 @@ return [
|
|||
'CLI.Auth.Success' => 'Authentication successful',
|
||||
'CLI.Auth.Failure' => 'Authentication failed',
|
||||
|
||||
'API.Miniflux.Error.401' => 'Access Unauthorized',
|
||||
'API.Miniflux.Error.invalidBodyJSON' => 'Invalid JSON payload: {0}',
|
||||
|
||||
'API.TTRSS.Category.Uncategorized' => 'Uncategorized',
|
||||
'API.TTRSS.Category.Special' => 'Special',
|
||||
'API.TTRSS.Category.Labels' => 'Labels',
|
||||
|
|
|
@ -33,12 +33,16 @@ trait SeriesToken {
|
|||
'class' => "str",
|
||||
'user' => "str",
|
||||
'expires' => "datetime",
|
||||
'data' => "str",
|
||||
],
|
||||
'rows' => [
|
||||
["80fa94c1a11f11e78667001e673b2560", "fever.login", "jane.doe@example.com", $faroff],
|
||||
["27c6de8da13311e78667001e673b2560", "fever.login", "jane.doe@example.com", $past], // expired
|
||||
["ab3b3eb8a13311e78667001e673b2560", "class.class", "jane.doe@example.com", null],
|
||||
["da772f8fa13c11e78667001e673b2560", "class.class", "john.doe@example.com", $future],
|
||||
["80fa94c1a11f11e78667001e673b2560", "fever.login", "jane.doe@example.com", $faroff, null],
|
||||
["27c6de8da13311e78667001e673b2560", "fever.login", "jane.doe@example.com", $past, null], // expired
|
||||
["ab3b3eb8a13311e78667001e673b2560", "class.class", "jane.doe@example.com", null, null],
|
||||
["da772f8fa13c11e78667001e673b2560", "class.class", "john.doe@example.com", $future, null],
|
||||
["A", "miniflux.login", "jane.doe@example.com", null, "Label 1"],
|
||||
["B", "miniflux.login", "jane.doe@example.com", null, "Label 2"],
|
||||
["C", "miniflux.login", "john.doe@example.com", null, "Label 1"],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
@ -127,4 +131,13 @@ trait SeriesToken {
|
|||
// revoking tokens which do not exist is not an error
|
||||
$this->assertFalse(Arsse::$db->tokenRevoke($user, "unknown.class"));
|
||||
}
|
||||
|
||||
public function testListTokens(): void {
|
||||
$user = "jane.doe@example.com";
|
||||
$exp = [
|
||||
['id' => "A", 'data' => "Label 1"],
|
||||
['id' => "B", 'data' => "Label 2"],
|
||||
];
|
||||
$this->assertResult($exp, Arsse::$db->tokenList($user, "miniflux.login"));
|
||||
}
|
||||
}
|
||||
|
|
22
tests/cases/REST/Miniflux/TestErrorResponse.php
Normal file
22
tests/cases/REST/Miniflux/TestErrorResponse.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\TestCase\REST\Miniflux;
|
||||
|
||||
use JKingWeb\Arsse\REST\Miniflux\ErrorResponse;
|
||||
|
||||
/** @covers \JKingWeb\Arsse\REST\Miniflux\ErrorResponse */
|
||||
class TestErrorResponse extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||
public function testCreateConstantResponse(): void {
|
||||
$act = new ErrorResponse("401", 401);
|
||||
$this->assertSame('{"error_message":"Access Unauthorized"}', (string) $act->getBody());
|
||||
}
|
||||
|
||||
public function testCreateVariableResponse(): void {
|
||||
$act = new ErrorResponse(["invalidBodyJSON", "Doh!"], 401);
|
||||
$this->assertSame('{"error_message":"Invalid JSON payload: Doh!"}', (string) $act->getBody());
|
||||
}
|
||||
}
|
34
tests/cases/REST/Miniflux/TestStatus.php
Normal file
34
tests/cases/REST/Miniflux/TestStatus.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\TestCase\REST\Miniflux;
|
||||
|
||||
use JKingWeb\Arsse\REST\Miniflux\Status;
|
||||
use JKingWeb\Arsse\REST\Miniflux\V1;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Laminas\Diactoros\Response\TextResponse;
|
||||
|
||||
/** @covers \JKingWeb\Arsse\REST\Miniflux\Status */
|
||||
class TestStatus extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||
/** @dataProvider provideRequests */
|
||||
public function testAnswerStatusRequests(string $url, string $method, ResponseInterface $exp): void {
|
||||
$act = (new Status)->dispatch($this->serverRequest($method, $url, ""));
|
||||
$this->assertMessage($exp, $act);
|
||||
}
|
||||
|
||||
public function provideRequests(): iterable {
|
||||
return [
|
||||
["/version", "GET", new TextResponse(V1::VERSION)],
|
||||
["/version", "POST", new EmptyResponse(405, ['Allow' => "HEAD, GET"])],
|
||||
["/version", "OPTIONS", new EmptyResponse(204, ['Allow' => "HEAD, GET"])],
|
||||
["/healthcheck", "GET", new TextResponse("OK")],
|
||||
["/healthcheck", "POST", new EmptyResponse(405, ['Allow' => "HEAD, GET"])],
|
||||
["/healthcheck", "OPTIONS", new EmptyResponse(204, ['Allow' => "HEAD, GET"])],
|
||||
["/version/", "GET", new EmptyResponse(404)],
|
||||
];
|
||||
}
|
||||
}
|
118
tests/cases/REST/Miniflux/TestV1.php
Normal file
118
tests/cases/REST/Miniflux/TestV1.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\TestCase\REST\NextcloudNews;
|
||||
|
||||
use JKingWeb\Arsse\Arsse;
|
||||
use JKingWeb\Arsse\User;
|
||||
use JKingWeb\Arsse\Database;
|
||||
use JKingWeb\Arsse\Db\Transaction;
|
||||
use JKingWeb\Arsse\REST\Miniflux\V1;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/** @covers \JKingWeb\Arsse\REST\Miniflux\V1<extended> */
|
||||
class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||
protected $h;
|
||||
protected $transaction;
|
||||
|
||||
protected function req(string $method, string $target, $data = "", array $headers = [], bool $authenticated = true, bool $body = true): ResponseInterface {
|
||||
$prefix = "/v1";
|
||||
$url = $prefix.$target;
|
||||
if ($body) {
|
||||
$params = [];
|
||||
} else {
|
||||
$params = $data;
|
||||
$data = [];
|
||||
}
|
||||
$req = $this->serverRequest($method, $url, $prefix, $headers, [], $data, "application/json", $params, $authenticated ? "john.doe@example.com" : "");
|
||||
return $this->h->dispatch($req);
|
||||
}
|
||||
|
||||
public function setUp(): void {
|
||||
self::clearData();
|
||||
self::setConf();
|
||||
// create a mock user manager
|
||||
Arsse::$user = \Phake::mock(User::class);
|
||||
Arsse::$user->id = "john.doe@example.com";
|
||||
// create a mock database interface
|
||||
Arsse::$db = \Phake::mock(Database::class);
|
||||
$this->transaction = \Phake::mock(Transaction::class);
|
||||
\Phake::when(Arsse::$db)->begin->thenReturn($this->transaction);
|
||||
//initialize a handler
|
||||
$this->h = new V1();
|
||||
}
|
||||
|
||||
public function tearDown(): void {
|
||||
self::clearData();
|
||||
}
|
||||
|
||||
protected function v($value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function testSendAuthenticationChallenge(): void {
|
||||
$exp = new EmptyResponse(401);
|
||||
$this->assertMessage($exp, $this->req("GET", "/", "", [], false));
|
||||
}
|
||||
|
||||
/** @dataProvider provideInvalidPaths */
|
||||
public function testRespondToInvalidPaths($path, $method, $code, $allow = null): void {
|
||||
$exp = new EmptyResponse($code, $allow ? ['Allow' => $allow] : []);
|
||||
$this->assertMessage($exp, $this->req($method, $path));
|
||||
}
|
||||
|
||||
public function provideInvalidPaths(): array {
|
||||
return [
|
||||
["/", "GET", 404],
|
||||
["/", "POST", 404],
|
||||
["/", "PUT", 404],
|
||||
["/", "DELETE", 404],
|
||||
["/", "OPTIONS", 404],
|
||||
["/version/invalid", "GET", 404],
|
||||
["/version/invalid", "POST", 404],
|
||||
["/version/invalid", "PUT", 404],
|
||||
["/version/invalid", "DELETE", 404],
|
||||
["/version/invalid", "OPTIONS", 404],
|
||||
["/folders/1/invalid", "GET", 404],
|
||||
["/folders/1/invalid", "POST", 404],
|
||||
["/folders/1/invalid", "PUT", 404],
|
||||
["/folders/1/invalid", "DELETE", 404],
|
||||
["/folders/1/invalid", "OPTIONS", 404],
|
||||
["/version", "POST", 405, "GET"],
|
||||
["/version", "PUT", 405, "GET"],
|
||||
["/version", "DELETE", 405, "GET"],
|
||||
["/folders", "PUT", 405, "GET, POST"],
|
||||
["/folders", "DELETE", 405, "GET, POST"],
|
||||
["/folders/1", "GET", 405, "PUT, DELETE"],
|
||||
["/folders/1", "POST", 405, "PUT, DELETE"],
|
||||
];
|
||||
}
|
||||
|
||||
public function testRespondToInvalidInputTypes(): void {
|
||||
$exp = new EmptyResponse(415, ['Accept' => "application/json"]);
|
||||
$this->assertMessage($exp, $this->req("PUT", "/folders/1", '<data/>', ['Content-Type' => "application/xml"]));
|
||||
$exp = new EmptyResponse(400);
|
||||
$this->assertMessage($exp, $this->req("PUT", "/folders/1", '<data/>'));
|
||||
$this->assertMessage($exp, $this->req("PUT", "/folders/1", '<data/>', ['Content-Type' => null]));
|
||||
}
|
||||
|
||||
/** @dataProvider provideOptionsRequests */
|
||||
public function testRespondToOptionsRequests(string $url, string $allow, string $accept): void {
|
||||
$exp = new EmptyResponse(204, [
|
||||
'Allow' => $allow,
|
||||
'Accept' => $accept,
|
||||
]);
|
||||
$this->assertMessage($exp, $this->req("OPTIONS", $url));
|
||||
}
|
||||
|
||||
public function provideOptionsRequests(): array {
|
||||
return [
|
||||
["/feeds", "HEAD,GET,POST", "application/json"],
|
||||
["/feeds/2112", "DELETE", "application/json"],
|
||||
["/user", "HEAD,GET", "application/json"],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ namespace JKingWeb\Arsse\TestCase\User;
|
|||
use JKingWeb\Arsse\Arsse;
|
||||
use JKingWeb\Arsse\Database;
|
||||
use JKingWeb\Arsse\User;
|
||||
use JKingWeb\Arsse\AbstractException as Exception;
|
||||
use JKingWeb\Arsse\User\ExceptionConflict;
|
||||
use JKingWeb\Arsse\User\ExceptionInput;
|
||||
use JKingWeb\Arsse\User\Driver;
|
||||
|
@ -25,7 +24,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
// create a mock user driver
|
||||
$this->drv = \Phake::mock(Driver::class);
|
||||
}
|
||||
|
||||
|
||||
public function tearDown(): void {
|
||||
\Phake::verifyNoOtherInteractions($this->drv);
|
||||
\Phake::verifyNoOtherInteractions(Arsse::$db);
|
||||
|
@ -43,7 +42,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
$u->id = null;
|
||||
$this->assertSame("", (string) $u);
|
||||
}
|
||||
|
||||
|
||||
public function testGeneratePasswords(): void {
|
||||
$u = new User($this->drv);
|
||||
$pass1 = $u->generatePassword();
|
||||
|
@ -166,7 +165,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
\Phake::verify($this->drv)->userAdd($user, $pass);
|
||||
\Phake::verify(Arsse::$db)->userExists($user);
|
||||
}
|
||||
|
||||
|
||||
public function testRemoveAUser(): void {
|
||||
$user = "john.doe@example.com";
|
||||
$pass = "secret";
|
||||
|
@ -178,7 +177,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
\Phake::verify(Arsse::$db)->userRemove($user);
|
||||
\Phake::verify($this->drv)->userRemove($user);
|
||||
}
|
||||
|
||||
|
||||
public function testRemoveAUserWeDoNotKnow(): void {
|
||||
$user = "john.doe@example.com";
|
||||
$pass = "secret";
|
||||
|
@ -189,7 +188,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
\Phake::verify(Arsse::$db)->userExists($user);
|
||||
\Phake::verify($this->drv)->userRemove($user);
|
||||
}
|
||||
|
||||
|
||||
public function testRemoveAMissingUser(): void {
|
||||
$user = "john.doe@example.com";
|
||||
$pass = "secret";
|
||||
|
@ -205,7 +204,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
\Phake::verify($this->drv)->userRemove($user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function testRemoveAMissingUserWeDoNotKnow(): void {
|
||||
$user = "john.doe@example.com";
|
||||
$pass = "secret";
|
||||
|
@ -381,7 +380,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
\Phake::verify($this->drv)->userPropertiesGet($user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @dataProvider providePropertyChanges */
|
||||
public function testSetThePropertiesOfAUser(array $in, $out): void {
|
||||
$user = "john.doe@example.com";
|
||||
|
@ -399,7 +398,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
\Phake::verify(Arsse::$db)->userExists($user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @dataProvider providePropertyChanges */
|
||||
public function testSetThePropertiesOfAUserWeDoNotKnow(array $in, $out): void {
|
||||
$user = "john.doe@example.com";
|
||||
|
@ -418,7 +417,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
\Phake::verify(Arsse::$db)->userAdd($user, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function providePropertyChanges(): iterable {
|
||||
return [
|
||||
[['admin' => true], ['admin' => true]],
|
||||
|
@ -431,7 +430,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
|
|||
[['lang' => null], ['lang' => null]],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function testSetThePropertiesOfAMissingUser(): void {
|
||||
$user = "john.doe@example.com";
|
||||
$in = ['admin' => true];
|
||||
|
|
|
@ -112,10 +112,14 @@
|
|||
<testsuite name="REST">
|
||||
<file>cases/REST/TestREST.php</file>
|
||||
</testsuite>
|
||||
<testsuite name="Miniflux">
|
||||
<file>cases/REST/Miniflux/TestErrorResponse.php</file>
|
||||
<file>cases/REST/Miniflux/TestStatus.php</file>
|
||||
</testsuite>
|
||||
<testsuite name="NCNv1">
|
||||
<file>cases/REST/NextcloudNews/TestVersions.php</file>
|
||||
<file>cases/REST/NextcloudNews/TestV1_2.php</file>
|
||||
<file>cases/REST/NextcloudNews/PDO/TestV1_2.php</file>
|
||||
<file>cases/REST/NextcloudNews/PDO/TestV1_2.php</file>
|
||||
</testsuite>
|
||||
<testsuite name="TTRSS">
|
||||
<file>cases/REST/TinyTinyRSS/TestSearch.php</file>
|
||||
|
|
Loading…
Reference in a new issue