1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-23 05:54:55 +00:00

Tests for NCN v1 article listing and marking, with slight fixes

-Fixes #7
-Fixes #13
-Fixes #14
-Fixes #15
-Fixes #16
-Fixes #17
-Fixes #18
-Fixes #19
-Fixes #20
-Fixes #21
-Fixes #22
-Fixes #23
-Fixes #24
This commit is contained in:
J. King 2017-07-09 17:57:18 -04:00
parent b3f631e335
commit 4992b2c669
2 changed files with 401 additions and 30 deletions

View file

@ -119,7 +119,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
]; ];
// the first path element is the overall scope of the request // the first path element is the overall scope of the request
$scope = $url[0]; $scope = $url[0];
// any URL components which are only digits should be replaced with "#", for easier comparison (integer segments are IDs, and we don't care about the specific ID) // any URL components which are only digits should be replaced with "0", for easier comparison (integer segments are IDs, and we don't care about the specific ID)
for($a = 0; $a < sizeof($url); $a++) { for($a = 0; $a < sizeof($url); $a++) {
if($this->validateInt($url[$a])) $url[$a] = "0"; if($this->validateInt($url[$a])) $url[$a] = "0";
} }
@ -185,9 +185,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
'author' => "author", 'author' => "author",
'pubDate' => "edited_date", 'pubDate' => "edited_date",
'body' => "content", 'body' => "content",
'enclsoureMime' => "media_type", 'enclosureMime' => "media_type",
'enclosureLink' => "media_url", 'enclosureLink' => "media_url",
'feedId' => "feed", 'feedId' => "subscription",
'unread' => "unread", 'unread' => "unread",
'starred' => "starred", 'starred' => "starred",
'lastModified' => "modified_date", 'lastModified' => "modified_date",
@ -263,6 +263,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(204); return new Response(204);
} }
// mark all articles associated with a folder as read
protected function folderMarkRead(array $url, array $data): Response { protected function folderMarkRead(array $url, array $data): Response {
$c = new Context; $c = new Context;
if(isset($data['newestItemId'])) { if(isset($data['newestItemId'])) {
@ -426,6 +427,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(204); return new Response(204);
} }
// mark all articles associated with a subscription as read
protected function subscriptionMarkRead(array $url, array $data): Response { protected function subscriptionMarkRead(array $url, array $data): Response {
$c = new Context; $c = new Context;
if(isset($data['newestItemId'])) { if(isset($data['newestItemId'])) {
@ -446,6 +448,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(204); return new Response(204);
} }
// list articles and their properties
protected function articleList(array $url, array $data): Response { protected function articleList(array $url, array $data): Response {
// set the context options supplied by the client // set the context options supplied by the client
$c = new Context; $c = new Context;
@ -499,6 +502,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(200, $out); return new Response(200, $out);
} }
// mark all articles as read
protected function articleMarkReadAll(array $url, array $data): Response { protected function articleMarkReadAll(array $url, array $data): Response {
$c = new Context; $c = new Context;
if(isset($data['newestItemId'])) { if(isset($data['newestItemId'])) {
@ -513,6 +517,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(204); return new Response(204);
} }
// mark a single article as read
protected function articleMarkRead(array $url, array $data): Response { protected function articleMarkRead(array $url, array $data): Response {
// initialize the matching context // initialize the matching context
$c = new Context; $c = new Context;
@ -528,6 +533,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(204); return new Response(204);
} }
// mark a single article as read
protected function articleMarkStarred(array $url, array $data): Response { protected function articleMarkStarred(array $url, array $data): Response {
// initialize the matching context // initialize the matching context
$c = new Context; $c = new Context;
@ -535,7 +541,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
// determine whether to mark read or unread // determine whether to mark read or unread
$set = ($url[3]=="star"); $set = ($url[3]=="star");
try { try {
Data::$db->articleMark(Data::$user->id, ['star' => $set], $c); Data::$db->articleMark(Data::$user->id, ['starred' => $set], $c);
} catch(ExceptionInput $e) { } catch(ExceptionInput $e) {
// ID is not valid // ID is not valid
return new Response(404); return new Response(404);
@ -543,9 +549,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(204); return new Response(204);
} }
// mark an array of articles as read
protected function articleMarkReadMulti(array $url, array $data): Response { protected function articleMarkReadMulti(array $url, array $data): Response {
// initialize the matching context
$c = new Context;
// determine whether to mark read or unread // determine whether to mark read or unread
$set = ($url[1]=="read"); $set = ($url[1]=="read");
// if the input data is not at all valid, return an error // if the input data is not at all valid, return an error
@ -554,6 +559,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
$t = Data::$db->begin(); $t = Data::$db->begin();
$in = array_chunk($data['items'], 50); $in = array_chunk($data['items'], 50);
for($a = 0; $a < sizeof($in); $a++) { for($a = 0; $a < sizeof($in); $a++) {
// initialize the matching context
$c = new Context;
$c->editions($in[$a]); $c->editions($in[$a]);
try { try {
Data::$db->articleMark(Data::$user->id, ['read' => $set], $c); Data::$db->articleMark(Data::$user->id, ['read' => $set], $c);
@ -563,10 +570,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new Response(204); return new Response(204);
} }
// mark an array of articles as starred
protected function articleMarkStarredMulti(array $url, array $data): Response { protected function articleMarkStarredMulti(array $url, array $data): Response {
// initialize the matching context // determine whether to mark starred or unstarred
$c = new Context;
// determine whether to mark read or unread
$set = ($url[1]=="star"); $set = ($url[1]=="star");
// if the input data is not at all valid, return an error // if the input data is not at all valid, return an error
if(!isset($data['items']) || !is_array($data['items'])) return new Response(422); if(!isset($data['items']) || !is_array($data['items'])) return new Response(422);
@ -574,6 +580,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
$t = Data::$db->begin(); $t = Data::$db->begin();
$in = array_chunk(array_column($data['items'], "guidHash"), 50); $in = array_chunk(array_column($data['items'], "guidHash"), 50);
for($a = 0; $a < sizeof($in); $a++) { for($a = 0; $a < sizeof($in); $a++) {
// initialize the matching context
$c = new Context;
$c->articles($in[$a]); $c->articles($in[$a]);
try { try {
Data::$db->articleMark(Data::$user->id, ['starred' => $set], $c); Data::$db->articleMark(Data::$user->id, ['starred' => $set], $c);

View file

@ -5,6 +5,8 @@ use JKingWeb\Arsse\REST\Request;
use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\REST\Response;
use JKingWeb\Arsse\Test\Result; use JKingWeb\Arsse\Test\Result;
use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Misc\Context;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\Transaction;
use Phake; use Phake;
@ -74,6 +76,187 @@ class TestNCNV1_2 extends Test\AbstractTest {
], ],
], ],
]; ];
protected $articles = [
'db' => [
[
'id' => 101,
'url' => 'http://example.com/1',
'title' => 'Article title 1',
'author' => '',
'content' => '<p>Article content 1</p>',
'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda',
'published_date' => '2000-01-01 00:00:00',
'edited_date' => '2000-01-01 00:00:01',
'modified_date' => '2000-01-01 01:00:00',
'unread' => 1,
'starred' => 0,
'edition' => 101,
'subscription' => 8,
'fingerprint' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6:fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4:18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207',
'media_url' => null,
'media_type' => null,
],
[
'id' => 102,
'url' => 'http://example.com/2',
'title' => 'Article title 2',
'author' => '',
'content' => '<p>Article content 2</p>',
'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7',
'published_date' => '2000-01-02 00:00:00',
'edited_date' => '2000-01-02 00:00:02',
'modified_date' => '2000-01-02 02:00:00',
'unread' => 0,
'starred' => 0,
'edition' => 202,
'subscription' => 8,
'fingerprint' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153:13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9:2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e',
'media_url' => "http://example.com/text",
'media_type' => "text/plain",
],
[
'id' => 103,
'url' => 'http://example.com/3',
'title' => 'Article title 3',
'author' => '',
'content' => '<p>Article content 3</p>',
'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92',
'published_date' => '2000-01-03 00:00:00',
'edited_date' => '2000-01-03 00:00:03',
'modified_date' => '2000-01-03 03:00:00',
'unread' => 1,
'starred' => 1,
'edition' => 203,
'subscription' => 9,
'fingerprint' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b:b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406:ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b',
'media_url' => "http://example.com/video",
'media_type' => "video/webm",
],
[
'id' => 104,
'url' => 'http://example.com/4',
'title' => 'Article title 4',
'author' => '',
'content' => '<p>Article content 4</p>',
'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180',
'published_date' => '2000-01-04 00:00:00',
'edited_date' => '2000-01-04 00:00:04',
'modified_date' => '2000-01-04 04:00:00',
'unread' => 0,
'starred' => 1,
'edition' => 204,
'subscription' => 9,
'fingerprint' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8:f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3:ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9',
'media_url' => "http://example.com/image",
'media_type' => "image/svg+xml",
],
[
'id' => 105,
'url' => 'http://example.com/5',
'title' => 'Article title 5',
'author' => '',
'content' => '<p>Article content 5</p>',
'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41',
'published_date' => '2000-01-05 00:00:00',
'edited_date' => '2000-01-05 00:00:05',
'modified_date' => '2000-01-05 05:00:00',
'unread' => 1,
'starred' => 0,
'edition' => 305,
'subscription' => 10,
'fingerprint' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022:834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900:43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba',
'media_url' => "http://example.com/audio",
'media_type' => "audio/ogg",
],
],
'rest' => [
[
'guidHash' => 101,
'url' => 'http://example.com/1',
'title' => 'Article title 1',
'author' => '',
'body' => '<p>Article content 1</p>',
'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda',
'pubDate' => 946684801,
'lastModified' => 946688400,
'unread' => true,
'starred' => false,
'id' => 101,
'feedId' => 8,
'fingerprint' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6:fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4:18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207',
'enclosureLink' => null,
'enclosureMime' => null,
],
[
'guidHash' => 102,
'url' => 'http://example.com/2',
'title' => 'Article title 2',
'author' => '',
'body' => '<p>Article content 2</p>',
'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7',
'pubDate' => 946771202,
'lastModified' => 946778400,
'unread' => false,
'starred' => false,
'id' => 202,
'feedId' => 8,
'fingerprint' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153:13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9:2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e',
'enclosureLink' => "http://example.com/text",
'enclosureMime' => "text/plain",
],
[
'guidHash' => 103,
'url' => 'http://example.com/3',
'title' => 'Article title 3',
'author' => '',
'body' => '<p>Article content 3</p>',
'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92',
'pubDate' => 946857603,
'lastModified' => 946868400,
'unread' => true,
'starred' => true,
'id' => 203,
'feedId' => 9,
'fingerprint' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b:b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406:ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b',
'enclosureLink' => "http://example.com/video",
'enclosureMime' => "video/webm",
],
[
'guidHash' => 104,
'url' => 'http://example.com/4',
'title' => 'Article title 4',
'author' => '',
'body' => '<p>Article content 4</p>',
'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180',
'pubDate' => 946944004,
'lastModified' => 946958400,
'unread' => false,
'starred' => true,
'id' => 204,
'feedId' => 9,
'fingerprint' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8:f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3:ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9',
'enclosureLink' => "http://example.com/image",
'enclosureMime' => "image/svg+xml",
],
[
'guidHash' => 105,
'url' => 'http://example.com/5',
'title' => 'Article title 5',
'author' => '',
'body' => '<p>Article content 5</p>',
'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41',
'pubDate' => 947030405,
'lastModified' => 947048400,
'unread' => true,
'starred' => false,
'id' => 305,
'feedId' => 10,
'fingerprint' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022:834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900:43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba',
'enclosureLink' => "http://example.com/audio",
'enclosureMime' => "audio/ogg",
],
],
];
function setUp() { function setUp() {
$this->clearData(); $this->clearData();
@ -84,6 +267,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
Data::$user->id = "john.doe@example.com"; Data::$user->id = "john.doe@example.com";
// create a mock database interface // create a mock database interface
Data::$db = Phake::mock(Database::Class); Data::$db = Phake::mock(Database::Class);
Phake::when(Data::$db)->begin->thenReturn(Phake::mock(Transaction::class));
$this->h = new REST\NextCloudNews\V1_2(); $this->h = new REST\NextCloudNews\V1_2();
} }
@ -165,14 +349,14 @@ class TestNCNV1_2 extends Test\AbstractTest {
['id' => 2, 'name' => "Hardware", 'parent' => null], ['id' => 2, 'name' => "Hardware", 'parent' => null],
]; ];
// set of various mocks for testing // set of various mocks for testing
Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[0])->thenReturn(1)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[0])->thenReturn(1)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[1])->thenReturn(2)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[1])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 1)->thenReturn($out[0]); Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 1)->thenReturn($out[0]);
Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 2)->thenReturn($out[1]); Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 2)->thenReturn($out[1]);
// set up mocks that produce errors // set up mocks that produce errors
Phake::when(Data::$db)->folderAdd(Data::$user->id, [])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing")); Phake::when(Data::$db)->folderAdd(Data::$user->id, [])->thenThrow(new ExceptionInput("missing"));
Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => ""])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing")); Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing"));
Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => " "])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace")); Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => " "])->thenThrow(new ExceptionInput("whitespace"));
// correctly add two folders, using different means // correctly add two folders, using different means
$exp = new Response(200, ['folders' => [$out[0]]]); $exp = new Response(200, ['folders' => [$out[0]]]);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json'))); $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json')));
@ -195,7 +379,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
} }
function testRemoveAFolder() { function testRemoveAFolder() {
Phake::when(Data::$db)->folderRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); Phake::when(Data::$db)->folderRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing"));
$exp = new Response(204); $exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1"))); $this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
// fail on the second invocation because it no longer exists // fail on the second invocation because it no longer exists
@ -213,11 +397,11 @@ class TestNCNV1_2 extends Test\AbstractTest {
[], [],
]; ];
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[0])->thenReturn(true); Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[0])->thenReturn(true);
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 2, $in[1])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 2, $in[1])->thenThrow(new ExceptionInput("constraintViolation"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[2])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing")); Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[2])->thenThrow(new ExceptionInput("missing"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[3])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace")); Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[3])->thenThrow(new ExceptionInput("whitespace"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 3, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); // folder ID 3 does not exist Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 3, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // folder ID 3 does not exist
$exp = new Response(204); $exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json'))); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json')));
$exp = new Response(409); $exp = new Response(409);
@ -268,13 +452,13 @@ class TestNCNV1_2 extends Test\AbstractTest {
[], [],
]; ];
// set up the necessary mocks // set up the necessary mocks
Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.com/news.atom")->thenReturn(2112)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.com/news.atom")->thenReturn(2112)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.org/news.atom")->thenReturn( 42 )->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.org/news.atom")->thenReturn( 42 )->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->subscriptionPropertiesGet(Data::$user->id, 2112)->thenReturn($this->feeds['db'][0]); Phake::when(Data::$db)->subscriptionPropertiesGet(Data::$user->id, 2112)->thenReturn($this->feeds['db'][0]);
Phake::when(Data::$db)->subscriptionPropertiesGet(Data::$user->id, 42)->thenReturn($this->feeds['db'][1]); Phake::when(Data::$db)->subscriptionPropertiesGet(Data::$user->id, 42)->thenReturn($this->feeds['db'][1]);
Phake::when(Data::$db)->editionLatest(Data::$user->id, (new Context)->subscription(2112))->thenReturn(0); Phake::when(Data::$db)->editionLatest(Data::$user->id, (new Context)->subscription(2112))->thenReturn(0);
Phake::when(Data::$db)->editionLatest(Data::$user->id, (new Context)->subscription( 42))->thenReturn(4758915); Phake::when(Data::$db)->editionLatest(Data::$user->id, (new Context)->subscription( 42))->thenReturn(4758915);
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 2112, ['folder' => 3])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing")); // folder ID 3 does not exist Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 2112, ['folder' => 3])->thenThrow(new ExceptionInput("idMissing")); // folder ID 3 does not exist
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, ['folder' => 8])->thenReturn(true); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, ['folder' => 8])->thenReturn(true);
// set up a mock for a bad feed // set up a mock for a bad feed
Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.net/news.atom")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.net/news.atom", new \PicoFeed\Client\InvalidUrlException())); Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.net/news.atom")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.net/news.atom", new \PicoFeed\Client\InvalidUrlException()));
@ -294,7 +478,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
} }
function testRemoveASubscription() { function testRemoveASubscription() {
Phake::when(Data::$db)->subscriptionRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); Phake::when(Data::$db)->subscriptionRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing"));
$exp = new Response(204); $exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1"))); $this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1")));
// fail on the second invocation because it no longer exists // fail on the second invocation because it no longer exists
@ -312,8 +496,8 @@ class TestNCNV1_2 extends Test\AbstractTest {
]; ];
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => 42])->thenReturn(true); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => 42])->thenReturn(true);
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => null])->thenReturn(true); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => null])->thenReturn(true);
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => 2112])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing")); // folder does not exist Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => 2112])->thenThrow(new ExceptionInput("idMissing")); // folder does not exist
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); // subscription does not exist Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // subscription does not exist
$exp = new Response(204); $exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[0]), 'application/json'))); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[0]), 'application/json')));
$exp = new Response(204); $exp = new Response(204);
@ -335,10 +519,10 @@ class TestNCNV1_2 extends Test\AbstractTest {
]; ];
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => null]))->thenReturn(true); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => null]))->thenReturn(true);
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => "Ook"]))->thenReturn(true); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => "Ook"]))->thenReturn(true);
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => " "]))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace")); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => " "]))->thenThrow(new ExceptionInput("whitespace"));
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => ""]))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing")); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => ""]))->thenThrow(new ExceptionInput("missing"));
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing")); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new ExceptionInput("missing"));
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing"));
$exp = new Response(204); $exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json'))); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json')));
$exp = new Response(204); $exp = new Response(204);
@ -379,7 +563,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
['feed' => 42], // invalid input ['feed' => 42], // invalid input
]; ];
Phake::when(Data::$db)->feedUpdate( 42)->thenReturn(true); Phake::when(Data::$db)->feedUpdate( 42)->thenReturn(true);
Phake::when(Data::$db)->feedUpdate(2112)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); Phake::when(Data::$db)->feedUpdate(2112)->thenThrow(new ExceptionInput("subjectMissing"));
$exp = new Response(204); $exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json'))); $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
$exp = new Response(404); $exp = new Response(404);
@ -393,4 +577,183 @@ class TestNCNV1_2 extends Test\AbstractTest {
$exp = new Response(403); $exp = new Response(403);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json'))); $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
} }
function testListArticles() {
$res = new Result($this->articles['db']);
$t = new \DateTime;
$in = [
['type' => 0, 'id' => 42],
['type' => 1, 'id' => 2112],
['type' => 2, 'id' => 0],
['type' => 3, 'id' => 0],
['oldestFirst' => true, 'batchSize' => 10, 'offset' => 5],
['oldestFirst' => false, 'batchSize' => 5, 'offset' => 5],
['getRead' => true],
['getRead' => false],
['lastModified' => $t->getTimestamp()],
];
Phake::when(Data::$db)->articleList(Data::$user->id, $this->anything())->thenReturn($res);
Phake::when(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->subscription(42))->thenThrow(new ExceptionInput("idMissing"));
Phake::when(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->folder(2112))->thenThrow(new ExceptionInput("idMissing"));
$exp = new Response(200, ['items' => $this->articles['rest']]);
// check the contents of the response
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/items"))); // first instance of base context
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/items/updated"))); // second instance of base context
// check error conditions
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[0]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[1]), 'application/json')));
// simply run through the remainder of the input for later method verification
$this->h->dispatch(new Request("GET", "/items", json_encode($in[2]), 'application/json'));
$this->h->dispatch(new Request("GET", "/items", json_encode($in[3]), 'application/json')); // third instance of base context
$this->h->dispatch(new Request("GET", "/items", json_encode($in[4]), 'application/json'));
$this->h->dispatch(new Request("GET", "/items", json_encode($in[5]), 'application/json'));
$this->h->dispatch(new Request("GET", "/items", json_encode($in[6]), 'application/json')); // fourth instance of base context
$this->h->dispatch(new Request("GET", "/items", json_encode($in[7]), 'application/json'));
$this->h->dispatch(new Request("GET", "/items", json_encode($in[8]), 'application/json'));
// perform method verifications
Phake::verify(Data::$db, Phake::times(4))->articleList(Data::$user->id, (new Context)->reverse(true));
Phake::verify(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->subscription(42));
Phake::verify(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->folder(2112));
Phake::verify(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->starred(true));
Phake::verify(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(false)->limit(10)->oldestEdition(6));
Phake::verify(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->limit(5)->latestEdition(4));
Phake::verify(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->unread(true));
Phake::verify(Data::$db)->articleList(Data::$user->id, (new Context)->reverse(true)->modifiedSince($t));
}
function testMarkAFolderRead() {
$read = ['read' => true];
$in = json_encode(['newestItemId' => 2112]);
Phake::when(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->folder(1)->latestEdition(2112))->thenReturn(true);
Phake::when(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->folder(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // folder doesn't exist
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read", $in, 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=2112")));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=ook")));
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/42/read", $in, 'application/json')));
}
function testMarkASubscriptionRead() {
$read = ['read' => true];
$in = json_encode(['newestItemId' => 2112]);
Phake::when(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->subscription(1)->latestEdition(2112))->thenReturn(true);
Phake::when(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->subscription(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // subscription doesn't exist
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read", $in, 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=2112")));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=ook")));
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/read", $in, 'application/json')));
}
function testMarkAllItemsRead() {
$read = ['read' => true];
$in = json_encode(['newestItemId' => 2112]);
Phake::when(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->latestEdition(2112))->thenReturn(true);
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read", $in, 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=2112")));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=ook")));
}
function testChangeMarksOfASingleArticle() {
$read = ['read' => true];
$unread = ['read' => false];
$star = ['starred' => true];
$unstar = ['starred' => false];
Phake::when(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->edition(1))->thenReturn(true);
Phake::when(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->edition(42))->thenThrow(new ExceptionInput("subjectMissing")); // edition doesn't exist doesn't exist
Phake::when(Data::$db)->articleMark(Data::$user->id, $unread, (new Context)->edition(2))->thenReturn(true);
Phake::when(Data::$db)->articleMark(Data::$user->id, $unread, (new Context)->edition(47))->thenThrow(new ExceptionInput("subjectMissing")); // edition doesn't exist doesn't exist
Phake::when(Data::$db)->articleMark(Data::$user->id, $star, (new Context)->article(3))->thenReturn(true);
Phake::when(Data::$db)->articleMark(Data::$user->id, $star, (new Context)->article(2112))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist
Phake::when(Data::$db)->articleMark(Data::$user->id, $unstar, (new Context)->article(4))->thenReturn(true);
Phake::when(Data::$db)->articleMark(Data::$user->id, $unstar, (new Context)->article(1337))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/1/read")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/2/unread")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/1/3/star")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/4400/4/unstar")));
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/42/read")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/47/unread")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/1/2112/star")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/4400/1337/unstar")));
Phake::verify(Data::$db, Phake::times(8))->articleMark(Data::$user->id, $this->anything(), $this->anything());
}
function testChangeMarksOfMultipleArticles() {
$read = ['read' => true];
$unread = ['read' => false];
$star = ['starred' => true];
$unstar = ['starred' => false];
$in = [
["ook","eek","ack"],
range(100,199),
range(100,149),
range(150,199),
];
$inStar = $in;
for($a = 0; $a < sizeof($inStar); $a++) {
for($b = 0; $b < sizeof($inStar[$a]); $b++) {
$inStar[$a][$b] = ['feedId' => 2112, 'guidHash' => $inStar[$a][$b]];
}
}
Phake::when(Data::$db)->articleMark(Data::$user->id, $this->anything(), $this->anything())->thenReturn(true);
Phake::when(Data::$db)->articleMark(Data::$user->id, $this->anything(), (new Context)->editions([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
Phake::when(Data::$db)->articleMark(Data::$user->id, $this->anything(), (new Context)->editions($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function for limited to 50 items for multiples
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => "ook"]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => "ook"]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => "ook"]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => "ook"]), 'application/json')));
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => []]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => []]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[0]]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[0]]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[1]]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[1]]), 'application/json')));
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => []]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => []]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[0]]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[0]]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
// ensure the data model was queried appropriately for read/unread
Phake::verify(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->editions([]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->editions($in[0]));
Phake::verify(Data::$db, Phake::times(0))->articleMark(Data::$user->id, $read, (new Context)->editions($in[1]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->editions($in[2]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $read, (new Context)->editions($in[3]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unread, (new Context)->editions([]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unread, (new Context)->editions($in[0]));
Phake::verify(Data::$db, Phake::times(0))->articleMark(Data::$user->id, $unread, (new Context)->editions($in[1]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unread, (new Context)->editions($in[2]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unread, (new Context)->editions($in[3]));
// ensure the data model was queried appropriately for star/unstar
Phake::verify(Data::$db)->articleMark(Data::$user->id, $star, (new Context)->articles([]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $star, (new Context)->articles($in[0]));
Phake::verify(Data::$db, Phake::times(0))->articleMark(Data::$user->id, $star, (new Context)->articles($in[1]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $star, (new Context)->articles($in[2]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $star, (new Context)->articles($in[3]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unstar, (new Context)->articles([]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unstar, (new Context)->articles($in[0]));
Phake::verify(Data::$db, Phake::times(0))->articleMark(Data::$user->id, $unstar, (new Context)->articles($in[1]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unstar, (new Context)->articles($in[2]));
Phake::verify(Data::$db)->articleMark(Data::$user->id, $unstar, (new Context)->articles($in[3]));
}
} }