Browse Source

Fix most subscription tests

Two tests depend upon article functions which will be addressed in due
course.
redup
J. King 2 years ago
parent
commit
6958c24be2
  1. 63
      lib/Database.php
  2. 2
      tests/cases/Database/AbstractTest.php
  3. 195
      tests/cases/Database/SeriesSubscription.php
  4. 5
      tests/lib/AbstractTest.php

63
lib/Database.php

@ -818,17 +818,17 @@ class Database {
$url = URL::normalize($url, $fetchUser, $fetchPassword);
// if discovery is enabled, check to see if the feed already exists; this will save us some network latency if it does
if ($discover) {
$id = $this->db->prepare("SELECT id from arsse_subscriptions where ownerr = ? and url = ?", "str", "str")->run($user, $url)->getValue();
$id = $this->db->prepare("SELECT id from arsse_subscriptions where owner = ? and url = ?", "str", "str")->run($user, $url)->getValue();
if (!$id) {
// if it doesn't exist, perform discovery
$url = Feed::discover($url);
}
}
try {
return (int) $this->db->prepare('INSERT INTO arsse_feeds(owner, url, deleted) values(?,?,?)', 'str', 'str', 'bool')->run($user, $url, 1)->lastId();
return (int) $this->db->prepare('INSERT INTO arsse_subscriptions(owner, url, deleted) values(?,?,?)', 'str', 'str', 'bool')->run($user, $url, 1)->lastId();
} catch (Db\ExceptionInput $e) {
// if the insertion fails, throw if the delete flag is not set, otherwise return the existing ID
$id = (int) $this->db->prepare("SELECT id from arsse_subscriptions where owner = ? and url = ? and deleted = 1")->run($user, $url)->getValue();
$id = (int) $this->db->prepare("SELECT id from arsse_subscriptions where owner = ? and url = ? and deleted = 1", "str", "str")->run($user, $url)->getValue();
if (!$id) {
throw $e;
} else {
@ -857,7 +857,7 @@ class Database {
*/
public function subscriptionReveal(string $user, int ...$id): void {
[$inClause, $inTypes, $inValues] = $this->generateIn($id, "int");
$this->db->prepare("UPDATE arsse_subscriptions set deleted = 0, modified = CURRENT_TIMESTAMP where deleted = 1 and user = ? and id in ($inClause)", "str", $inTypes)->run($user, $inValues);
$this->db->prepare("UPDATE arsse_subscriptions set deleted = 0, modified = CURRENT_TIMESTAMP where deleted = 1 and owner = ? and id in ($inClause)", "str", $inTypes)->run($user, $inValues);
}
/** Lists a user's subscriptions, returning various data
@ -865,7 +865,7 @@ class Database {
* Each record has the following keys:
*
* - "id": The numeric identifier of the subscription
* - "feed": The numeric identifier of the underlying newsfeed
* - "feed": The numeric identifier of the subscription (historical)
* - "url": The URL of the newsfeed, after discovery and HTTP redirects
* - "title": The title of the newsfeed
* - "source": The URL of the source of the newsfeed i.e. its parent Web site
@ -908,43 +908,43 @@ class Database {
)
select
s.id as id,
s.feed as feed,
f.url,source,pinned,err_count,err_msg,order_type,added,keep_rule,block_rule,f.etag,s.scrape,
f.updated as updated,
f.modified as edited,
s.id as feed,
s.url,source,pinned,err_count,err_msg,order_type,added,keep_rule,block_rule,s.etag,s.scrape,
s.updated as updated,
s.modified as edited,
s.modified as modified,
f.next_fetch,
s.next_fetch,
case when i.data is not null then i.id end as icon_id,
i.url as icon_url,
folder, t.top as top_folder, d.name as folder_name, dt.name as top_folder_name,
coalesce(s.title, f.title) as title,
coalesce(s.title, s.feed_title) as title,
cast(coalesce((articles - hidden - marked), coalesce(articles,0)) as $integerType) as unread -- this cast is required for MySQL for unclear reasons
from arsse_subscriptions as s
join arsse_feeds as f on f.id = s.feed
left join topmost as t on t.f_id = s.folder
left join arsse_folders as d on s.folder = d.id
left join arsse_folders as dt on t.top = dt.id
left join arsse_icons as i on i.id = f.icon
left join arsse_icons as i on i.id = s.icon
left join (
select
feed,
subscription,
count(*) as articles
from arsse_articles
group by feed
) as article_stats on article_stats.feed = s.feed
group by subscription
) as article_stats on article_stats.subscription = s.id
left join (
select
subscription,
sum(hidden) as hidden,
sum(case when \"read\" = 1 and hidden = 0 then 1 else 0 end) as marked
from arsse_marks group by subscription
from arsse_articles group by subscription
) as mark_stats on mark_stats.subscription = s.id",
["str", "int"],
[$user, $folder]
);
$q->setWhere("s.owner = ?", ["str"], [$user]);
$q->setWhere("s.deleted = 0");
$nocase = $this->db->sqlToken("nocase");
$q->setOrder("pinned desc, coalesce(s.title, f.title) collate $nocase");
$q->setOrder("pinned desc, coalesce(s.title, s.feed_title) collate $nocase");
if ($id) {
// if an ID is specified, add a suitable WHERE condition and bindings
// this condition facilitates the implementation of subscriptionPropertiesGet, which would otherwise have to duplicate the complex query; it takes precedence over a specified folder
@ -978,6 +978,7 @@ class Database {
[$folder]
);
$q->setWhere("owner = ?", "str", $user);
$q->setWhere("deleted = 0");
if ($folder) {
// if the specified folder exists, add a suitable WHERE condition
$q->setWhere("folder in (select folder from folders)");
@ -996,7 +997,7 @@ class Database {
if (!V::id($id)) {
throw new Db\ExceptionInput("typeViolation", ["action" => __FUNCTION__, "field" => "feed", 'type' => "int > 0"]);
}
$changes = $this->db->prepare("DELETE from arsse_subscriptions where owner = ? and id = ?", "str", "int")->run($user, $id)->changes();
$changes = $this->db->prepare("UPDATE arsse_subscriptions set deleted = 1, modified = CURRENT_TIMESTAMP where owner = ? and id = ? and deleted = 0", "str", "int")->run($user, $id)->changes();
if (!$changes) {
throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id]);
}
@ -1119,8 +1120,9 @@ class Database {
*/
public function subscriptionIcon(?string $user, int $id, bool $includeData = true): ?array {
$data = $includeData ? "i.data" : "null as data";
$q = new Query("SELECT i.id, i.url, i.type, $data from arsse_subscriptions as s join arsse_feeds as f on s.feed = f.id left join arsse_icons as i on f.icon = i.id");
$q = new Query("SELECT i.id, i.url, i.type, $data from arsse_subscriptions as s left join arsse_icons as i on s.icon = i.id");
$q->setWhere("s.id = ?", "int", $id);
$q->setWhere("s.deleted = 0");
if (isset($user)) {
$q->setWhere("s.owner = ?", "str", $user);
}
@ -1135,10 +1137,11 @@ class Database {
/** Returns the time at which any of a user's subscriptions (or a specific subscription) was last refreshed, as a DateTimeImmutable object */
public function subscriptionRefreshed(string $user, int $id = null): ?\DateTimeImmutable {
$q = new Query("SELECT max(arsse_feeds.updated) from arsse_feeds join arsse_subscriptions on arsse_subscriptions.feed = arsse_feeds.id");
$q->setWhere("arsse_subscriptions.owner = ?", "str", $user);
$q = new Query("SELECT max(updated) from arsse_subscriptions");
$q->setWhere("owner = ?", "str", $user);
$q->setWhere("deleted = 0");
if ($id) {
$q->setWhere("arsse_subscriptions.id = ?", "int", $id);
$q->setWhere("id = ?", "int", $id);
}
$out = $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue();
if (!$out && $id) {
@ -1155,17 +1158,17 @@ class Database {
protected function subscriptionRulesApply(string $user, int $id): void {
// start a transaction for read isolation
$tr = $this->begin();
$sub = $this->db->prepare("SELECT feed, coalesce(keep_rule, '') as keep, coalesce(block_rule, '') as block from arsse_subscriptions where owner = ? and id = ?", "str", "int")->run($user, $id)->getRow();
$sub = $this->db->prepare("SELECT id, coalesce(keep_rule, '') as keep, coalesce(block_rule, '') as block from arsse_subscriptions where owner = ? and id = ?", "str", "int")->run($user, $id)->getRow();
try {
$keep = Rule::prep($sub['keep']);
$block = Rule::prep($sub['block']);
$feed = $sub['feed'];
$feed = $sub['id'];
} catch (RuleException $e) { // @codeCoverageIgnore
// invalid rules should not normally appear in the database, but it's possible
// in this case we should halt evaluation and just leave things as they are
return; // @codeCoverageIgnore
}
$articles = $this->db->prepare("SELECT id, title, coalesce(categories, 0) as categories from arsse_articles as a left join (select article, count(*) as categories from arsse_categories group by article) as c on a.id = c.article where a.feed = ?", "int")->run($feed)->getAll();
$articles = $this->db->prepare("SELECT id, title, coalesce(categories, 0) as categories from arsse_articles as a left join (select article, count(*) as categories from arsse_categories group by article) as c on a.id = c.article where a.subscription = ?", "int")->run($id)->getAll();
$hide = [];
$unhide = [];
foreach ($articles as $r) {
@ -1196,12 +1199,14 @@ class Database {
* @param string $user The user who owns the subscription to be validated
* @param integer $id The identifier of the subscription to validate
* @param boolean $subject Whether the subscription is the subject (true) rather than the object (false) of the operation being performed; this only affects the semantics of the error message if validation fails
* @param boolean $acceptDeleted Whether to consider a soft-deleted subscription as valid
*/
protected function subscriptionValidateId(string $user, $id, bool $subject = false): array {
protected function subscriptionValidateId(string $user, $id, bool $subject = false, bool $acceptDeleted = false): array {
if (!V::id($id)) {
throw new Db\ExceptionInput("typeViolation", ["action" => $this->caller(), "field" => "feed", 'type' => "int > 0"]);
}
$out = $this->db->prepare("SELECT id,feed from arsse_subscriptions where id = ? and owner = ?", "int", "str")->run($id, $user)->getRow();
$deleted = $acceptDeleted ? "deleted" : "0";
$out = $this->db->prepare("SELECT id, id from arsse_subscriptions where id = ? and owner = ? and deleted = $deleted", "int", "str")->run($id, $user)->getRow();
if (!$out) {
throw new Db\ExceptionInput($subject ? "subjectMissing" : "idMissing", ["action" => $this->caller(), "field" => "subscription", 'id' => $id]);
}
@ -2175,7 +2180,7 @@ class Database {
"SELECT articles.article as article, max(arsse_editions.id) as edition from (
select arsse_articles.id as article
FROM arsse_articles
join arsse_subscriptions on arsse_subscriptions.feed = arsse_articles.feed
join arsse_subscriptions on arsse_subscriptions.id = arsse_articles.subscription
WHERE arsse_articles.id = ? and arsse_subscriptions.owner = ?
) as articles left join arsse_editions on arsse_editions.article = articles.article group by articles.article",
["int", "str"]

2
tests/cases/Database/AbstractTest.php

@ -19,7 +19,7 @@ abstract class AbstractTest extends \JKingWeb\Arsse\Test\AbstractTest {
use SeriesFolder;
//use SeriesFeed;
use SeriesIcon;
//use SeriesSubscription;
use SeriesSubscription;
//use SeriesLabel;
use SeriesTag;
//use SeriesArticle;

195
tests/cases/Database/SeriesSubscription.php

@ -10,6 +10,7 @@ use GuzzleHttp\Exception\ClientException;
use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\Test\Database;
use JKingWeb\Arsse\Feed\Exception as FeedException;
use JKingWeb\Arsse\Misc\Date;
trait SeriesSubscription {
public function setUpSeriesSubscription(): void {
@ -42,14 +43,15 @@ trait SeriesSubscription {
],
],
'arsse_subscriptions' => [
'columns' => ["id", "owner", "url", "feed_title", "updated", "next_fetch", "icon", "title", "folder", "pinned", "order_type", "keep_rule", "block_rule", "scrape"],
'columns' => ["id", "owner", "url", "feed_title", "updated", "next_fetch", "icon", "title", "folder", "pinned", "order_type", "keep_rule", "block_rule", "scrape", "deleted", "modified"],
'rows' => [
[1, "john.doe@example.com", "http://example.com/feed2", "eek", strtotime("now - 1 hour"), strtotime("now - 1 hour"), 1, null, null, 1, 2, null, null, 0],
[2, "jane.doe@example.com", "http://example.com/feed2", "eek", strtotime("now - 1 hour"), strtotime("now - 1 hour"), 1, null, null, 0, 0, null, null, 0],
[3, "john.doe@example.com", "http://example.com/feed3", "Ack", strtotime("now + 1 hour"), strtotime("now + 1 hour"), 2, "Ook", 2, 0, 1, null, null, 0],
[4, "jill.doe@example.com", "http://example.com/feed2", "eek", strtotime("now - 1 hour"), strtotime("now - 1 hour"), 1, null, null, 0, 0, null, null, 0],
[5, "jack.doe@example.com", "http://example.com/feed2", "eek", strtotime("now - 1 hour"), strtotime("now - 1 hour"), 1, null, null, 1, 2, "", "3|E", 0],
[6, "john.doe@example.com", "http://example.com/feed4", "Foo", strtotime("now + 1 hour"), strtotime("now + 1 hour"), null, "Bar", 3, 0, 0, null, null, 0],
[1, "john.doe@example.com", "http://example.com/feed2", "eek", Date::transform("now - 1 hour", "sql"), Date::transform("now - 1 hour", "sql"), 1, null, null, 1, 2, null, null, 0, 0, Date::transform("now - 1 hour", "sql")],
[2, "jane.doe@example.com", "http://example.com/feed2", "eek", Date::transform("now - 1 hour", "sql"), Date::transform("now - 1 hour", "sql"), 1, null, null, 0, 0, null, null, 0, 0, Date::transform("now - 1 hour", "sql")],
[3, "john.doe@example.com", "http://example.com/feed3", "Ack", Date::transform("now + 1 hour", "sql"), Date::transform("now + 1 hour", "sql"), 2, "Ook", 2, 0, 1, null, null, 0, 0, Date::transform("now - 1 hour", "sql")],
[4, "jill.doe@example.com", "http://example.com/feed2", "eek", Date::transform("now - 1 hour", "sql"), Date::transform("now - 1 hour", "sql"), 1, null, null, 0, 0, null, null, 0, 0, Date::transform("now - 1 hour", "sql")],
[5, "jack.doe@example.com", "http://example.com/feed2", "eek", Date::transform("now - 1 hour", "sql"), Date::transform("now - 1 hour", "sql"), 1, null, null, 1, 2, "", "3|E", 0, 0, Date::transform("now - 1 hour", "sql")],
[6, "john.doe@example.com", "http://example.com/feed4", "Foo", Date::transform("now + 1 hour", "sql"), Date::transform("now + 1 hour", "sql"), null, "Bar", 3, 0, 0, null, null, 0, 0, Date::transform("now - 1 hour", "sql")],
[7, "john.doe@example.com", "http://example.com/feed1", "ook", Date::transform("now + 6 hour", "sql"), Date::transform("now - 1 hour", "sql"), null, null, null, 0, 0, null, null, 0, 1, Date::transform("now - 1 hour", "sql")],
],
],
'arsse_tags' => [
@ -159,102 +161,99 @@ trait SeriesSubscription {
unset($this->data, $this->user);
}
public function testAddASubscriptionToAnExistingFeed(): void {
public function testReserveASubscription(): void {
$url = "http://example.com/feed5";
$exp = $this->nextID("arsse_subscriptions");
$act = Arsse::$db->subscriptionReserve($this->user, $url, "", "", false);
$this->assertSame($exp, $act);
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$state['arsse_subscriptions']['rows'][] = [$exp, $this->user, $url, 1, Date::transform("now", "sql")];
$this->compareExpectations(static::$drv, $state);
}
public function testReserveADeletedSubscription(): void {
$url = "http://example.com/feed1";
$subID = $this->nextID("arsse_subscriptions");
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->returns(true);
Arsse::$db = $db->get();
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url));
$db->feedUpdate->never()->called();
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ['id', 'owner', 'feed', 'url']]);
$state['arsse_subscriptions']['rows'][] = [$subID, $this->user, "http://example.com/feed1"];
$exp = 7;
$act = Arsse::$db->subscriptionReserve($this->user, $url, "", "", false);
$this->assertSame($exp, $act);
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$state['arsse_subscriptions']['rows'][6] = [$exp, $this->user, $url, 1, Date::transform("now", "sql")];
$this->compareExpectations(static::$drv, $state);
}
public function testAddASubscriptionToANewFeed(): void {
$url = "http://example.org/feed1";
$feedID = $this->nextID("arsse_feeds");
$subID = $this->nextID("arsse_subscriptions");
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->returns(true);
Arsse::$db = $db->get();
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url, "", "", false));
$db->feedUpdate->calledWith($feedID, true, false);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
]);
$state['arsse_feeds']['rows'][] = [$feedID,$url,"",""];
$state['arsse_subscriptions']['rows'][] = [$subID,$this->user,$feedID];
public function testReserveASubscriptionWithPassword(): void {
$url = "http://john:secret@example.com/feed5";
$exp = $this->nextID("arsse_subscriptions");
$act = Arsse::$db->subscriptionReserve($this->user, "http://example.com/feed5", "john", "secret", false);
$this->assertSame($exp, $act);
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$state['arsse_subscriptions']['rows'][] = [$exp, $this->user, $url, 1, Date::transform("now", "sql")];
$this->compareExpectations(static::$drv, $state);
}
public function testAddASubscriptionToANewFeedViaDiscovery(): void {
$url = "http://localhost:8000/Feed/Discovery/Valid";
$discovered = "http://localhost:8000/Feed/Discovery/Feed";
$feedID = $this->nextID("arsse_feeds");
$subID = $this->nextID("arsse_subscriptions");
public function testReserveADuplicateSubscription(): void {
$url = "http://example.com/feed2";
$this->assertException("constraintViolation", "Db", "ExceptionInput");
Arsse::$db->subscriptionReserve($this->user, $url, "", "", false);
}
public function testReserveASubscriptionWithDiscovery(): void {
$exp = $this->nextID("arsse_subscriptions");
$act = Arsse::$db->subscriptionReserve($this->user, "http://localhost:8000/Feed/Discovery/Valid");
$this->assertSame($exp, $act);
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$state['arsse_subscriptions']['rows'][] = [$exp, $this->user, "http://localhost:8000/Feed/Discovery/Feed", 1, Date::transform("now", "sql")];
$this->compareExpectations(static::$drv, $state);
}
public function testRevealASubscription(): void {
$url = "http://example.com/feed1";
$this->assertNull(Arsse::$db->subscriptionReveal($this->user, 1, 7));
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$state['arsse_subscriptions']['rows'][6] = [7, $this->user, $url, 0, Date::transform("now", "sql")];
$this->compareExpectations(static::$drv, $state);
}
public function testAddASubscription(): void {
$url = "http://example.org/feed5";
$id = $this->nextID("arsse_subscriptions");
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->returns(true);
$db->subscriptionUpdate->returns(true);
$db->subscriptionPropertiesSet->returns(true);
Arsse::$db = $db->get();
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url, "", "", true));
$db->feedUpdate->calledWith($feedID, true, false);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
]);
$state['arsse_feeds']['rows'][] = [$feedID,$discovered,"",""];
$state['arsse_subscriptions']['rows'][] = [$subID,$this->user,$feedID];
$this->compareExpectations(static::$drv, $state);
try {
$this->assertSame($id, Arsse::$db->subscriptionAdd($this->user, $url, "", "", false, ['order_type' => 2]));
} finally {
$db->subscriptionUpdate->calledWith($this->user, $id, true);
$db->subscriptionPropertiesSet->calledWith($this->user, $id, ['order_type' => 2]);
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$state['arsse_subscriptions']['rows'][] = [$id, $this->user, $url, 0, Date::transform("now", "sql")];
$this->compareExpectations(static::$drv, $state);
}
}
public function testAddASubscriptionToAnInvalidFeed(): void {
$url = "http://example.org/feed1";
$feedID = $this->nextID("arsse_feeds");
$url = "http://example.org/feed5";
$id = $this->nextID("arsse_subscriptions");
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->throws(new FeedException("", ['url' => $url], $this->mockGuzzleException(ClientException::class, "", 404)));
$db->subscriptionUpdate->throws(new FeedException("", ['url' => $url], $this->mockGuzzleException(ClientException::class, "", 404)));
$db->subscriptionPropertiesSet->returns(true);
Arsse::$db = $db->get();
$this->assertException("invalidUrl", "Feed");
try {
Arsse::$db->subscriptionAdd($this->user, $url, "", "", false);
Arsse::$db->subscriptionAdd($this->user, $url, "", "", false, ['order_type' => 2]);
} finally {
$db->feedUpdate->calledWith($feedID, true, false);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
]);
$db->subscriptionUpdate->calledWith($this->user, $id, true);
$db->subscriptionPropertiesSet->calledWith($this->user, $id, ['order_type' => 2]);
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$this->compareExpectations(static::$drv, $state);
}
}
public function testAddADuplicateSubscription(): void {
$url = "http://example.com/feed2";
$this->assertException("constraintViolation", "Db", "ExceptionInput");
Arsse::$db->subscriptionAdd($this->user, $url);
}
public function testAddADuplicateSubscriptionWithEquivalentUrl(): void {
$url = "http://EXAMPLE.COM/feed2";
$this->assertException("constraintViolation", "Db", "ExceptionInput");
Arsse::$db->subscriptionAdd($this->user, $url);
}
public function testAddADuplicateSubscriptionViaRedirection(): void {
$url = "http://localhost:8000/Feed/Parsing/Valid";
Arsse::$db->subscriptionAdd($this->user, $url);
$subID = $this->nextID("arsse_subscriptions");
$url = "http://localhost:8000/Feed/Fetching/RedirectionDuplicate";
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url));
}
public function testRemoveASubscription(): void {
$this->assertTrue(Arsse::$db->subscriptionRemove($this->user, 1));
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
]);
array_shift($state['arsse_subscriptions']['rows']);
$state = $this->primeExpectations($this->data, ['arsse_subscriptions' => ["id", "owner", "url", "deleted", "modified"]]);
$state['arsse_subscriptions']['rows'][0] = [1, $this->user, "http://example.com/feed2", 1, Date::transform("now", "sql")];
$this->compareExpectations(static::$drv, $state);
}
@ -263,6 +262,11 @@ trait SeriesSubscription {
Arsse::$db->subscriptionRemove($this->user, 2112);
}
public function testRemoveADeletedSubscription(): void {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
Arsse::$db->subscriptionRemove($this->user, 7);
}
public function testRemoveAnInvalidSubscription(): void {
$this->assertException("typeViolation", "Db", "ExceptionInput");
Arsse::$db->subscriptionRemove($this->user, -1);
@ -384,12 +388,18 @@ trait SeriesSubscription {
Arsse::$db->subscriptionPropertiesGet($this->user, 2112);
}
public function testGetThePropertiesOfADeletedSubscription(): void {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
Arsse::$db->subscriptionPropertiesGet($this->user, 7);
}
public function testGetThePropertiesOfAnInvalidSubscription(): void {
$this->assertException("typeViolation", "Db", "ExceptionInput");
Arsse::$db->subscriptionPropertiesGet($this->user, -1);
}
public function testSetThePropertiesOfASubscription(): void {
$this->markTestIncomplete();
Arsse::$db->subscriptionPropertiesSet($this->user, 1, [
'title' => "Ook Ook",
'folder' => 3,
@ -400,17 +410,16 @@ trait SeriesSubscription {
'block_rule' => "eek",
]);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password','title'],
'arsse_subscriptions' => ['id','owner','feed','title','folder','pinned','order_type','keep_rule','block_rule','scrape'],
'arsse_subscriptions' => ['id','owner','feed_title', 'title','folder','pinned','order_type','keep_rule','block_rule','scrape'],
]);
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com",2,"Ook Ook",3,0,0,"ook","eek",1];
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com","eek","Ook Ook",3,0,0,"ook","eek",1];
$this->compareExpectations(static::$drv, $state);
Arsse::$db->subscriptionPropertiesSet($this->user, 1, [
'title' => null,
'keep_rule' => null,
'block_rule' => null,
]);
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com",2,null,3,0,0,null,null,1];
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com","eek",null,3,0,0,null,null,1];
$this->compareExpectations(static::$drv, $state);
// making no changes is a valid result
Arsse::$db->subscriptionPropertiesSet($this->user, 1, ['unhinged' => true]);
@ -470,6 +479,11 @@ trait SeriesSubscription {
Arsse::$db->subscriptionIcon(null, -2112);
}
public function testRetrieveTheFaviconOfADeletedSubscription(): void {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
Arsse::$db->subscriptionIcon(null, 7);
}
public function testRetrieveTheFaviconOfASubscriptionWithUser(): void {
$exp = "http://example.com/favicon.ico";
$user = "john.doe@example.com";
@ -497,6 +511,11 @@ trait SeriesSubscription {
Arsse::$db->subscriptionTagsGet($this->user, 101);
}
public function testListTheTagsOfADeletedSubscription(): void {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
Arsse::$db->subscriptionTagsGet($this->user, 7);
}
public function testGetRefreshTimeOfASubscription(): void {
$user = "john.doe@example.com";
$this->assertTime(strtotime("now + 1 hour"), Arsse::$db->subscriptionRefreshed($user));
@ -505,10 +524,16 @@ trait SeriesSubscription {
public function testGetRefreshTimeOfAMissingSubscription(): void {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
$this->assertTime(strtotime("now - 1 hour"), Arsse::$db->subscriptionRefreshed("john.doe@example.com", 2));
Arsse::$db->subscriptionRefreshed("john.doe@example.com", 2);
}
public function testGetRefreshTimeOfADeletedSubscription(): void {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
Arsse::$db->subscriptionRefreshed("john.doe@example.com", 7);
}
public function testSetTheFilterRulesOfASubscriptionCheckingMarks(): void {
$this->markTestIncomplete();
Arsse::$db->subscriptionPropertiesSet("jack.doe@example.com", 5, ['keep_rule' => "1|B|3|D", 'block_rule' => "4"]);
$state = $this->primeExpectations($this->data, ['arsse_marks' => ['article', 'subscription', 'hidden']]);
$state['arsse_marks']['rows'][9][2] = 0;

5
tests/lib/AbstractTest.php

@ -508,7 +508,7 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
foreach ($tableSpecs as $table => $columns) {
// make sure the source has the table we want
if (!isset($source[$table])) {
throw new Exception("Source for expectations does not contain requested table $table.");
throw new \Exception("Source for expectations does not contain requested table $table.");
}
// fill the output, particularly the correct number of (empty) rows
$rows = sizeof($source[$table]['rows']);
@ -519,7 +519,7 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
// fill the rows with the requested data, column-wise
foreach ($columns as $c) {
if (($index = array_search($c, $source[$table]['columns'], true)) === false) {
throw new exception("Expected column $table.$c is not present in test data");
throw new \Exception("Expected column $table.$c is not present in test data");
}
for ($a = 0; $a < $rows; $a++) {
$out[$table]['rows'][$a][] = $source[$table]['rows'][$a][$index];
@ -535,7 +535,6 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
if (static::$stringOutput ?? false) {
$expected = $this->stringify($expected);
}
$this->assertCount(sizeof($expected), $data, "Number of result rows (".sizeof($data).") differs from number of expected rows (".sizeof($expected).")");
if (sizeof($expected)) {
// make sure the expectations are consistent
foreach ($expected as $exp) {

Loading…
Cancel
Save