mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Complete testing of TTRSS handler
Also implemented OPTIONS handling for TTRSS; improves #107
This commit is contained in:
parent
a61aa0a22c
commit
b820a004d6
2 changed files with 62 additions and 22 deletions
|
@ -109,26 +109,25 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
const FATAL_ERR = [
|
const FATAL_ERR = [
|
||||||
'seq' => null,
|
'seq' => null,
|
||||||
'status' => 1,
|
'status' => 1,
|
||||||
'content' => ['error' => "NOT_LOGGED_IN"],
|
'content' => ['error' => "MALFORMED_INPUT"],
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
|
public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
|
||||||
if ($req->method != "POST") {
|
if ($req->method=="OPTIONS") {
|
||||||
// only POST requests are allowed
|
// respond to OPTIONS rquests; the response is a fib, as we technically accept any type or method
|
||||||
return new Response(405, self::FATAL_ERR, "application/json", ["Allow: POST"]);
|
return new Response(204, "", "", [
|
||||||
|
"Allow: POST",
|
||||||
|
"Accept: application/json, text/json",
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
if ($req->body) {
|
if ($req->body) {
|
||||||
// only JSON entities are allowed
|
// only JSON entities are allowed, but Content-Type is ignored, as is request method
|
||||||
if (!preg_match("<^application/json\b|^$>", $req->type)) {
|
|
||||||
return new Response(415, self::FATAL_ERR, "application/json", ['Accept: application/json']);
|
|
||||||
}
|
|
||||||
$data = @json_decode($req->body, true);
|
$data = @json_decode($req->body, true);
|
||||||
if (json_last_error() != \JSON_ERROR_NONE || !is_array($data)) {
|
if (json_last_error() != \JSON_ERROR_NONE || !is_array($data)) {
|
||||||
// non-JSON input indicates an error
|
return new Response(200, self::FATAL_ERR);
|
||||||
return new Response(400, self::FATAL_ERR);
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// normalize input
|
// normalize input
|
||||||
|
@ -144,16 +143,8 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
$method = "op".ucfirst($data['op']);
|
$method = "op".ucfirst($data['op']);
|
||||||
if (!method_exists($this, $method)) {
|
if (!method_exists($this, $method)) {
|
||||||
// because method names are supposed to be case insensitive, we need to try a bit harder to match
|
// TT-RSS operations are case-insensitive by dint of PHP method names being case-insensitive; this will only trigger if the method really doesn't exist
|
||||||
$method = strtolower($method);
|
throw new Exception("UNKNOWN_METHOD", ['method' => $data['op']]);
|
||||||
$map = get_class_methods($this);
|
|
||||||
$map = array_combine(array_map("strtolower", $map), $map);
|
|
||||||
if (!array_key_exists($method, $map)) {
|
|
||||||
// if the method really doesn't exist, throw an exception
|
|
||||||
throw new Exception("UNKNWON_METHOD", ['method' => $data['op']]);
|
|
||||||
}
|
|
||||||
// otherwise retrieve the correct camelCase and continue
|
|
||||||
$method = $map[$method];
|
|
||||||
}
|
}
|
||||||
return new Response(200, [
|
return new Response(200, [
|
||||||
'seq' => $data['seq'],
|
'seq' => $data['seq'],
|
||||||
|
@ -171,7 +162,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// absence of a request body indicates an error
|
// absence of a request body indicates an error
|
||||||
return new Response(400, self::FATAL_ERR);
|
return new Response(200, self::FATAL_ERR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1245,7 +1236,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
|
||||||
public function opGetHeadlines(array $data): array {
|
public function opGetHeadlines(array $data): array {
|
||||||
// normalize input
|
// normalize input
|
||||||
$data['limit'] = max(min(!$data['limit'] ? 200 : $data['limit'], 200), 0); // at most 200; not specified/zero yields 200; negative values yield no limit
|
$data['limit'] = max(min(!$data['limit'] ? self::LIMIT_ARTICLES : $data['limit'], self::LIMIT_ARTICLES), 0); // at most 200; not specified/zero yields 200; negative values yield no limit
|
||||||
$tr = Arsse::$db->begin();
|
$tr = Arsse::$db->begin();
|
||||||
// retrieve the list of label names for the user
|
// retrieve the list of label names for the user
|
||||||
$labels = [];
|
$labels = [];
|
||||||
|
|
|
@ -176,6 +176,20 @@ LONG_STRING;
|
||||||
$this->clearData();
|
$this->clearData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHandleOptionsRequest() {
|
||||||
|
$exp = new Response(204, "", "", [
|
||||||
|
"Allow: POST",
|
||||||
|
"Accept: application/json, text/json",
|
||||||
|
]);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("OPTIONS", "")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleInvalidData() {
|
||||||
|
$exp = $this->RESPERR("MALFORMED_INPUT");
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", "This is not valid JSON data")));
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", ""))); // lack of data is also an error
|
||||||
|
}
|
||||||
|
|
||||||
public function testLogIn() {
|
public function testLogIn() {
|
||||||
Phake::when(Arsse::$user)->auth(Arsse::$user->id, "superman")->thenReturn(false);
|
Phake::when(Arsse::$user)->auth(Arsse::$user->id, "superman")->thenReturn(false);
|
||||||
Phake::when(Arsse::$db)->sessionCreate->thenReturn("PriestsOfSyrinx")->thenReturn("SolarFederation");
|
Phake::when(Arsse::$db)->sessionCreate->thenReturn("PriestsOfSyrinx")->thenReturn("SolarFederation");
|
||||||
|
@ -196,6 +210,17 @@ LONG_STRING;
|
||||||
Phake::verify(Arsse::$db, Phake::times(0))->sessionResume($this->anything());
|
Phake::verify(Arsse::$db, Phake::times(0))->sessionResume($this->anything());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHandleGenericError() {
|
||||||
|
Phake::when(Arsse::$user)->auth(Arsse::$user->id, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionTimeout("general"));
|
||||||
|
$data = [
|
||||||
|
'op' => "login",
|
||||||
|
'user' => Arsse::$user->id,
|
||||||
|
'password' => "secret",
|
||||||
|
];
|
||||||
|
$exp = new Response(500);
|
||||||
|
$this->assertEquals($exp, $this->req($data));
|
||||||
|
}
|
||||||
|
|
||||||
public function testLogOut() {
|
public function testLogOut() {
|
||||||
Phake::when(Arsse::$db)->sessionDestroy->thenReturn(true);
|
Phake::when(Arsse::$db)->sessionDestroy->thenReturn(true);
|
||||||
$data = [
|
$data = [
|
||||||
|
@ -219,6 +244,30 @@ LONG_STRING;
|
||||||
$this->assertEquals($exp, $this->req($data));
|
$this->assertEquals($exp, $this->req($data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHandleUnknownMethods() {
|
||||||
|
$exp = $this->respErr("UNKNOWN_METHOD", ['method' => "thisMethodDoesNotExist"]);
|
||||||
|
$data = [
|
||||||
|
'op' => "thisMethodDoesNotExist",
|
||||||
|
'sid' => "PriestsOfSyrinx",
|
||||||
|
];
|
||||||
|
$this->assertEquals($exp, $this->req($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleMixedCaseMethods() {
|
||||||
|
$data = [
|
||||||
|
'op' => "isLoggedIn",
|
||||||
|
'sid' => "PriestsOfSyrinx",
|
||||||
|
];
|
||||||
|
$exp = $this->respGood(['status' => true]);
|
||||||
|
$this->assertEquals($exp, $this->req($data));
|
||||||
|
$data['op'] = "isloggedin";
|
||||||
|
$this->assertEquals($exp, $this->req($data));
|
||||||
|
$data['op'] = "ISLOGGEDIN";
|
||||||
|
$this->assertEquals($exp, $this->req($data));
|
||||||
|
$data['op'] = "iSlOgGeDiN";
|
||||||
|
$this->assertEquals($exp, $this->req($data));
|
||||||
|
}
|
||||||
|
|
||||||
public function testRetrieveServerVersion() {
|
public function testRetrieveServerVersion() {
|
||||||
$data = [
|
$data = [
|
||||||
'op' => "getVersion",
|
'op' => "getVersion",
|
||||||
|
|
Loading…
Reference in a new issue