2019-03-19 02:49:47 +00:00
< ? 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\Fever ;
use JKingWeb\Arsse\Arsse ;
use JKingWeb\Arsse\User ;
use JKingWeb\Arsse\Database ;
use JKingWeb\Arsse\Test\Result ;
use JKingWeb\Arsse\Context\Context ;
use JKingWeb\Arsse\Db\ExceptionInput ;
use JKingWeb\Arsse\Db\Transaction ;
use JKingWeb\Arsse\REST\Fever\API ;
use Psr\Http\Message\ResponseInterface ;
2020-01-20 15:40:05 +00:00
use Laminas\Diactoros\ServerRequest ;
use Laminas\Diactoros\Response\JsonResponse ;
use Laminas\Diactoros\Response\XmlResponse ;
use Laminas\Diactoros\Response\EmptyResponse ;
2019-03-19 02:49:47 +00:00
/** @covers \JKingWeb\Arsse\REST\Fever\API<extended> */
class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
2019-07-24 13:10:13 +00:00
/** @var \JKingWeb\Arsse\REST\Fever\API */
protected $h ;
2021-02-27 20:24:02 +00:00
protected $hMock ;
protected $userId = " john.doe@example.com " ;
2019-04-08 22:41:56 +00:00
protected $articles = [
'db' => [
[
2020-03-01 20:16:50 +00:00
'id' => 101 ,
'url' => 'http://example.com/1' ,
'title' => 'Article title 1' ,
'author' => '' ,
'content' => '<p>Article content 1</p>' ,
2019-04-08 22:41:56 +00:00
'published_date' => '2000-01-01 00:00:00' ,
2020-03-01 20:16:50 +00:00
'unread' => 1 ,
'starred' => 0 ,
'subscription' => 8 ,
2019-04-08 22:41:56 +00:00
],
[
2020-03-01 20:16:50 +00:00
'id' => 102 ,
'url' => 'http://example.com/2' ,
'title' => 'Article title 2' ,
'author' => '' ,
'content' => '<p>Article content 2</p>' ,
2019-04-08 22:41:56 +00:00
'published_date' => '2000-01-02 00:00:00' ,
2020-03-01 20:16:50 +00:00
'unread' => 0 ,
'starred' => 0 ,
'subscription' => 8 ,
2019-04-08 22:41:56 +00:00
],
[
2020-03-01 20:16:50 +00:00
'id' => 103 ,
'url' => 'http://example.com/3' ,
'title' => 'Article title 3' ,
'author' => '' ,
'content' => '<p>Article content 3</p>' ,
2019-04-08 22:41:56 +00:00
'published_date' => '2000-01-03 00:00:00' ,
2020-03-01 20:16:50 +00:00
'unread' => 1 ,
'starred' => 1 ,
'subscription' => 9 ,
2019-04-08 22:41:56 +00:00
],
[
2020-03-01 20:16:50 +00:00
'id' => 104 ,
'url' => 'http://example.com/4' ,
'title' => 'Article title 4' ,
'author' => '' ,
'content' => '<p>Article content 4</p>' ,
2019-04-08 22:41:56 +00:00
'published_date' => '2000-01-04 00:00:00' ,
2020-03-01 20:16:50 +00:00
'unread' => 0 ,
'starred' => 1 ,
'subscription' => 9 ,
2019-04-08 22:41:56 +00:00
],
[
2020-03-01 20:16:50 +00:00
'id' => 105 ,
'url' => 'http://example.com/5' ,
'title' => 'Article title 5' ,
'author' => '' ,
'content' => '<p>Article content 5</p>' ,
2019-04-08 22:41:56 +00:00
'published_date' => '2000-01-05 00:00:00' ,
2020-03-01 20:16:50 +00:00
'unread' => 1 ,
'starred' => 0 ,
'subscription' => 10 ,
2019-04-08 22:41:56 +00:00
],
],
'rest' => [
[
2020-03-01 20:16:50 +00:00
'id' => 101 ,
'feed_id' => 8 ,
'title' => 'Article title 1' ,
'author' => '' ,
'html' => '<p>Article content 1</p>' ,
'url' => 'http://example.com/1' ,
'is_saved' => 0 ,
'is_read' => 0 ,
2019-04-08 22:41:56 +00:00
'created_on_time' => 946684800 ,
],
[
2020-03-01 20:16:50 +00:00
'id' => 102 ,
'feed_id' => 8 ,
'title' => 'Article title 2' ,
'author' => '' ,
'html' => '<p>Article content 2</p>' ,
'url' => 'http://example.com/2' ,
'is_saved' => 0 ,
'is_read' => 1 ,
2019-04-08 22:41:56 +00:00
'created_on_time' => 946771200 ,
],
[
2020-03-01 20:16:50 +00:00
'id' => 103 ,
'feed_id' => 9 ,
'title' => 'Article title 3' ,
'author' => '' ,
'html' => '<p>Article content 3</p>' ,
'url' => 'http://example.com/3' ,
'is_saved' => 1 ,
'is_read' => 0 ,
2019-04-08 22:41:56 +00:00
'created_on_time' => 946857600 ,
],
[
2020-03-01 20:16:50 +00:00
'id' => 104 ,
'feed_id' => 9 ,
'title' => 'Article title 4' ,
'author' => '' ,
'html' => '<p>Article content 4</p>' ,
'url' => 'http://example.com/4' ,
'is_saved' => 1 ,
'is_read' => 1 ,
2019-04-08 22:41:56 +00:00
'created_on_time' => 946944000 ,
],
[
2020-03-01 20:16:50 +00:00
'id' => 105 ,
'feed_id' => 10 ,
'title' => 'Article title 5' ,
'author' => '' ,
'html' => '<p>Article content 5</p>' ,
'url' => 'http://example.com/5' ,
'is_saved' => 0 ,
'is_read' => 0 ,
2019-04-08 22:41:56 +00:00
'created_on_time' => 947030400 ,
],
],
];
2021-02-27 20:24:02 +00:00
2019-03-19 02:49:47 +00:00
protected function v ( $value ) {
return $value ;
}
2021-02-27 20:24:02 +00:00
protected function req ( $dataGet , $dataPost = " " , string $method = " POST " , ? string $type = null , string $target = " " , ? string $user = null ) : ResponseInterface {
Arsse :: $db = $this -> dbMock -> get ();
$this -> h = $this -> hMock -> get ();
2019-09-25 22:30:53 +00:00
$prefix = " /fever/ " ;
$url = $prefix . $target ;
2019-04-10 19:07:34 +00:00
$type = $type ? ? " application/x-www-form-urlencoded " ;
2021-02-27 20:24:02 +00:00
return $this -> h -> dispatch ( $this -> serverRequest ( $method , $url , $prefix , [], [], $dataPost , $type , $dataGet , $user ));
2019-03-19 02:49:47 +00:00
}
2019-10-16 18:42:43 +00:00
public function setUp () : void {
2019-03-19 02:49:47 +00:00
self :: clearData ();
self :: setConf ();
// create a mock user manager
2021-02-27 20:24:02 +00:00
$this -> userMock = $this -> mock ( User :: class );
$this -> userMock -> auth -> returns ( true );
Arsse :: $user = $this -> userMock -> get ();
Arsse :: $user -> id = $this -> userId ;
2019-03-19 02:49:47 +00:00
// create a mock database interface
2021-02-27 20:24:02 +00:00
$this -> dbMock = $this -> mock ( Database :: class );
$this -> dbMock -> begin -> returns ( $this -> mock ( Transaction :: class ));
$this -> dbMock -> tokenLookup -> returns ([ 'user' => " john.doe@example.com " ]);
2019-03-27 19:09:04 +00:00
// instantiate the handler as a partial mock to simplify testing
2021-02-27 20:24:02 +00:00
$this -> hMock = $this -> partialMock ( API :: class );
$this -> hMock -> baseResponse -> returns ([]);
2019-03-19 02:49:47 +00:00
}
2019-03-24 19:05:21 +00:00
/** @dataProvider provideTokenAuthenticationRequests */
2020-01-20 18:52:48 +00:00
public function testAuthenticateAUserToken ( bool $httpRequired , bool $tokenEnforced , string $httpUser = null , array $dataPost , array $dataGet , ResponseInterface $exp ) : void {
2019-03-19 02:49:47 +00:00
self :: setConf ([
'userHTTPAuthRequired' => $httpRequired ,
2020-03-01 20:16:50 +00:00
'userSessionEnforced' => $tokenEnforced ,
2019-03-19 02:49:47 +00:00
], true );
2019-03-20 03:37:08 +00:00
Arsse :: $user -> id = null ;
2021-02-27 20:24:02 +00:00
$this -> dbMock -> tokenLookup -> throws ( new ExceptionInput ( " subjectMissing " ));
$this -> dbMock -> tokenLookup -> with ( " fever.login " , " validtoken " ) -> returns ([ 'user' => " jane.doe@example.com " ]);
2019-03-27 19:09:04 +00:00
// test only the authentication process
2021-02-27 20:24:02 +00:00
$this -> hMock -> baseResponse -> does ( function ( bool $authenticated ) {
2019-03-27 19:09:04 +00:00
return [ 'auth' => ( int ) $authenticated ];
});
2021-02-27 20:24:02 +00:00
$this -> hMock -> processRequest -> does ( function ( $out , $G , $P ) {
2019-03-27 15:54:47 +00:00
return $out ;
});
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( $dataGet , $dataPost , " POST " , null , " " , $httpUser ));
2019-03-19 02:49:47 +00:00
}
2019-10-16 18:42:43 +00:00
public function provideTokenAuthenticationRequests () : iterable {
2019-03-27 19:09:04 +00:00
$success = new JsonResponse ([ 'auth' => 1 ]);
$failure = new JsonResponse ([ 'auth' => 0 ]);
2019-03-20 03:37:08 +00:00
$denied = new EmptyResponse ( 401 );
2019-03-19 02:49:47 +00:00
return [
2019-03-20 03:37:08 +00:00
[ false , true , null , [], [ 'api' => null ], $failure ],
[ false , false , null , [], [ 'api' => null ], $failure ],
[ true , true , null , [], [ 'api' => null ], $denied ],
[ true , false , null , [], [ 'api' => null ], $denied ],
[ false , true , " " , [], [ 'api' => null ], $denied ],
[ false , false , " " , [], [ 'api' => null ], $denied ],
[ true , true , " " , [], [ 'api' => null ], $denied ],
[ true , false , " " , [], [ 'api' => null ], $denied ],
[ false , true , null , [], [ 'api' => null , 'api_key' => " validToken " ], $failure ],
[ false , false , null , [], [ 'api' => null , 'api_key' => " validToken " ], $failure ],
[ true , true , null , [], [ 'api' => null , 'api_key' => " validToken " ], $denied ],
[ true , false , null , [], [ 'api' => null , 'api_key' => " validToken " ], $denied ],
2019-03-21 17:49:55 +00:00
[ false , true , " " , [], [ 'api' => null , 'api_key' => " validToken " ], $denied ],
[ false , false , " " , [], [ 'api' => null , 'api_key' => " validToken " ], $denied ],
[ true , true , " " , [], [ 'api' => null , 'api_key' => " validToken " ], $denied ],
[ true , false , " " , [], [ 'api' => null , 'api_key' => " validToken " ], $denied ],
[ false , true , " validUser " , [], [ 'api' => null , 'api_key' => " validToken " ], $failure ],
[ false , false , " validUser " , [], [ 'api' => null , 'api_key' => " validToken " ], $success ],
[ true , true , " validUser " , [], [ 'api' => null , 'api_key' => " validToken " ], $failure ],
[ true , false , " validUser " , [], [ 'api' => null , 'api_key' => " validToken " ], $success ],
2019-03-20 03:37:08 +00:00
[ false , true , null , [ 'api_key' => " validToken " ], [ 'api' => null ], $success ],
[ false , false , null , [ 'api_key' => " validToken " ], [ 'api' => null ], $success ],
[ true , true , null , [ 'api_key' => " validToken " ], [ 'api' => null ], $denied ],
[ true , false , null , [ 'api_key' => " validToken " ], [ 'api' => null ], $denied ],
[ false , true , " " , [ 'api_key' => " validToken " ], [ 'api' => null ], $denied ],
[ false , false , " " , [ 'api_key' => " validToken " ], [ 'api' => null ], $denied ],
[ true , true , " " , [ 'api_key' => " validToken " ], [ 'api' => null ], $denied ],
[ true , false , " " , [ 'api_key' => " validToken " ], [ 'api' => null ], $denied ],
[ false , true , " validUser " , [ 'api_key' => " validToken " ], [ 'api' => null ], $success ],
[ false , false , " validUser " , [ 'api_key' => " validToken " ], [ 'api' => null ], $success ],
[ true , true , " validUser " , [ 'api_key' => " validToken " ], [ 'api' => null ], $success ],
[ true , false , " validUser " , [ 'api_key' => " validToken " ], [ 'api' => null ], $success ],
[ false , true , null , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $failure ],
[ false , false , null , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $failure ],
[ true , true , null , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $denied ],
[ true , false , null , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $denied ],
[ false , true , " " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $denied ],
[ false , false , " " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $denied ],
[ true , true , " " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $denied ],
[ true , false , " " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $denied ],
[ false , true , " validUser " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $failure ],
[ false , false , " validUser " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $success ],
[ true , true , " validUser " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $failure ],
[ true , false , " validUser " , [ 'api_key' => " invalidToken " ], [ 'api' => null ], $success ],
2019-03-19 02:49:47 +00:00
];
}
2019-03-27 19:09:04 +00:00
2020-01-20 18:52:48 +00:00
public function testListGroups () : void {
2021-02-27 20:24:02 +00:00
$this -> dbMock -> tagList -> with ( $this -> userId ) -> returns ( new Result ([
2019-03-27 19:09:04 +00:00
[ 'id' => 1 , 'name' => " Fascinating " , 'subscriptions' => 2 ],
[ 'id' => 2 , 'name' => " Interesting " , 'subscriptions' => 2 ],
[ 'id' => 3 , 'name' => " Boring " , 'subscriptions' => 0 ],
]));
2021-02-27 20:24:02 +00:00
$this -> dbMock -> tagSummarize -> with ( $this -> userId ) -> returns ( new Result ([
2019-03-27 19:09:04 +00:00
[ '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 " ],
],
]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api&groups " ));
2019-03-27 19:09:04 +00:00
}
2020-01-20 18:52:48 +00:00
public function testListFeeds () : void {
2021-02-27 20:24:02 +00:00
$this -> dbMock -> subscriptionList -> with ( $this -> userId ) -> returns ( new Result ([
2021-02-09 04:51:40 +00:00
[ 'id' => 1 , 'feed' => 5 , 'title' => " Ankh-Morpork News " , 'url' => " http://example.com/feed " , 'source' => " http://example.com/ " , 'edited' => " 2019-01-01 21:12:00 " , 'icon_url' => " http://example.com/favicon.ico " , 'icon_id' => 42 ],
[ '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 " , 'icon_url' => " " , 'icon_id' => null ],
[ 'id' => 3 , 'feed' => 1 , 'title' => " The Last Soul " , 'url' => " http://example.org/feed " , 'source' => " http://example.org/ " , 'edited' => " 1991-08-12 03:22:00 " , 'icon_url' => " http://example.org/favicon.ico " , 'icon_id' => 42 ],
2019-03-27 19:09:04 +00:00
]));
2021-02-27 20:24:02 +00:00
$this -> dbMock -> tagSummarize -> with ( $this -> userId ) -> returns ( new Result ([
2019-03-27 19:09:04 +00:00
[ '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' => [
2021-02-09 04:51:40 +00:00
[ 'id' => 1 , 'favicon_id' => 42 , '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' => 42 , '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 " )],
2019-03-27 19:09:04 +00:00
],
'feeds_groups' => [
[ 'group_id' => 1 , 'feed_ids' => " 1,2 " ],
[ 'group_id' => 2 , 'feed_ids' => " 1,3 " ],
],
]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api&feeds " ));
2019-09-05 14:21:36 +00:00
}
2019-04-08 22:41:56 +00:00
/** @dataProvider provideItemListContexts */
2020-01-20 18:52:48 +00:00
public function testListItems ( string $url , Context $c , bool $desc ) : void {
2019-04-08 22:41:56 +00:00
$fields = [ " id " , " subscription " , " title " , " author " , " content " , " url " , " starred " , " unread " , " published_date " ];
$order = [ $desc ? " id desc " : " id " ];
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleList -> returns ( new Result ( $this -> articles [ 'db' ]));
$this -> dbMock -> articleCount -> with ( $this -> userId , ( new Context ) -> hidden ( false )) -> returns ( 1024 );
2019-04-08 22:41:56 +00:00
$exp = new JsonResponse ([
2020-03-01 20:16:50 +00:00
'items' => $this -> articles [ 'rest' ],
2019-04-08 22:41:56 +00:00
'total_items' => 1024 ,
]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api& $url " ));
$this -> dbMock -> articleList -> calledWith ( $this -> userId , $this -> equalTo ( $c ), $fields , $order );
2019-04-08 22:41:56 +00:00
}
2019-10-16 18:42:43 +00:00
public function provideItemListContexts () : iterable {
2019-04-08 22:41:56 +00:00
$c = ( new Context ) -> limit ( 50 );
return [
2020-12-21 00:32:07 +00:00
[ " items " , ( clone $c ) -> hidden ( false ), false ],
[ " items&group_ids=1,2,3,4 " , ( clone $c ) -> tags ([ 1 , 2 , 3 , 4 ]) -> hidden ( false ), false ],
[ " items&feed_ids=1,2,3,4 " , ( clone $c ) -> subscriptions ([ 1 , 2 , 3 , 4 ]) -> hidden ( false ), false ],
2019-04-08 22:41:56 +00:00
[ " items&with_ids=1,2,3,4 " , ( clone $c ) -> articles ([ 1 , 2 , 3 , 4 ]), false ],
2020-12-21 00:32:07 +00:00
[ " items&since_id=1 " , ( clone $c ) -> oldestArticle ( 2 ) -> hidden ( false ), false ],
[ " items&max_id=2 " , ( clone $c ) -> latestArticle ( 1 ) -> hidden ( false ), true ],
2019-04-08 22:41:56 +00:00
[ " items&with_ids=1,2,3,4&max_id=6 " , ( clone $c ) -> articles ([ 1 , 2 , 3 , 4 ]), false ],
[ " items&with_ids=1,2,3,4&since_id=6 " , ( clone $c ) -> articles ([ 1 , 2 , 3 , 4 ]), false ],
2020-12-21 00:32:07 +00:00
[ " items&max_id=3&since_id=6 " , ( clone $c ) -> latestArticle ( 2 ) -> hidden ( false ), true ],
[ " items&feed_ids=1,2,3,4&since_id=6 " , ( clone $c ) -> subscriptions ([ 1 , 2 , 3 , 4 ]) -> oldestArticle ( 7 ) -> hidden ( false ), false ],
2019-04-08 22:41:56 +00:00
];
}
2019-04-08 23:15:12 +00:00
2020-01-20 18:52:48 +00:00
public function testListItemIds () : void {
2019-04-08 23:15:12 +00:00
$saved = [[ 'id' => 1 ],[ 'id' => 2 ],[ 'id' => 3 ]];
$unread = [[ 'id' => 4 ],[ 'id' => 5 ],[ 'id' => 6 ]];
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleList -> with ( $this -> userId , ( new Context ) -> starred ( true ) -> hidden ( false )) -> returns ( new Result ( $saved ));
$this -> dbMock -> articleList -> with ( $this -> userId , ( new Context ) -> unread ( true ) -> hidden ( false )) -> returns ( new Result ( $unread ));
2020-03-01 20:16:50 +00:00
$exp = new JsonResponse ([ 'saved_item_ids' => " 1,2,3 " ]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api&saved_item_ids " ));
2020-03-01 20:16:50 +00:00
$exp = new JsonResponse ([ 'unread_item_ids' => " 4,5,6 " ]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api&unread_item_ids " ));
2019-04-08 23:15:12 +00:00
}
2019-04-08 23:21:21 +00:00
2020-01-20 18:52:48 +00:00
public function testListHotLinks () : void {
2019-04-08 23:21:21 +00:00
// hot links are not actually implemented, so an empty array should be all we get
2020-03-01 20:16:50 +00:00
$exp = new JsonResponse ([ 'links' => []]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api&links " ));
2019-04-08 23:21:21 +00:00
}
2019-04-10 13:48:28 +00:00
/** @dataProvider provideMarkingContexts */
2020-01-20 18:52:48 +00:00
public function testSetMarks ( string $post , Context $c , array $data , array $out ) : void {
2019-04-10 13:48:28 +00:00
$saved = [[ 'id' => 1 ],[ 'id' => 2 ],[ 'id' => 3 ]];
$unread = [[ 'id' => 4 ],[ 'id' => 5 ],[ 'id' => 6 ]];
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleList -> with ( $this -> userId , ( new Context ) -> starred ( true ) -> hidden ( false )) -> returns ( new Result ( $saved ));
$this -> dbMock -> articleList -> with ( $this -> userId , ( new Context ) -> unread ( true ) -> hidden ( false )) -> returns ( new Result ( $unread ));
$this -> dbMock -> articleMark -> returns ( 0 );
$this -> dbMock -> articleMark -> with ( $this -> userId , $this -> anything (), ( new Context ) -> article ( 2112 )) -> throws ( new \JKingWeb\Arsse\Db\ExceptionInput ( " subjectMissing " ));
2019-04-10 13:48:28 +00:00
$exp = new JsonResponse ( $out );
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api " , $post ));
2019-04-10 13:48:28 +00:00
if ( $c && $data ) {
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleMark -> calledWith ( $this -> userId , $data , $this -> equalTo ( $c ));
2019-04-10 13:48:28 +00:00
} else {
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleMark -> never () -> called ();
2019-07-27 03:23:22 +00:00
}
}
/** @dataProvider provideMarkingContexts */
2020-01-20 18:52:48 +00:00
public function testSetMarksWithQuery ( string $get , Context $c , array $data , array $out ) : void {
2019-07-27 03:23:22 +00:00
$saved = [[ 'id' => 1 ],[ 'id' => 2 ],[ 'id' => 3 ]];
$unread = [[ 'id' => 4 ],[ 'id' => 5 ],[ 'id' => 6 ]];
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleList -> with ( $this -> userId , ( new Context ) -> starred ( true ) -> hidden ( false )) -> returns ( new Result ( $saved ));
$this -> dbMock -> articleList -> with ( $this -> userId , ( new Context ) -> unread ( true ) -> hidden ( false )) -> returns ( new Result ( $unread ));
$this -> dbMock -> articleMark -> returns ( 0 );
$this -> dbMock -> articleMark -> with ( $this -> userId , $this -> anything (), ( new Context ) -> article ( 2112 )) -> throws ( new \JKingWeb\Arsse\Db\ExceptionInput ( " subjectMissing " ));
2019-07-27 03:23:22 +00:00
$exp = new JsonResponse ( $out );
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api& $get " ));
2019-07-27 03:23:22 +00:00
if ( $c && $data ) {
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleMark -> calledWith ( $this -> userId , $data , $this -> equalTo ( $c ));
2019-07-27 03:23:22 +00:00
} else {
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleMark -> never () -> called ();
2019-04-10 13:48:28 +00:00
}
}
2019-10-16 18:42:43 +00:00
public function provideMarkingContexts () : iterable {
2019-04-10 13:48:28 +00:00
$markRead = [ 'read' => true ];
$markUnread = [ 'read' => false ];
$markSaved = [ 'starred' => true ];
$markUnsaved = [ 'starred' => false ];
$listSaved = [ 'saved_item_ids' => " 1,2,3 " ];
$listUnread = [ 'unread_item_ids' => " 4,5,6 " ];
return [
[ " mark=item&as=read&id=5 " , ( new Context ) -> article ( 5 ), $markRead , $listUnread ],
[ " mark=item&as=unread&id=42 " , ( new Context ) -> article ( 42 ), $markUnread , $listUnread ],
[ " mark=item&as=read&id=2112 " , ( new Context ) -> article ( 2112 ), $markRead , $listUnread ], // article doesn't exist
[ " mark=item&as=saved&id=5 " , ( new Context ) -> article ( 5 ), $markSaved , $listSaved ],
[ " mark=item&as=unsaved&id=42 " , ( new Context ) -> article ( 42 ), $markUnsaved , $listSaved ],
2020-12-21 00:32:07 +00:00
[ " mark=feed&as=read&id=5 " , ( new Context ) -> subscription ( 5 ) -> hidden ( false ), $markRead , $listUnread ],
[ " mark=feed&as=unread&id=42 " , ( new Context ) -> subscription ( 42 ) -> hidden ( false ), $markUnread , $listUnread ],
[ " mark=feed&as=saved&id=5 " , ( new Context ) -> subscription ( 5 ) -> hidden ( false ), $markSaved , $listSaved ],
[ " mark=feed&as=unsaved&id=42 " , ( new Context ) -> subscription ( 42 ) -> hidden ( false ), $markUnsaved , $listSaved ],
[ " mark=group&as=read&id=5 " , ( new Context ) -> tag ( 5 ) -> hidden ( false ), $markRead , $listUnread ],
[ " mark=group&as=unread&id=42 " , ( new Context ) -> tag ( 42 ) -> hidden ( false ), $markUnread , $listUnread ],
[ " mark=group&as=saved&id=5 " , ( new Context ) -> tag ( 5 ) -> hidden ( false ), $markSaved , $listSaved ],
[ " mark=group&as=unsaved&id=42 " , ( new Context ) -> tag ( 42 ) -> hidden ( false ), $markUnsaved , $listSaved ],
2019-04-10 13:48:28 +00:00
[ " mark=item&as=invalid&id=42 " , new Context , [], []],
[ " mark=invalid&as=unread&id=42 " , new Context , [], []],
2020-12-21 00:32:07 +00:00
[ " mark=group&as=read&id=0 " , ( new Context ) -> hidden ( false ), $markRead , $listUnread ],
[ " mark=group&as=unread&id=0 " , ( new Context ) -> hidden ( false ), $markUnread , $listUnread ],
[ " mark=group&as=saved&id=0 " , ( new Context ) -> hidden ( false ), $markSaved , $listSaved ],
[ " mark=group&as=unsaved&id=0 " , ( new Context ) -> hidden ( false ), $markUnsaved , $listSaved ],
2019-04-10 13:48:28 +00:00
[ " mark=group&as=read&id=-1 " , ( new Context ) -> not -> folder ( 0 ), $markRead , $listUnread ],
[ " mark=group&as=unread&id=-1 " , ( new Context ) -> not -> folder ( 0 ), $markUnread , $listUnread ],
[ " mark=group&as=saved&id=-1 " , ( new Context ) -> not -> folder ( 0 ), $markSaved , $listSaved ],
[ " mark=group&as=unsaved&id=-1 " , ( new Context ) -> not -> folder ( 0 ), $markUnsaved , $listSaved ],
2019-04-10 20:01:58 +00:00
[ " mark=group&as=read&id=-1&before=946684800 " , ( new Context ) -> not -> folder ( 0 ) -> notMarkedSince ( " 2000-01-01T00:00:00Z " ), $markRead , $listUnread ],
2019-04-10 13:48:28 +00:00
[ " mark=item&as=unread " , new Context , [], []],
[ " mark=item&id=6 " , new Context , [], []],
[ " as=unread&id=6 " , new Context , [], []],
];
}
2019-04-10 19:07:34 +00:00
/** @dataProvider provideInvalidRequests */
2021-02-27 20:24:02 +00:00
public function testSendInvalidRequests ( string $get , string $post , string $method , ? string $type , ResponseInterface $exp ) : void {
$this -> assertMessage ( $exp , $this -> req ( $get , $post , $method , $type ));
2019-04-10 19:07:34 +00:00
}
2019-10-16 18:42:43 +00:00
public function provideInvalidRequests () : iterable {
2019-04-10 19:07:34 +00:00
return [
2021-02-27 20:24:02 +00:00
'Not an API request' => [ " " , " " , " POST " , null , new EmptyResponse ( 404 )],
'Wrong method' => [ " api " , " " , " PUT " , null , new EmptyResponse ( 405 , [ 'Allow' => " OPTIONS,POST " ])],
'Non-standard method' => [ " api " , " " , " GET " , null , new JsonResponse ([])],
2021-03-06 00:31:11 +00:00
'Wrong content type' => [ " api " , '{"api_key":"validToken"}' , " POST " , " application/json " , new JsonResponse ([])], // some clients send nonsensical content types; Fever seems to have allowed this
'Non-standard content type' => [ " api " , '{"api_key":"validToken"}' , " POST " , " multipart/form-data; boundary=33b68964f0de4c1f-5144aa6caaa6e4a8-18bfaf416a1786c8-5c5053a45f221bc1 " , new JsonResponse ([])], // some clients send nonsensical content types; Fever seems to have allowed this
2019-04-10 19:07:34 +00:00
];
}
2019-04-10 20:01:58 +00:00
2020-01-20 18:52:48 +00:00
public function testMakeABaseQuery () : void {
2021-02-27 20:24:02 +00:00
$this -> hMock -> baseResponse -> forwards ();
$this -> hMock -> logIn -> returns ( true );
$this -> dbMock -> subscriptionRefreshed -> with ( $this -> userId ) -> returns ( new \DateTimeImmutable ( " 2000-01-01T00:00:00Z " ));
2019-04-10 20:01:58 +00:00
$exp = new JsonResponse ([
2020-03-01 20:16:50 +00:00
'api_version' => API :: LEVEL ,
'auth' => 1 ,
2019-04-10 20:01:58 +00:00
'last_refreshed_on_time' => 946684800 ,
]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api " ));
$this -> dbMock -> subscriptionRefreshed -> with ( $this -> userId ) -> returns ( null ); // no subscriptions
2019-04-10 20:01:58 +00:00
$exp = new JsonResponse ([
2020-03-01 20:16:50 +00:00
'api_version' => API :: LEVEL ,
'auth' => 1 ,
2019-04-10 20:01:58 +00:00
'last_refreshed_on_time' => null ,
]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api " ));
$this -> hMock -> logIn -> returns ( false );
2019-04-10 20:01:58 +00:00
$exp = new JsonResponse ([
'api_version' => API :: LEVEL ,
2020-03-01 20:16:50 +00:00
'auth' => 0 ,
2019-04-10 20:01:58 +00:00
]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api " ));
2019-04-10 20:01:58 +00:00
}
2019-04-10 22:27:57 +00:00
2020-01-20 18:52:48 +00:00
public function testUndoReadMarks () : void {
2019-04-10 22:27:57 +00:00
$unread = [[ 'id' => 4 ],[ 'id' => 5 ],[ 'id' => 6 ]];
$out = [ 'unread_item_ids' => " 4,5,6 " ];
2021-02-27 20:24:02 +00:00
$this -> dbMock -> articleList -> with ( $this -> userId , $this -> equalTo (( new Context ) -> limit ( 1 ) -> hidden ( false )), [ " marked_date " ], [ " marked_date desc " ]) -> returns ( new Result ([[ 'marked_date' => " 2000-01-01 00:00:00 " ]]));
$this -> dbMock -> articleList -> with ( $this -> userId , $this -> equalTo (( new Context ) -> unread ( true ) -> hidden ( false ))) -> returns ( new Result ( $unread ));
$this -> dbMock -> articleMark -> returns ( 0 );
2019-04-10 22:27:57 +00:00
$exp = new JsonResponse ( $out );
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api " , [ 'unread_recently_read' => 1 ]));
$this -> dbMock -> articleMark -> calledWith ( $this -> userId , [ 'read' => false ], $this -> equalTo (( new Context ) -> unread ( false ) -> markedSince ( " 1999-12-31T23:59:45Z " ) -> hidden ( false )));
$this -> dbMock -> articleList -> with ( $this -> userId , ( new Context ) -> limit ( 1 ) -> hidden ( false ), [ " marked_date " ], [ " marked_date desc " ]) -> returns ( new Result ([]));
$this -> assertMessage ( $exp , $this -> req ( " api " , [ 'unread_recently_read' => 1 ]));
$this -> dbMock -> articleMark -> once () -> called (); // only called one time, above
2019-04-10 22:27:57 +00:00
}
2019-07-24 13:10:13 +00:00
2020-01-20 18:52:48 +00:00
public function testOutputToXml () : void {
2021-02-27 20:24:02 +00:00
$this -> hMock -> processRequest -> returns ([
2020-03-01 20:16:50 +00:00
'items' => $this -> articles [ 'rest' ],
2019-07-24 13:10:13 +00:00
'total_items' => 1024 ,
]);
$exp = new XmlResponse ( " <response><items><item><id>101</id><feed_id>8</feed_id><title>Article title 1</title><author></author><html><p>Article content 1</p></html><url>http://example.com/1</url><is_saved>0</is_saved><is_read>0</is_read><created_on_time>946684800</created_on_time></item><item><id>102</id><feed_id>8</feed_id><title>Article title 2</title><author></author><html><p>Article content 2</p></html><url>http://example.com/2</url><is_saved>0</is_saved><is_read>1</is_read><created_on_time>946771200</created_on_time></item><item><id>103</id><feed_id>9</feed_id><title>Article title 3</title><author></author><html><p>Article content 3</p></html><url>http://example.com/3</url><is_saved>1</is_saved><is_read>0</is_read><created_on_time>946857600</created_on_time></item><item><id>104</id><feed_id>9</feed_id><title>Article title 4</title><author></author><html><p>Article content 4</p></html><url>http://example.com/4</url><is_saved>1</is_saved><is_read>1</is_read><created_on_time>946944000</created_on_time></item><item><id>105</id><feed_id>10</feed_id><title>Article title 5</title><author></author><html><p>Article content 5</p></html><url>http://example.com/5</url><is_saved>0</is_saved><is_read>0</is_read><created_on_time>947030400</created_on_time></item></items><total_items>1024</total_items></response> " );
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api=xml " ));
2019-07-24 13:10:13 +00:00
}
2019-07-24 16:27:50 +00:00
2020-01-20 18:52:48 +00:00
public function testListFeedIcons () : void {
2020-03-01 23:32:01 +00:00
$iconType = ( new \ReflectionClassConstant ( API :: class , " GENERIC_ICON_TYPE " )) -> getValue ();
$iconData = ( new \ReflectionClassConstant ( API :: class , " GENERIC_ICON_DATA " )) -> getValue ();
2021-02-27 20:24:02 +00:00
$this -> dbMock -> iconList -> returns ( new Result ( $this -> v ([
2021-02-09 04:51:40 +00:00
[ 'id' => 42 , 'type' => " image/svg+xml " , 'data' => " <svg/> " ],
[ 'id' => 44 , 'type' => null , 'data' => " IMAGE DATA " ],
[ 'id' => 47 , 'type' => null , 'data' => null ],
])));
$exp = new JsonResponse ([ 'favicons' => [
[ 'id' => 0 , 'data' => $iconType . " , " . $iconData ],
[ 'id' => 42 , 'data' => " image/svg+xml;base64,PHN2Zy8+ " ],
[ 'id' => 44 , 'data' => " application/octet-stream;base64,SU1BR0UgREFUQQ== " ],
]]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api&favicons " ));
2019-07-24 16:27:50 +00:00
}
2019-07-24 16:32:00 +00:00
2020-01-20 18:52:48 +00:00
public function testAnswerOptionsRequest () : void {
2019-07-24 16:32:00 +00:00
$exp = new EmptyResponse ( 204 , [
2020-03-01 20:16:50 +00:00
'Allow' => " POST " ,
2020-10-27 14:49:54 +00:00
'Accept' => " application/x-www-form-urlencoded, multipart/form-data " ,
2019-07-24 16:32:00 +00:00
]);
2021-02-27 20:24:02 +00:00
$this -> assertMessage ( $exp , $this -> req ( " api " , " " , " OPTIONS " ));
2019-07-24 16:32:00 +00:00
}
2019-03-19 02:49:47 +00:00
}