mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
First tests for articleList
Fixed many errors in queries: - Prepared statement wasn't running at all :) - Returning feed ID rather than subscription ID - latestEdition wasn't matching
This commit is contained in:
parent
7e458dddbc
commit
ee9d8c1ceb
4 changed files with 232 additions and 22 deletions
|
@ -619,9 +619,9 @@ class Database {
|
|||
if(!$context) $context = new Context;
|
||||
$q = new Query(
|
||||
"SELECT
|
||||
arsse_articles.id,
|
||||
arsse_articles.url,
|
||||
title,author,content,feed,guid,
|
||||
arsse_articles.id as id,
|
||||
arsse_articles.url as url,
|
||||
title,author,content,guid,
|
||||
DATEFORMAT(?, published) as published,
|
||||
DATEFORMAT(?, edited) as edited,
|
||||
DATEFORMAT(?, max(
|
||||
|
@ -630,7 +630,8 @@ class Database {
|
|||
)) as modified,
|
||||
NOT (SELECT count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and read is 1) as unread,
|
||||
(SELECT count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and starred is 1) as starred,
|
||||
(SELECT max(id) from arsse_editions where article is arsse_articles.id) as latestEdition,
|
||||
(SELECT max(id) from arsse_editions where article is arsse_articles.id) as edition,
|
||||
subscribed_feeds.sub as subscription,
|
||||
url_title_hash||':'||url_content_hash||':'||title_content_hash as fingerprint,
|
||||
arsse_enclosures.url as media_url,
|
||||
arsse_enclosures.type as media_type
|
||||
|
@ -639,7 +640,7 @@ class Database {
|
|||
left join arsse_enclosures on arsse_enclosures.article is arsse_articles.id
|
||||
",
|
||||
"", // WHERE clause
|
||||
"latestEdition".(!$context->reverse ? " desc" : ""), // ORDER BY clause
|
||||
"edition".(!$context->reverse ? " desc" : ""), // ORDER BY clause
|
||||
$context->limit,
|
||||
$context->offset
|
||||
);
|
||||
|
@ -648,31 +649,29 @@ class Database {
|
|||
// if a subscription is specified, make sure it exists
|
||||
$id = $this->subscriptionValidateId($user, $context->subscription)['feed'];
|
||||
// add a basic CTE that will join in only the requested subscription
|
||||
$q->setCTE("subscribed_feeds(id) as (SELECT ?)", "int", $id);
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT ?,?)", ["int","int"], [$id,$context->subscription]);
|
||||
} else if($context->folder()) {
|
||||
// if a folder is specified, make sure it exists
|
||||
$this->folderValidateId($user, $context->folder);
|
||||
// if it does exist, add a common table expression to list it and its children so that we select from the entire subtree
|
||||
$q->setCTE("folders(folder) as (SELECT ? union select id from arsse_folders join folders on parent is folder)", "int", $context->folder);
|
||||
// add another CTE for the subscriptions within the folder
|
||||
$q->setCTE("subscribed_feeds(id) as (SELECT feed from arsse_subscriptions join user on user is owner join folders on arsse_subscription.folder is folders.folder)");
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner join folders on arsse_subscriptions.folder is folders.folder)");
|
||||
} else {
|
||||
// otherwise add a CTE for all the user's subscriptions
|
||||
$q->setCTE("subscribed_feeds(id) as (SELECT feed from arsse_subscriptions join user on user is owner)");
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner)");
|
||||
}
|
||||
// filter based on edition offset
|
||||
if($context->oldestEdition()) {
|
||||
$q->setWhere("latestEdition >= ?", "int", $context->oldestEdition);
|
||||
} else if($context->latestEdition()) {
|
||||
$q->setWhere("latestEdition <= ?", "int", $context->oldestEdition);
|
||||
}
|
||||
if($context->oldestEdition()) $q->setWhere("edition >= ?", "int", $context->oldestEdition);
|
||||
if($context->latestEdition()) $q->setWhere("edition <= ?", "int", $context->latestEdition);
|
||||
// filter based on lastmod time
|
||||
if($context->modifiedSince()) $q->setWhere("modified >= ?", "datetime", $context->modifiedSince);
|
||||
if($context->notModifiedSince()) $q->setWhere("modified <= ?", "datetime", $context->notModifiedSince);
|
||||
// filter for un/read and un/starred status if specified
|
||||
if($context->unread()) $q->setWhere("unread is ?", "bool", $context->unread);
|
||||
if($context->starred()) $q->setWhere("starred is ?", "bool", $context->starred);
|
||||
// perform the query and return results
|
||||
return $this->db->prepare($q, "str", "str", "str")-run($this->dateFormatDefault, $this->dateFormatDefault, $this->dateFormatDefault);
|
||||
return $this->db->prepare($q, "str", "str", "str")->run($this->dateFormatDefault, $this->dateFormatDefault, $this->dateFormatDefault);
|
||||
}
|
||||
|
||||
public function articlePropertiesSet(string $user, array $data, Context $context = null): bool {
|
||||
|
@ -712,8 +711,11 @@ class Database {
|
|||
$q = new Query(
|
||||
"SELECT
|
||||
arsse_articles.id as id,
|
||||
max(arsse_editions.id) as latestEdition
|
||||
(select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id) as exists
|
||||
max(arsse_editions.id) as edition
|
||||
(select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id) as exists,
|
||||
max(modified,
|
||||
coalesce((SELECT modified from arsse_marks join user on user is owner where article is arsse_articles.id),'')
|
||||
) as modified,
|
||||
FROM arsse_articles
|
||||
join subscribed_feeds on feed is subscribed_feeds.id
|
||||
join arsse_editions on arsse_articles.id is arsse_editions.article
|
||||
|
@ -724,26 +726,27 @@ class Database {
|
|||
// if a subscription is specified, make sure it exists
|
||||
$id = $this->subscriptionValidateId($user, $context->subscription)['feed'];
|
||||
// add a basic CTE that will join in only the requested subscription
|
||||
$q->setCTE("subscribed_feeds(id) as (SELECT ?)", "int", $id);
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT ?,?)", ["int","int"], [$id,$context->subscription]);
|
||||
} else if($context->folder()) {
|
||||
// if a folder is specified, make sure it exists
|
||||
$this->folderValidateId($user, $context->folder);
|
||||
// if it does exist, add a common table expression to list it and its children so that we select from the entire subtree
|
||||
$q->setCTE("folders(folder) as (SELECT ? union select id from arsse_folders join folders on parent is folder)", "int", $context->folder);
|
||||
// add another CTE for the subscriptions within the folder
|
||||
$q->setCTE("subscribed_feeds(id) as (SELECT feed from arsse_subscriptions join user on user is owner join folders on arsse_subscription.folder is folders.folder)");
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner join folders on arsse_subscriptions.folder is folders.folder)");
|
||||
} else {
|
||||
// otherwise add a CTE for all the user's subscriptions
|
||||
$q->setCTE("subscribed_feeds(id) as (SELECT feed from arsse_subscriptions join user on user is owner)");
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner)");
|
||||
}
|
||||
// filter for specific article or edition
|
||||
if($context->article()) $q->setWhere("arsse_article.id is ?", "int", $context->article);
|
||||
if($context->edition()) $q->setWhere("arsse_article.id is (SELECT article from arsse_editions where id is ?)", "int", $context->edition);
|
||||
// filter for un/read and un/starred status if specified
|
||||
if($context->unread()) $q->setWhere("unread is ?", "bool", $context->unread);
|
||||
if($context->starred()) $q->setWhere("starred is ?", "bool", $context->starred);
|
||||
// filter based on edition offset
|
||||
if($context->oldestEdition()) $q->setWhere("edition >= ?", "int", $context->oldestEdition);
|
||||
if($context->latestEdition()) $q->setWhere("edition <= ?", "int", $context->latestEdition);
|
||||
// filter based on lastmod time
|
||||
if($context->modifiedSince()) $q->setWhere("modified >= ?", "datetime", $context->modifiedSince);
|
||||
if($context->notModifiedSince()) $q->setWhere("modified <= ?", "datetime", $context->notModifiedSince);
|
||||
// push the current query onto the CTE stack and execute the query we're actually interested in
|
||||
$q->pushCTE(
|
||||
"target_articles(id, exists)", // CTE table specification
|
||||
|
|
9
tests/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php
Normal file
9
tests/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
|
||||
class TestDatabaseArticleSQLite3 extends \PHPUnit\Framework\TestCase {
|
||||
use Test\Tools, Test\Database\Setup;
|
||||
use Test\Database\DriverSQLite3;
|
||||
use Test\Database\SeriesArticle;
|
||||
}
|
197
tests/lib/Database/SeriesArticle.php
Normal file
197
tests/lib/Database/SeriesArticle.php
Normal file
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Test\Database;
|
||||
use JKingWeb\Arsse\Data;
|
||||
use JKingWeb\Arsse\Feed;
|
||||
use JKingWeb\Arsse\Test\Database;
|
||||
use JKingWeb\Arsse\User\Driver as UserDriver;
|
||||
use JKingWeb\Arsse\Feed\Exception as FeedException;
|
||||
use JKingWeb\Arsse\Misc\Context;
|
||||
use Phake;
|
||||
|
||||
trait SeriesArticle {
|
||||
protected $data = [
|
||||
'arsse_users' => [
|
||||
'columns' => [
|
||||
'id' => 'str',
|
||||
'password' => 'str',
|
||||
'name' => 'str',
|
||||
'rights' => 'int',
|
||||
],
|
||||
'rows' => [
|
||||
["jane.doe@example.com", "", "Jane Doe", UserDriver::RIGHTS_NONE],
|
||||
["john.doe@example.com", "", "John Doe", UserDriver::RIGHTS_NONE],
|
||||
],
|
||||
],
|
||||
'arsse_folders' => [
|
||||
'columns' => [
|
||||
'id' => "int",
|
||||
'owner' => "str",
|
||||
'parent' => "int",
|
||||
'name' => "str",
|
||||
],
|
||||
'rows' => [
|
||||
[1, "john.doe@example.com", null, "Technology"],
|
||||
[2, "john.doe@example.com", 1, "Software"],
|
||||
[3, "john.doe@example.com", 1, "Rocketry"],
|
||||
[4, "jane.doe@example.com", null, "Politics"],
|
||||
[5, "john.doe@example.com", null, "Politics"],
|
||||
[6, "john.doe@example.com", 2, "Politics"],
|
||||
]
|
||||
],
|
||||
'arsse_feeds' => [
|
||||
'columns' => [
|
||||
'id' => "int",
|
||||
'url' => "str",
|
||||
],
|
||||
'rows' => [
|
||||
[1,"http://example.com/1"],
|
||||
[2,"http://example.com/2"],
|
||||
[3,"http://example.com/3"],
|
||||
[4,"http://example.com/4"],
|
||||
[5,"http://example.com/5"],
|
||||
[6,"http://example.com/6"],
|
||||
[7,"http://example.com/7"],
|
||||
[8,"http://example.com/8"],
|
||||
[9,"http://example.com/9"],
|
||||
[10,"http://example.com/10"],
|
||||
[11,"http://example.com/11"],
|
||||
[12,"http://example.com/12"],
|
||||
[13,"http://example.com/13"],
|
||||
[14,"http://example.com/14"],
|
||||
[15,"http://example.com/15"],
|
||||
[16,"http://example.com/16"],
|
||||
[17,"http://example.com/17"],
|
||||
[18,"http://example.com/18"],
|
||||
[19,"http://example.com/19"],
|
||||
[20,"http://example.com/20"],
|
||||
]
|
||||
],
|
||||
'arsse_subscriptions' => [
|
||||
'columns' => [
|
||||
'id' => "int",
|
||||
'owner' => "str",
|
||||
'feed' => "int",
|
||||
'folder' => "int",
|
||||
],
|
||||
'rows' => [
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[2,"john.doe@example.com",2,null],
|
||||
[3,"john.doe@example.com",3,1],
|
||||
[4,"john.doe@example.com",4,6],
|
||||
[5,"john.doe@example.com",10,5],
|
||||
[6,"jane.doe@example.com",1,null],
|
||||
[7,"jane.doe@example.com",12,null],/*
|
||||
[8,"john.doe@example.com",1,null],
|
||||
[9,"john.doe@example.com",1,null],
|
||||
[10,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],
|
||||
[1,"john.doe@example.com",1,null],*/
|
||||
]
|
||||
],
|
||||
'arsse_articles' => [
|
||||
'columns' => [
|
||||
'id' => "int",
|
||||
'feed' => "int",
|
||||
'modified' => "datetime",
|
||||
'url_title_hash' => "str",
|
||||
'url_content_hash' => "str",
|
||||
'title_content_hash' => "str",
|
||||
],
|
||||
'rows' => [] // filled by series setup
|
||||
],
|
||||
'arsse_enclosures' => [
|
||||
'columns' => [
|
||||
'article' => "int",
|
||||
'url' => "str",
|
||||
'type' => "str",
|
||||
],
|
||||
'rows' => []
|
||||
],
|
||||
'arsse_editions' => [
|
||||
'columns' => [
|
||||
'id' => "int",
|
||||
'article' => "int",
|
||||
],
|
||||
'rows' => [ // lower IDs are filled by series setup
|
||||
[1001,20],
|
||||
]
|
||||
],
|
||||
'arsse_marks' => [
|
||||
'columns' => [
|
||||
'owner' => "str",
|
||||
'article' => "int",
|
||||
'read' => "bool",
|
||||
'starred' => "bool",
|
||||
],
|
||||
'rows' => [
|
||||
["john.doe@example.com",1,1,1],
|
||||
["john.doe@example.com",19,1,0],
|
||||
["john.doe@example.com",20,0,1],
|
||||
["jane.doe@example.com",20,1,0],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
function setUpSeries() {
|
||||
for($a = 0, $b = 1; $b <= sizeof($this->data['arsse_feeds']['rows']); $b++) {
|
||||
// add two generic articles per feed, and matching initial editions
|
||||
$this->data['arsse_articles']['rows'][] = [++$a,$b,"2000-01-01T00:00:00Z","","",""];
|
||||
$this->data['arsse_editions']['rows'][] = [$a,$a];
|
||||
$this->data['arsse_articles']['rows'][] = [++$a,$b,"2010-01-01T00:00:00Z","","",""];
|
||||
$this->data['arsse_editions']['rows'][] = [$a,$a];
|
||||
}
|
||||
$this->user = "john.doe@example.com";
|
||||
}
|
||||
|
||||
function testListArticlesByContext() {
|
||||
// get all items for user
|
||||
$exp = [1,2,3,4,5,6,7,8,19,20];
|
||||
$this->compareIds($exp, new Context);
|
||||
// get items from a folder tree
|
||||
$exp = [5,6,7,8];
|
||||
$this->compareIds($exp, (new Context)->folder(1));
|
||||
// get items from a leaf folder
|
||||
$exp = [7,8];
|
||||
$this->compareIds($exp, (new Context)->folder(6));
|
||||
// get items from a single subscription
|
||||
$exp = [19,20];
|
||||
$this->compareIds($exp, (new Context)->subscription(5));
|
||||
// get un/read items from a single subscription
|
||||
$this->compareIds([20], (new Context)->subscription(5)->unread(true));
|
||||
$this->compareIds([19], (new Context)->subscription(5)->unread(false));
|
||||
// get starred articles
|
||||
$this->compareIds([1,20], (new Context)->starred(true));
|
||||
$this->compareIds([1], (new Context)->starred(true)->unread(false));
|
||||
$this->compareIds([], (new Context)->starred(true)->unread(false)->subscription(5));
|
||||
// get items relative to edition
|
||||
$this->compareIds([19], (new Context)->subscription(5)->latestEdition(999));
|
||||
$this->compareIds([19], (new Context)->subscription(5)->latestEdition(19));
|
||||
$this->compareIds([20], (new Context)->subscription(5)->oldestEdition(999));
|
||||
$this->compareIds([20], (new Context)->subscription(5)->oldestEdition(1001));
|
||||
// get items relative to modification date
|
||||
$exp = [2,4,6,8,20];
|
||||
$this->compareIds($exp, (new Context)->modifiedSince("2005-01-01T00:00:00Z"));
|
||||
$this->compareIds($exp, (new Context)->modifiedSince("2010-01-01T00:00:00Z"));
|
||||
$exp = [1,3,5,7,19];
|
||||
$this->compareIds($exp, (new Context)->notModifiedSince("2005-01-01T00:00:00Z"));
|
||||
$this->compareIds($exp, (new Context)->notModifiedSince("2000-01-01T00:00:00Z"));
|
||||
|
||||
}
|
||||
|
||||
protected function compareIds(array $exp, Context $c) {
|
||||
$ids = array_column($ids = Data::$db->articleList($this->user, $c)->getAll(), "id");
|
||||
sort($ids);
|
||||
sort($exp);
|
||||
$this->assertEquals($exp, $ids);
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@
|
|||
<file>Db/SQLite3/Database/TestDatabaseFolderSQLite3.php</file>
|
||||
<file>Db/SQLite3/Database/TestDatabaseFeedSQLite3.php</file>
|
||||
<file>Db/SQLite3/Database/TestDatabaseSubscriptionSQLite3.php</file>
|
||||
<file>Db/SQLite3/Database/TestDatabaseArticleSQLite3.php</file>
|
||||
</testsuite>
|
||||
<testsuite name="NextCloud News API">
|
||||
<file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file>
|
||||
|
|
Loading…
Reference in a new issue