mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Tests and fixed for Fever feeds and groups
This commit is contained in:
parent
7faec3b0db
commit
de615c671a
2 changed files with 96 additions and 28 deletions
|
@ -30,8 +30,8 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dispatch(ServerRequestInterface $req): ResponseInterface {
|
public function dispatch(ServerRequestInterface $req): ResponseInterface {
|
||||||
$inR = $req->getQueryParams();
|
$inR = $req->getQueryParams() ?? [];
|
||||||
$inW = $req->getParsedBody();
|
$inW = $req->getParsedBody() ?? [];
|
||||||
if (!array_key_exists("api", $inR)) {
|
if (!array_key_exists("api", $inR)) {
|
||||||
// the original would have shown the Fever UI in the absence of the "api" parameter, but we'll return 404
|
// the original would have shown the Fever UI in the absence of the "api" parameter, but we'll return 404
|
||||||
return new EmptyResponse(404);
|
return new EmptyResponse(404);
|
||||||
|
@ -57,14 +57,13 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
// otherwise if HTTP authentication failed or is required, deny access at the HTTP level
|
// otherwise if HTTP authentication failed or is required, deny access at the HTTP level
|
||||||
return new EmptyResponse(401);
|
return new EmptyResponse(401);
|
||||||
}
|
}
|
||||||
// check that the user specified credentials
|
// produce a full response if authenticated or a basic response otherwise
|
||||||
if ($this->logIn(strtolower($inW['api_key'] ?? ""))) {
|
if ($this->logIn(strtolower($inW['api_key'] ?? ""))) {
|
||||||
$out['auth'] = 1;
|
$out = $this->processRequest($this->baseResponse(true), $inR, $inW);
|
||||||
$out = $this->processRequest($out, $inR, $inW);
|
|
||||||
} else {
|
} else {
|
||||||
$out['auth'] = 0;
|
$out = $this->baseResponse(false);
|
||||||
}
|
}
|
||||||
// return the result
|
// return the result, possibly formatted as XML
|
||||||
return $this->formatResponse($out, $xml);
|
return $this->formatResponse($out, $xml);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -73,9 +72,6 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function processRequest(array $out, array $G, array $P): array {
|
protected function processRequest(array $out, array $G, array $P): array {
|
||||||
// add base metadata
|
|
||||||
$out['last_refreshed_on_time'] = Date::transform(Arsse::$db->subscriptionRefreshed(Arsse::$user->id), "unix");
|
|
||||||
// handle each possible parameter
|
|
||||||
if (array_key_exists("feeds", $G) || array_key_exists("groups", $G)) {
|
if (array_key_exists("feeds", $G) || array_key_exists("groups", $G)) {
|
||||||
if (array_key_exists("groups", $G)) {
|
if (array_key_exists("groups", $G)) {
|
||||||
$out['groups'] = $this->getGroups();
|
$out['groups'] = $this->getGroups();
|
||||||
|
@ -91,6 +87,18 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function baseResponse(bool $authenticated): array {
|
||||||
|
$out = [
|
||||||
|
'api_version' => self::LEVEL,
|
||||||
|
'auth' => (int) $authenticated,
|
||||||
|
];
|
||||||
|
if ($authenticated) {
|
||||||
|
// authenticated requests always include the most recent feed refresh
|
||||||
|
$out['last_refreshed_on_time'] = $this->getRefreshTime();
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
protected function formatResponse(array $data, bool $xml): ResponseInterface {
|
protected function formatResponse(array $data, bool $xml): ResponseInterface {
|
||||||
if ($xml) {
|
if ($xml) {
|
||||||
throw \Exception("Not implemented yet");
|
throw \Exception("Not implemented yet");
|
||||||
|
@ -115,17 +123,21 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getRefreshTime() {
|
||||||
|
return Date::transform(Arsse::$db->subscriptionRefreshed(Arsse::$user->id), "unix");
|
||||||
|
}
|
||||||
|
|
||||||
protected function getFeeds(): array {
|
protected function getFeeds(): array {
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach (arsse::$db->subscriptionList(Arsse::$user->id) as $sub) {
|
foreach (arsse::$db->subscriptionList(Arsse::$user->id) as $sub) {
|
||||||
$out[] = [
|
$out[] = [
|
||||||
'id' => (int) $sub['id'],
|
'id' => (int) $sub['id'],
|
||||||
'favicon_id' => (int) ($sub['favicon'] ? $sub['feed'] : 0),
|
'favicon_id' => (int) ($sub['favicon'] ? $sub['feed'] : 0),
|
||||||
'title' => (string) $sub['title'],
|
'title' => (string) $sub['title'],
|
||||||
'url' => $sub['url'],
|
'url' => $sub['url'],
|
||||||
'site_url' => $sub['source'],
|
'site_url' => $sub['source'],
|
||||||
'is_spark' => 0,
|
'is_spark' => 0,
|
||||||
'lat_updated_on_time' => Date::transform($sub['edited'], "unix", "sql"),
|
'last_updated_on_time' => Date::transform($sub['edited'], "unix", "sql"),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return $out;
|
return $out;
|
||||||
|
|
|
@ -31,7 +31,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function req($dataGet, $dataPost, string $method = "POST", string $type = null, string $url = "", string $user = null): ResponseInterface {
|
protected function req($dataGet, $dataPost = "", string $method = "POST", string $type = null, string $url = "", string $user = null): ResponseInterface {
|
||||||
$url = "/fever/".$url;
|
$url = "/fever/".$url;
|
||||||
$server = [
|
$server = [
|
||||||
'REQUEST_METHOD' => $method,
|
'REQUEST_METHOD' => $method,
|
||||||
|
@ -39,11 +39,10 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
'HTTP_CONTENT_TYPE' => $type ?? "application/x-www-form-urlencoded",
|
'HTTP_CONTENT_TYPE' => $type ?? "application/x-www-form-urlencoded",
|
||||||
];
|
];
|
||||||
$req = new ServerRequest($server, [], $url, $method, "php://memory");
|
$req = new ServerRequest($server, [], $url, $method, "php://memory");
|
||||||
if (is_array($dataGet)) {
|
if (!is_array($dataGet)) {
|
||||||
$req = $req->withRequestTarget($url)->withQueryParams($dataGet);
|
parse_str($dataGet, $dataGet);
|
||||||
} else {
|
|
||||||
$req = $req->withRequestTarget($url."?".http_build_query((string) $dataGet, "", "&", \PHP_QUERY_RFC3986));
|
|
||||||
}
|
}
|
||||||
|
$req = $req->withRequestTarget($url)->withQueryParams($dataGet);
|
||||||
if (is_array($dataPost)) {
|
if (is_array($dataPost)) {
|
||||||
$req = $req->withParsedBody($dataPost);
|
$req = $req->withParsedBody($dataPost);
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,8 +71,9 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
Arsse::$db = \Phake::mock(Database::class);
|
Arsse::$db = \Phake::mock(Database::class);
|
||||||
\Phake::when(Arsse::$db)->begin->thenReturn(\Phake::mock(Transaction::class));
|
\Phake::when(Arsse::$db)->begin->thenReturn(\Phake::mock(Transaction::class));
|
||||||
\Phake::when(Arsse::$db)->tokenLookup->thenReturn(['user' => "john.doe@example.com"]);
|
\Phake::when(Arsse::$db)->tokenLookup->thenReturn(['user' => "john.doe@example.com"]);
|
||||||
// instantiate the handler
|
// instantiate the handler as a partial mock to simplify testing
|
||||||
$this->h = new API();
|
$this->h = \Phake::partialMock(API::class);
|
||||||
|
\Phake::when($this->h)->baseResponse->thenReturn([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
|
@ -89,8 +89,10 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
Arsse::$user->id = null;
|
Arsse::$user->id = null;
|
||||||
\Phake::when(Arsse::$db)->tokenLookup->thenThrow(new ExceptionInput("subjectMissing"));
|
\Phake::when(Arsse::$db)->tokenLookup->thenThrow(new ExceptionInput("subjectMissing"));
|
||||||
\Phake::when(Arsse::$db)->tokenLookup("fever.login", "validtoken")->thenReturn(['user' => "jane.doe@example.com"]);
|
\Phake::when(Arsse::$db)->tokenLookup("fever.login", "validtoken")->thenReturn(['user' => "jane.doe@example.com"]);
|
||||||
// use a partial mock to test only the authentication process
|
// test only the authentication process
|
||||||
$this->h = \Phake::partialMock(API::class);
|
\Phake::when($this->h)->baseResponse->thenReturnCallback(function(bool $authenticated) {
|
||||||
|
return ['auth' => (int) $authenticated];
|
||||||
|
});
|
||||||
\Phake::when($this->h)->processRequest->thenReturnCallback(function($out, $G, $P) {
|
\Phake::when($this->h)->processRequest->thenReturnCallback(function($out, $G, $P) {
|
||||||
return $out;
|
return $out;
|
||||||
});
|
});
|
||||||
|
@ -99,8 +101,8 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideTokenAuthenticationRequests() {
|
public function provideTokenAuthenticationRequests() {
|
||||||
$success = new JsonResponse(['api_version' => API::LEVEL, 'auth' => 1]);
|
$success = new JsonResponse(['auth' => 1]);
|
||||||
$failure = new JsonResponse(['api_version' => API::LEVEL, 'auth' => 0]);
|
$failure = new JsonResponse(['auth' => 0]);
|
||||||
$denied = new EmptyResponse(401);
|
$denied = new EmptyResponse(401);
|
||||||
return [
|
return [
|
||||||
[false, true, null, [], ['api' => null], $failure],
|
[false, true, null, [], ['api' => null], $failure],
|
||||||
|
@ -149,4 +151,58 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
[true, false, "validUser", ['api_key' => "invalidToken"], ['api' => null], $success],
|
[true, false, "validUser", ['api_key' => "invalidToken"], ['api' => null], $success],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testListGroups() {
|
||||||
|
\Phake::when(Arsse::$db)->tagList(Arsse::$user->id)->thenReturn(new Result([
|
||||||
|
['id' => 1, 'name' => "Fascinating", 'subscriptions' => 2],
|
||||||
|
['id' => 2, 'name' => "Interesting", 'subscriptions' => 2],
|
||||||
|
['id' => 3, 'name' => "Boring", 'subscriptions' => 0],
|
||||||
|
]));
|
||||||
|
\Phake::when(Arsse::$db)->tagSummarize(Arsse::$user->id)->thenReturn(new Result([
|
||||||
|
['id' => 1, 'name' => "Fascinating", 'subscription' => 1],
|
||||||
|
['id' => 1, 'name' => "Fascinating", 'subscription' => 2],
|
||||||
|
['id' => 2, 'name' => "Interesting", 'subscription' => 1],
|
||||||
|
['id' => 2, 'name' => "Interesting", 'subscription' => 3],
|
||||||
|
]));
|
||||||
|
$exp = new JsonResponse([
|
||||||
|
'groups' => [
|
||||||
|
['id' => 1, 'title' => "Fascinating"],
|
||||||
|
['id' => 2, 'title' => "Interesting"],
|
||||||
|
['id' => 3, 'title' => "Boring"],
|
||||||
|
],
|
||||||
|
'feeds_groups' => [
|
||||||
|
['group_id' => 1, 'feed_ids' => "1,2"],
|
||||||
|
['group_id' => 2, 'feed_ids' => "1,3"],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$act = $this->req("api&groups");
|
||||||
|
$this->assertMessage($exp, $act);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testListFeeds() {
|
||||||
|
\Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([
|
||||||
|
['id' => 1, 'feed' => 5, 'title' => "Ankh-Morpork News", 'url' => "http://example.com/feed", 'source' => "http://example.com/", 'edited' => "2019-01-01 21:12:00", 'favicon' => "http://example.com/favicon.ico"],
|
||||||
|
['id' => 2, 'feed' => 9, 'title' => "Ook, Ook Eek Ook!", 'url' => "http://example.net/feed", 'source' => "http://example.net/", 'edited' => "1988-06-24 12:21:00", 'favicon' => ""],
|
||||||
|
['id' => 3, 'feed' => 1, 'title' => "The Last Soul", 'url' => "http://example.org/feed", 'source' => "http://example.org/", 'edited' => "1991-08-12 03:22:00", 'favicon' => "http://example.org/favicon.ico"],
|
||||||
|
]));
|
||||||
|
\Phake::when(Arsse::$db)->tagSummarize(Arsse::$user->id)->thenReturn(new Result([
|
||||||
|
['id' => 1, 'name' => "Fascinating", 'subscription' => 1],
|
||||||
|
['id' => 1, 'name' => "Fascinating", 'subscription' => 2],
|
||||||
|
['id' => 2, 'name' => "Interesting", 'subscription' => 1],
|
||||||
|
['id' => 2, 'name' => "Interesting", 'subscription' => 3],
|
||||||
|
]));
|
||||||
|
$exp = new JsonResponse([
|
||||||
|
'feeds' => [
|
||||||
|
['id' => 1, 'favicon_id' => 5, 'title' => "Ankh-Morpork News", 'url' => "http://example.com/feed", 'site_url' => "http://example.com/", 'is_spark' => 0, 'last_updated_on_time' => strtotime("2019-01-01T21:12:00Z")],
|
||||||
|
['id' => 2, 'favicon_id' => 0, 'title' => "Ook, Ook Eek Ook!", 'url' => "http://example.net/feed", 'site_url' => "http://example.net/", 'is_spark' => 0, 'last_updated_on_time' => strtotime("1988-06-24T12:21:00Z")],
|
||||||
|
['id' => 3, 'favicon_id' => 1, 'title' => "The Last Soul", 'url' => "http://example.org/feed", 'site_url' => "http://example.org/", 'is_spark' => 0, 'last_updated_on_time' => strtotime("1991-08-12T03:22:00Z")],
|
||||||
|
],
|
||||||
|
'feeds_groups' => [
|
||||||
|
['group_id' => 1, 'feed_ids' => "1,2"],
|
||||||
|
['group_id' => 2, 'feed_ids' => "1,3"],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$act = $this->req("api&feeds");
|
||||||
|
$this->assertMessage($exp, $act);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue