Browse Source

Move date formatting out of SQL and standardize on the DateFormatter trait

Fixes #56
microsub
J. King 7 years ago
parent
commit
17ec6cf669
  1. 38
      lib/Database.php
  2. 38
      lib/Db/AbstractStatement.php
  3. 26
      lib/Db/SQLite3/CustomFunctions.php
  4. 2
      lib/Db/SQLite3/Driver.php
  5. 8
      lib/Db/SQLite3/Statement.php
  6. 5
      lib/Db/Statement.php
  7. 6
      lib/Feed.php
  8. 44
      lib/Misc/DateFormatter.php
  9. 16
      lib/REST/AbstractHandler.php
  10. 14
      lib/REST/NextCloudNews/V1_2.php
  11. 16
      tests/Feed/TestFeed.php
  12. 4
      tests/REST/NextCloudNews/TestNCNV1_2.php
  13. 31
      tests/lib/Database/SeriesArticle.php
  14. 4
      tests/lib/Database/SeriesFeed.php
  15. 8
      tests/lib/Database/SeriesSubscription.php
  16. 54
      tests/lib/Db/BindingTests.php
  17. 4
      tests/lib/Tools.php

38
lib/Database.php

@ -6,6 +6,7 @@ use JKingWeb\Arsse\Misc\Query;
use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Misc\Context;
class Database { class Database {
use Misc\DateFormatter;
const SCHEMA_VERSION = 1; const SCHEMA_VERSION = 1;
const FORMAT_TS = "Y-m-d h:i:s"; const FORMAT_TS = "Y-m-d h:i:s";
@ -13,7 +14,6 @@ class Database {
const FORMAT_TIME = "h:i:s"; const FORMAT_TIME = "h:i:s";
public $db; public $db;
protected $dateFormatDefault = "sql";
public function __construct() { public function __construct() {
$driver = Data::$conf->dbDriver; $driver = Data::$conf->dbDriver;
@ -27,15 +27,6 @@ class Database {
protected function caller(): string { protected function caller(): string {
return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function']; return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function'];
} }
public function dateFormatDefault(string $set = null): string {
if(is_null($set)) return $this->dateFormatDefault;
$set = strtolower($set);
if(in_array($set, ["sql", "iso8601", "unix", "http"])) {
$this->dateFormatDefault = $set;
}
return $this->dateFormatDefault;
}
static public function listDrivers(): array { static public function listDrivers(): array {
$sep = \DIRECTORY_SEPARATOR; $sep = \DIRECTORY_SEPARATOR;
@ -349,17 +340,14 @@ class Database {
$q = new Query( $q = new Query(
"SELECT "SELECT
arsse_subscriptions.id, arsse_subscriptions.id,
url,favicon,source,folder,pinned,err_count,err_msg,order_type, url,favicon,source,folder,pinned,err_count,err_msg,order_type,added,
DATEFORMAT(?, added) as added,
topmost.top as top_folder, topmost.top as top_folder,
coalesce(arsse_subscriptions.title, arsse_feeds.title) as title, coalesce(arsse_subscriptions.title, arsse_feeds.title) as title,
(SELECT count(*) from arsse_articles where feed is arsse_subscriptions.feed) - (SELECT count(*) from arsse_marks join user on user is owner join arsse_articles on article = arsse_articles.id where feed is arsse_feeds.id and read is 1) as unread (SELECT count(*) from arsse_articles where feed is arsse_subscriptions.feed) - (SELECT count(*) from arsse_marks join user on user is owner join arsse_articles on article = arsse_articles.id where feed is arsse_feeds.id and read is 1) as unread
from arsse_subscriptions from arsse_subscriptions
join user on user is owner join user on user is owner
join arsse_feeds on feed = arsse_feeds.id join arsse_feeds on feed = arsse_feeds.id
left join topmost on folder=f_id", left join topmost on folder=f_id"
"str", // where terms
$this->dateFormatDefault
); );
$q->setOrder("pinned desc, title"); $q->setOrder("pinned desc, title");
// define common table expressions // define common table expressions
@ -444,13 +432,13 @@ class Database {
public function feedUpdate(int $feedID, bool $throwError = false): bool { public function feedUpdate(int $feedID, bool $throwError = false): bool {
$tr = $this->db->begin(); $tr = $this->db->begin();
// check to make sure the feed exists // check to make sure the feed exists
$f = $this->db->prepare("SELECT url, username, password, DATEFORMAT('http', modified) AS lastmodified, etag, err_count FROM arsse_feeds where id is ?", "int")->run($feedID)->getRow(); $f = $this->db->prepare("SELECT url, username, password, modified, etag, err_count FROM arsse_feeds where id is ?", "int")->run($feedID)->getRow();
if(!$f) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $feedID]); if(!$f) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $feedID]);
// the Feed object throws an exception when there are problems, but that isn't ideal // the Feed object throws an exception when there are problems, but that isn't ideal
// here. When an exception is thrown it should update the database with the // here. When an exception is thrown it should update the database with the
// error instead of failing; if other exceptions are thrown, we should simply roll back // error instead of failing; if other exceptions are thrown, we should simply roll back
try { try {
$feed = new Feed($feedID, $f['url'], (string)$f['lastmodified'], $f['etag'], $f['username'], $f['password']); $feed = new Feed($feedID, $f['url'], $this->dateTransform($f['modified'], "http", "sql"), $f['etag'], $f['username'], $f['password']);
if(!$feed->modified) { if(!$feed->modified) {
// if the feed hasn't changed, just compute the next fetch time and record it // if the feed hasn't changed, just compute the next fetch time and record it
$this->db->prepare("UPDATE arsse_feeds SET updated = CURRENT_TIMESTAMP, next_fetch = ? WHERE id is ?", 'datetime', 'int')->run($feed->nextFetch, $feedID); $this->db->prepare("UPDATE arsse_feeds SET updated = CURRENT_TIMESTAMP, next_fetch = ? WHERE id is ?", 'datetime', 'int')->run($feed->nextFetch, $feedID);
@ -556,7 +544,7 @@ class Database {
public function feedMatchLatest(int $feedID, int $count): Db\Result { public function feedMatchLatest(int $feedID, int $count): Db\Result {
return $this->db->prepare( return $this->db->prepare(
"SELECT id, DATEFORMAT('unix', edited) AS edited_date, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? ORDER BY modified desc, id desc limit ?", "SELECT id, edited, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? ORDER BY modified desc, id desc limit ?",
'int', 'int' 'int', 'int'
)->run($feedID, $count); )->run($feedID, $count);
} }
@ -569,7 +557,7 @@ class Database {
list($cHashTC, $tHashTC) = $this->generateIn($hashesTC, "str"); list($cHashTC, $tHashTC) = $this->generateIn($hashesTC, "str");
// perform the query // perform the query
return $articles = $this->db->prepare( return $articles = $this->db->prepare(
"SELECT id, DATEFORMAT('unix', edited) AS edited_date, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? and (guid in($cId) or url_title_hash in($cHashUT) or url_content_hash in($cHashUC) or title_content_hash in($cHashTC))", "SELECT id, edited, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? and (guid in($cId) or url_title_hash in($cHashUT) or url_content_hash in($cHashUC) or title_content_hash in($cHashTC))",
'int', $tId, $tHashUT, $tHashUC, $tHashTC 'int', $tId, $tHashUT, $tHashUC, $tHashTC
)->run($feedID, $ids, $hashesUT, $hashesUC, $hashesTC); )->run($feedID, $ids, $hashesUT, $hashesUC, $hashesTC);
} }
@ -613,12 +601,12 @@ class Database {
arsse_articles.id as id, arsse_articles.id as id,
arsse_articles.url as url, arsse_articles.url as url,
title,author,content,guid, title,author,content,guid,
DATEFORMAT(?, published) as published_date, published as published_date,
DATEFORMAT(?, edited) as edited_date, edited as edited_date,
DATEFORMAT(?, max( max(
modified, modified,
coalesce((select modified from arsse_marks join user on user is owner where article is arsse_articles.id),'') coalesce((select modified from arsse_marks join user on user is owner where article is arsse_articles.id),'')
)) as modified_date, ) as modified_date,
NOT (select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and read is 1) as unread, 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 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 edition, (select max(id) from arsse_editions where article is arsse_articles.id) as edition,
@ -629,9 +617,7 @@ class Database {
FROM arsse_articles FROM arsse_articles
join subscribed_feeds on arsse_articles.feed is subscribed_feeds.id join subscribed_feeds on arsse_articles.feed is subscribed_feeds.id
left join arsse_enclosures on arsse_enclosures.article is arsse_articles.id left join arsse_enclosures on arsse_enclosures.article is arsse_articles.id
", "
["str", "str", "str"],
[$this->dateFormatDefault, $this->dateFormatDefault, $this->dateFormatDefault]
); );
$q->setOrder("edition".($context->reverse ? " desc" : "")); $q->setOrder("edition".($context->reverse ? " desc" : ""));
$q->setLimit($context->limit, $context->offset); $q->setLimit($context->limit, $context->offset);

38
lib/Db/AbstractStatement.php

@ -3,12 +3,13 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\Db; namespace JKingWeb\Arsse\Db;
abstract class AbstractStatement implements Statement { abstract class AbstractStatement implements Statement {
use \JKingWeb\Arsse\Misc\DateFormatter;
protected $types = []; protected $types = [];
protected $isNullable = []; protected $isNullable = [];
protected $values = ['pre' => [], 'post' => []]; protected $values = ['pre' => [], 'post' => []];
abstract function runArray(array $values = []): Result; abstract function runArray(array $values = []): Result;
abstract static function dateFormat(int $part = self::TS_BOTH): string;
public function run(...$values): Result { public function run(...$values): Result {
return $this->runArray($values); return $this->runArray($values);
@ -44,13 +45,13 @@ abstract class AbstractStatement implements Statement {
switch($t) { switch($t) {
case "date": case "date":
if(is_null($v) && !$nullable) $v = 0; if(is_null($v) && !$nullable) $v = 0;
return $this->formatDate($v, self::TS_DATE); return $this->dateTransform($v, "date");
case "time": case "time":
if(is_null($v) && !$nullable) $v = 0; if(is_null($v) && !$nullable) $v = 0;
return $this->formatDate($v, self::TS_TIME); return $this->dateTransform($v, "time");
case "datetime": case "datetime":
if(is_null($v) && !$nullable) $v = 0; if(is_null($v) && !$nullable) $v = 0;
return $this->formatDate($v, self::TS_BOTH); return $this->dateTransform($v, "sql");
case "null": case "null":
case "integer": case "integer":
case "float": case "float":
@ -65,9 +66,12 @@ abstract class AbstractStatement implements Statement {
// handle objects // handle objects
$value = $v; $value = $v;
if($value instanceof \DateTimeInterface) { if($value instanceof \DateTimeInterface) {
$value = $value->getTimestamp(); if($t=="string") {
if($t=="string") $value = $this->formatDate($value, self::TS_BOTH); $value = $this->dateTransform($value, "sql");
settype($value, $t); } else {
$value = $value->getTimestamp();
settype($value, $t);
}
} else { } else {
$value = null; $value = null;
settype($value, $t); settype($value, $t);
@ -78,24 +82,4 @@ abstract class AbstractStatement implements Statement {
throw new Exception("paramTypeUnknown", $type); throw new Exception("paramTypeUnknown", $type);
} }
} }
protected function formatDate($date, int $part = self::TS_BOTH) {
// convert input to a Unix timestamp
if($date instanceof \DateTimeInterface) {
$time = $date->getTimestamp();
} else if(is_numeric($date)) {
$time = (int) $date;
} else if($date===null) {
return null;
} else if(is_string($date)) {
$time = strtotime($date);
if($time===false) return null;
} else if (is_bool($date)) {
return null;
} else {
$time = (int) $date;
}
// ISO 8601 with space in the middle instead of T.
return gmdate($this->dateFormat($part), $time);
}
} }

26
lib/Db/SQLite3/CustomFunctions.php

@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
namespace JKingWeb\Arsse\Db\SQLite3;
class CustomFunctions {
protected static $tz;
// Converts from SQL date format to a specified standard date format.
public static function dateFormat(string $format, $date) {
$format = strtolower($format);
if($format=="sql") return $date;
settype($date, "string");
if($date=="") return null;
if(is_null(self::$tz)) self::$tz = new \DateTimeZone("UTC");
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $date, self::$tz);
switch ($format) {
case 'unix':
return $date->getTimestamp();
case 'http':
return $date->format("D, d M Y H:i:s \G\M\T");
case 'iso8601':
default:
return $date->format(\DateTime::ATOM);
}
}
}

2
lib/Db/SQLite3/Driver.php

@ -40,8 +40,6 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
$this->db->enableExceptions(true); $this->db->enableExceptions(true);
$this->exec("PRAGMA journal_mode = wal"); $this->exec("PRAGMA journal_mode = wal");
$this->exec("PRAGMA foreign_keys = yes"); $this->exec("PRAGMA foreign_keys = yes");
// Create custom functions
$this->db->createFunction('DATEFORMAT', [CustomFunctions::class, 'dateFormat'], 2);
} catch(\Exception $e) { } catch(\Exception $e) {
list($excClass, $excMsg, $excData) = $this->exceptionBuild(); list($excClass, $excMsg, $excData) = $this->exceptionBuild();
throw new $excClass($excMsg, $excData); throw new $excClass($excMsg, $excData);

8
lib/Db/SQLite3/Statement.php

@ -37,14 +37,6 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
unset($this->st); unset($this->st);
} }
public static function dateFormat(int $part = self::TS_BOTH): string {
return ([
self::TS_TIME => 'H:i:s',
self::TS_DATE => 'Y-m-d',
self::TS_BOTH => 'Y-m-d H:i:s',
])[$part];
}
public function runArray(array $values = []): \JKingWeb\Arsse\Db\Result { public function runArray(array $values = []): \JKingWeb\Arsse\Db\Result {
$this->st->clear(); $this->st->clear();
$this->bindValues($values); $this->bindValues($values);

5
lib/Db/Statement.php

@ -3,9 +3,6 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\Db; namespace JKingWeb\Arsse\Db;
interface Statement { interface Statement {
const TS_TIME = -1;
const TS_DATE = 0;
const TS_BOTH = 1;
const TYPES = [ const TYPES = [
"null" => "null", "null" => "null",
"nil" => "null", "nil" => "null",
@ -30,8 +27,6 @@ interface Statement {
"bit" => "boolean", "bit" => "boolean",
]; ];
static function dateFormat(int $part = self::TS_BOTH): string;
function run(...$values): Result; function run(...$values): Result;
function runArray(array $values = []): Result; function runArray(array $values = []): Result;
function rebind(...$bindings): bool; function rebind(...$bindings): bool;

6
lib/Feed.php

@ -7,6 +7,8 @@ use PicoFeed\Reader\Favicon;
use PicoFeed\Config\Config; use PicoFeed\Config\Config;
class Feed { class Feed {
use Misc\DateFormatter;
public $data = null; public $data = null;
public $favicon; public $favicon;
public $parser; public $parser;
@ -24,7 +26,7 @@ class Feed {
// format the HTTP Last-Modified date returned // format the HTTP Last-Modified date returned
$lastMod = $this->resource->getLastModified(); $lastMod = $this->resource->getLastModified();
if(strlen($lastMod)) { if(strlen($lastMod)) {
$this->lastModified = \DateTime::createFromFormat("!D, d M Y H:i:s e", $lastMod); $this->lastModified = $this->dateNormalize($lastMod, "http");
} }
$this->modified = $this->resource->isModified(); $this->modified = $this->resource->isModified();
//parse the feed, if it has been modified //parse the feed, if it has been modified
@ -229,7 +231,7 @@ class Feed {
($i->urlContentHash && $i->urlContentHash === $a['url_content_hash']) || ($i->urlContentHash && $i->urlContentHash === $a['url_content_hash']) ||
($i->titleContentHash && $i->titleContentHash === $a['title_content_hash']) ($i->titleContentHash && $i->titleContentHash === $a['title_content_hash'])
) { ) {
if($i->updatedDate && $i->updatedDate->getTimestamp() !== $a['edited_date']) { if($i->updatedDate && $this->dateTransform($i->updatedDate, "sql") !== $a['edited']) {
// if the item has an edit timestamp and it doesn't match that of the article in the database, the the article has been edited // if the item has an edit timestamp and it doesn't match that of the article in the database, the the article has been edited
// we store the item index and database record ID as a key/value pair // we store the item index and database record ID as a key/value pair
$found = true; $found = true;

44
lib/Misc/DateFormatter.php

@ -4,36 +4,46 @@ namespace JKingWeb\Arsse\Misc;
trait DateFormatter { trait DateFormatter {
protected function dateTransform($date, string $format = "iso8601", bool $local = false) { protected function dateTransform($date, string $outFormat = null, string $inFormat = null, bool $inLocal = false) {
$date = $this->dateNormalize($date); $date = $this->dateNormalize($date, $inFormat, $inLocal);
$format = strtolower($format); if(is_null($date) || is_null($outFormat)) return $date;
if($format=="unix") return $date; $outFormat = strtolower($outFormat);
switch ($format) { if($outFormat=="unix") return $date->getTimestamp();
switch ($outFormat) {
case 'http': $f = "D, d M Y H:i:s \G\M\T"; break; case 'http': $f = "D, d M Y H:i:s \G\M\T"; break;
case 'iso8601': $f = \DateTime::ATOM; break; case 'iso8601': $f = "Y-m-dTH:i:s"; break;
case 'sql': $f = "Y-m-d H:i:s"; break; case 'sql': $f = "Y-m-d H:i:s"; break;
case 'date': $f = "Y-m-d"; break; case 'date': $f = "Y-m-d"; break;
case 'time': $f = "H:i:s"; break; case 'time': $f = "H:i:s"; break;
default: $f = \DateTime::ATOM; break; default: $f = $outFormat; break;
}
if($local) {
return date($f, $date);
} else {
return gmdate($f, $date);
} }
return $date->format($f);
} }
protected function dateNormalize($date) { protected function dateNormalize($date, string $inFormat = null, bool $inLocal = false) {
// convert input to a Unix timestamp // convert input to a Unix timestamp
if($date instanceof \DateTimeInterface) { if($date instanceof \DateTimeInterface) {
$time = $date->getTimestamp(); return $date;
} else if(is_numeric($date)) { } else if(is_numeric($date)) {
$time = (int) $date; $time = (int) $date;
} else if($date===null) { } else if($date===null) {
return null; return null;
} else if(is_string($date)) { } else if(is_string($date)) {
try { try {
$time = (new \DateTime($date, new \DateTimeZone("UTC")))->getTimestamp(); $tz = (!$inLocal) ? new \DateTimeZone("UTC") : null;
if(!is_null($inFormat)) {
switch($inFormat) {
case 'http': $f = "D, d M Y H:i:s \G\M\T"; break;
case 'iso8601': $f = "Y-m-dTH:i:sP"; break;
case 'sql': $f = "Y-m-d H:i:s"; break;
case 'date': $f = "Y-m-d"; break;
case 'time': $f = "H:i:s"; break;
default: $f = $inFormat; break;
}
return \DateTime::createFromFormat("!".$f, $date, $tz);
} else {
return new \DateTime($date, $tz);
}
} catch(\Throwable $e) { } catch(\Throwable $e) {
return null; return null;
} }
@ -42,6 +52,8 @@ trait DateFormatter {
} else { } else {
$time = (int) $date; $time = (int) $date;
} }
return $time; $d = new \DateTime();
$d->setTimestamp($time);
return $d;
} }
} }

16
lib/REST/AbstractHandler.php

@ -3,6 +3,8 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\REST; namespace JKingWeb\Arsse\REST;
abstract class AbstractHandler implements Handler { abstract class AbstractHandler implements Handler {
use \JKingWeb\Arsse\Misc\DateFormatter;
abstract function __construct(); abstract function __construct();
abstract function dispatch(Request $req): Response; abstract function dispatch(Request $req): Response;
@ -16,9 +18,15 @@ abstract class AbstractHandler implements Handler {
return $out; return $out;
} }
protected function fieldMapTypes(array $data, array $map): array { protected function fieldMapTypes(array $data, array $map, string $dateFormat = "sql"): array {
foreach($map as $key => $type) { foreach($map as $key => $type) {
if(array_key_exists($key, $data)) settype($data[$key], $type); if(array_key_exists($key, $data)) {
if($type=="datetime" && $dateFormat != "sql") {
$data[$key] = $this->dateTransform($data[$key], $dateFormat, "sql");
} else {
settype($data[$key], $type);
}
}
} }
return $data; return $data;
} }
@ -33,7 +41,7 @@ abstract class AbstractHandler implements Handler {
return ($ch1 === $ch2); return ($ch1 === $ch2);
} }
protected function NormalizeInput(array $data, array $types, string $dateFormat = "Y-m-d\TH:i:sP"): array { protected function NormalizeInput(array $data, array $types, string $dateFormat = null): array {
$out = []; $out = [];
foreach($data as $key => $value) { foreach($data as $key => $value) {
if(!isset($types[$key])) { if(!isset($types[$key])) {
@ -67,7 +75,7 @@ abstract class AbstractHandler implements Handler {
if(is_numeric($value)) $out[$key] = (float) $value; if(is_numeric($value)) $out[$key] = (float) $value;
break; break;
case "datetime": case "datetime":
$t = \DateTime::createFromFormat($dateFormat, (string) $value); $t = $this->dateNormalize($value, $dateFormat);
if($t) $out[$key] = $t; if($t) $out[$key] = $t;
break; break;
default: default:

14
lib/REST/NextCloudNews/V1_2.php

@ -14,6 +14,8 @@ use JKingWeb\Arsse\REST\Exception405;
class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
const REALM = "NextCloud News API v1-2"; const REALM = "NextCloud News API v1-2";
protected $dateFormat = "unix";
protected $validInput = [ protected $validInput = [
'name' => "string", 'name' => "string",
'url' => "string", 'url' => "string",
@ -69,7 +71,6 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
if(!method_exists($this, $func)) return new Response(501); if(!method_exists($this, $func)) return new Response(501);
// dispatch // dispatch
try { try {
Data::$db->dateFormatDefault("unix");
return $this->$func($req->paths, $data); return $this->$func($req->paths, $data);
} catch(Exception $e) { } catch(Exception $e) {
// if there was a REST exception return 400 // if there was a REST exception return 400
@ -169,7 +170,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
$feed = $this->fieldMapTypes($feed, [ $feed = $this->fieldMapTypes($feed, [
'folderId' => "int", 'folderId' => "int",
'pinned' => "bool", 'pinned' => "bool",
]); 'added' => "datetime",
], $this->dateFormat);
return $feed; return $feed;
} }
@ -194,9 +196,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
]); ]);
// cast values // cast values
$article = $this->fieldMapTypes($article, [ $article = $this->fieldMapTypes($article, [
'unread' => "bool", 'unread' => "bool",
'starred' => "bool", 'starred' => "bool",
]); 'pubDate' => "datetime",
'lastModified' => "datetime",
], $this->dateFormat);
return $article; return $article;
} }

16
tests/Feed/TestFeed.php

@ -12,7 +12,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
protected $latest = [ protected $latest = [
[ [
'id' => 1, 'id' => 1,
'edited_date' => 946684800, 'edited' => '2000-01-01 00:00:00',
'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda', 'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda',
'url_title_hash' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6', 'url_title_hash' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6',
'url_content_hash' => 'fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4', 'url_content_hash' => 'fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4',
@ -20,7 +20,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
], ],
[ [
'id' => 2, 'id' => 2,
'edited_date' => 946771200, 'edited' => '2000-01-02 00:00:00',
'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7', 'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7',
'url_title_hash' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153', 'url_title_hash' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153',
'url_content_hash' => '13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9', 'url_content_hash' => '13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9',
@ -28,7 +28,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
], ],
[ [
'id' => 3, 'id' => 3,
'edited_date' => 946857600, 'edited' => '2000-01-03 00:00:00',
'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92', 'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92',
'url_title_hash' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b', 'url_title_hash' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b',
'url_content_hash' => 'b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406', 'url_content_hash' => 'b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406',
@ -36,7 +36,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
], ],
[ [
'id' => 4, 'id' => 4,
'edited_date' => 946944000, 'edited' => '2000-01-04 00:00:00',
'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180', 'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180',
'url_title_hash' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8', 'url_title_hash' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8',
'url_content_hash' => 'f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3', 'url_content_hash' => 'f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3',
@ -44,7 +44,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
], ],
[ [
'id' => 5, 'id' => 5,
'edited_date' => 947030400, 'edited' => '2000-01-05 00:00:00',
'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41', 'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41',
'url_title_hash' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022', 'url_title_hash' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022',
'url_content_hash' => '834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900', 'url_content_hash' => '834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900',
@ -54,7 +54,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
protected $others = [ protected $others = [
[ [
'id' => 6, 'id' => 6,
'edited_date' => 947116800, 'edited' => '2000-01-06 00:00:00',
'guid' => 'b3461ab8e8759eeb1d65a818c65051ec00c1dfbbb32a3c8f6999434e3e3b76ab', 'guid' => 'b3461ab8e8759eeb1d65a818c65051ec00c1dfbbb32a3c8f6999434e3e3b76ab',
'url_title_hash' => '91d051a8e6749d014506848acd45e959af50bf876427c4f0e3a1ec0f04777b51', 'url_title_hash' => '91d051a8e6749d014506848acd45e959af50bf876427c4f0e3a1ec0f04777b51',
'url_content_hash' => '211d78b1a040d40d17e747a363cc283f58767b2e502630d8de9b8f1d5e941d18', 'url_content_hash' => '211d78b1a040d40d17e747a363cc283f58767b2e502630d8de9b8f1d5e941d18',
@ -62,7 +62,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
], ],
[ [
'id' => 7, 'id' => 7,
'edited_date' => 947203200, 'edited' => '2000-01-07 00:00:00',
'guid' => 'f4fae999d6531747523f4ff0c74f3f0c7c588b67e4f32d8f7dba5f6f36e8a45d', 'guid' => 'f4fae999d6531747523f4ff0c74f3f0c7c588b67e4f32d8f7dba5f6f36e8a45d',
'url_title_hash' => 'b92f805f0d0643dad1d6c0bb5cbaec24729f5f71b37b831cf7ad31f6c9403ac8', 'url_title_hash' => 'b92f805f0d0643dad1d6c0bb5cbaec24729f5f71b37b831cf7ad31f6c9403ac8',
'url_content_hash' => '4fc8789b787246e9be08ca1bac0d4a1ac4db1984f0db07f7142417598cf7211f', 'url_content_hash' => '4fc8789b787246e9be08ca1bac0d4a1ac4db1984f0db07f7142417598cf7211f',
@ -70,7 +70,7 @@ class TestFeed extends \PHPUnit\Framework\TestCase {
], ],
[ [
'id' => 8, 'id' => 8,
'edited_date' => 947289600, 'edited' => '2000-01-08 00:00:00',
'guid' => 'b9d2d58e3172096b1d23b42a59961fabc89962836c3cd5de54f3d3a98ff08e6c', 'guid' => 'b9d2d58e3172096b1d23b42a59961fabc89962836c3cd5de54f3d3a98ff08e6c',
'url_title_hash' => '53a6cbcfeb66b46d09cbb7b25035df0562da35786933319c83b04be29acfb6f4', 'url_title_hash' => '53a6cbcfeb66b46d09cbb7b25035df0562da35786933319c83b04be29acfb6f4',
'url_content_hash' => 'c6f3722b4445b49d19d39c3bf5b11a7cf23dd69873e2a0a458aab662f1cd9438', 'url_content_hash' => 'c6f3722b4445b49d19d39c3bf5b11a7cf23dd69873e2a0a458aab662f1cd9438',

4
tests/REST/NextCloudNews/TestNCNV1_2.php

@ -256,8 +256,6 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds"))); $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds")));
$exp = new Response(200, $exp2); $exp = new Response(200, $exp2);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds"))); $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds")));
// make sure the correct date format is actually requested
Phake::verify(Data::$db, Phake::atLeast(1))->dateFormatDefault("unix");
} }
function testAddASubscription() { function testAddASubscription() {
@ -295,8 +293,6 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
// try to add a bad feed // try to add a bad feed
$exp = new Response(422); $exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[2]), 'application/json'))); $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[2]), 'application/json')));
// make sure the correct date format is actually requested
Phake::verify(Data::$db, Phake::atLeast(1))->dateFormatDefault("unix");
} }
function testRemoveASubscription() { function testRemoveASubscription() {

31
tests/lib/Database/SeriesArticle.php

@ -179,9 +179,9 @@ trait SeriesArticle {
'author' => '', 'author' => '',
'content' => '<p>Article content 1</p>', 'content' => '<p>Article content 1</p>',
'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda', 'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda',
'published_date' => 946684800, 'published_date' => '2000-01-01 00:00:00',
'edited_date' => 946684801, 'edited_date' => '2000-01-01 00:00:01',
'modified_date' => 946688400, 'modified_date' => '2000-01-01 01:00:00',
'unread' => 1, 'unread' => 1,
'starred' => 0, 'starred' => 0,
'edition' => 101, 'edition' => 101,
@ -197,9 +197,9 @@ trait SeriesArticle {
'author' => '', 'author' => '',
'content' => '<p>Article content 2</p>', 'content' => '<p>Article content 2</p>',
'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7', 'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7',
'published_date' => 946771200, 'published_date' => '2000-01-02 00:00:00',
'edited_date' => 946771202, 'edited_date' => '2000-01-02 00:00:02',
'modified_date' => 946778400, 'modified_date' => '2000-01-02 02:00:00',
'unread' => 0, 'unread' => 0,
'starred' => 0, 'starred' => 0,
'edition' => 202, 'edition' => 202,
@ -215,9 +215,9 @@ trait SeriesArticle {
'author' => '', 'author' => '',
'content' => '<p>Article content 3</p>', 'content' => '<p>Article content 3</p>',
'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92', 'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92',
'published_date' => 946857600, 'published_date' => '2000-01-03 00:00:00',
'edited_date' => 946857603, 'edited_date' => '2000-01-03 00:00:03',
'modified_date' => 946868400, 'modified_date' => '2000-01-03 03:00:00',
'unread' => 1, 'unread' => 1,
'starred' => 1, 'starred' => 1,
'edition' => 203, 'edition' => 203,
@ -233,9 +233,9 @@ trait SeriesArticle {
'author' => '', 'author' => '',
'content' => '<p>Article content 4</p>', 'content' => '<p>Article content 4</p>',
'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180', 'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180',
'published_date' => 946944000, 'published_date' => '2000-01-04 00:00:00',
'edited_date' => 946944004, 'edited_date' => '2000-01-04 00:00:04',
'modified_date' => 946958400, 'modified_date' => '2000-01-04 04:00:00',
'unread' => 0, 'unread' => 0,
'starred' => 1, 'starred' => 1,
'edition' => 204, 'edition' => 204,
@ -251,9 +251,9 @@ trait SeriesArticle {
'author' => '', 'author' => '',
'content' => '<p>Article content 5</p>', 'content' => '<p>Article content 5</p>',
'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41', 'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41',
'published_date' => 947030400, 'published_date' => '2000-01-05 00:00:00',
'edited_date' => 947030405, 'edited_date' => '2000-01-05 00:00:05',
'modified_date' => 947048400, 'modified_date' => '2000-01-05 05:00:00',
'unread' => 1, 'unread' => 1,
'starred' => 0, 'starred' => 0,
'edition' => 305, 'edition' => 305,
@ -340,7 +340,6 @@ trait SeriesArticle {
function testListArticlesCheckingProperties() { function testListArticlesCheckingProperties() {
$this->user = "john.doe@example.org"; $this->user = "john.doe@example.org";
Data::$db->dateFormatDefault("unix");
$this->assertResult($this->matches, Data::$db->articleList($this->user)); $this->assertResult($this->matches, Data::$db->articleList($this->user));
} }

4
tests/lib/Database/SeriesFeed.php

@ -12,7 +12,7 @@ trait SeriesFeed {
protected $matches = [ protected $matches = [
[ [
'id' => 4, 'id' => 4,
'edited_date' => 946944000, // 2000-01-04T00:00:00Z 'edited' => '2000-01-04 00:00:00',
'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180', 'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180',
'url_title_hash' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8', 'url_title_hash' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8',
'url_content_hash' => 'f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3', 'url_content_hash' => 'f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3',
@ -20,7 +20,7 @@ trait SeriesFeed {
], ],
[ [
'id' => 5, 'id' => 5,
'edited_date' => 947030400, // 2000-01-05T00:00:00Z 'edited' => '2000-01-05 00:00:00',
'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41', 'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41',
'url_title_hash' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022', 'url_title_hash' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022',
'url_content_hash' => '834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900', 'url_content_hash' => '834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900',

8
tests/lib/Database/SeriesSubscription.php

@ -253,14 +253,6 @@ trait SeriesSubscription {
$this->assertResult($exp, Data::$db->subscriptionList($this->user, 2)); $this->assertResult($exp, Data::$db->subscriptionList($this->user, 2));
} }
function testListSubscriptionsWithDifferentDateFormats() {
Data::$db->dateFormatDefault("iso8601");
$d1 = Data::$db->subscriptionList($this->user, 2)->getRow()['added'];
Data::$db->dateFormatDefault("http");
$d2 = Data::$db->subscriptionList($this->user, 2)->getRow()['added'];
$this->assertNotEquals($d1, $d2);
}
function testListSubscriptionsInAMissingFolder() { function testListSubscriptionsInAMissingFolder() {
$this->assertException("idMissing", "Db", "ExceptionInput"); $this->assertException("idMissing", "Db", "ExceptionInput");
Data::$db->subscriptionList($this->user, 4); Data::$db->subscriptionList($this->user, 4);

54
tests/lib/Db/BindingTests.php

@ -3,7 +3,7 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\Test\Db; namespace JKingWeb\Arsse\Test\Db;
use JKingWeb\Arsse\Db\Statement; use JKingWeb\Arsse\Db\Statement;
trait BindingTests { trait BindingTests {
function testBindNull() { function testBindNull() {
$input = null; $input = null;
$exp = [ $exp = [
@ -24,9 +24,9 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => 0, "integer" => 0,
"float" => 0.0, "float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0), "date" => gmdate("Y-m-d", 0),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0), "time" => gmdate("H:i:s", 0),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0), "datetime" => gmdate("Y-m-d H:i:s", 0),
"binary" => "", "binary" => "",
"string" => "", "string" => "",
"boolean" => 0, "boolean" => 0,
@ -74,9 +74,9 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => 2112, "integer" => 2112,
"float" => 2112.0, "float" => 2112.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112), "date" => gmdate("Y-m-d", 2112),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112), "time" => gmdate("H:i:s", 2112),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112), "datetime" => gmdate("Y-m-d H:i:s", 2112),
"binary" => "2112", "binary" => "2112",
"string" => "2112", "string" => "2112",
"boolean" => 1, "boolean" => 1,
@ -91,9 +91,9 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => 0, "integer" => 0,
"float" => 0.0, "float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0), "date" => gmdate("Y-m-d", 0),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0), "time" => gmdate("H:i:s", 0),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0), "datetime" => gmdate("Y-m-d H:i:s", 0),
"binary" => "0", "binary" => "0",
"string" => "0", "string" => "0",
"boolean" => 0, "boolean" => 0,
@ -108,9 +108,9 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => 2112, "integer" => 2112,
"float" => 2112.0, "float" => 2112.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112), "date" => gmdate("Y-m-d", 2112),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112), "time" => gmdate("H:i:s", 2112),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112), "datetime" => gmdate("Y-m-d H:i:s", 2112),
"binary" => "2112", "binary" => "2112",
"string" => "2112", "string" => "2112",
"boolean" => 1, "boolean" => 1,
@ -125,9 +125,9 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => 0, "integer" => 0,
"float" => 0.0, "float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0), "date" => gmdate("Y-m-d", 0),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0), "time" => gmdate("H:i:s", 0),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0), "datetime" => gmdate("Y-m-d H:i:s", 0),
"binary" => "0", "binary" => "0",
"string" => "0", "string" => "0",
"boolean" => 0, "boolean" => 0,
@ -195,9 +195,9 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => 2017, "integer" => 2017,
"float" => 2017.0, "float" => 2017.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time), "date" => gmdate("Y-m-d", $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time), "time" => gmdate("H:i:s", $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time), "datetime" => gmdate("Y-m-d H:i:s", $time),
"binary" => $input, "binary" => $input,
"string" => $input, "string" => $input,
"boolean" => 1, "boolean" => 1,
@ -213,9 +213,9 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => 0, "integer" => 0,
"float" => 0.0, "float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time), "date" => gmdate("Y-m-d", $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time), "time" => gmdate("H:i:s", $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time), "datetime" => gmdate("Y-m-d H:i:s", $time),
"binary" => $input, "binary" => $input,
"string" => $input, "string" => $input,
"boolean" => 1, "boolean" => 1,
@ -231,11 +231,11 @@ trait BindingTests {
"null" => null, "null" => null,
"integer" => $time, "integer" => $time,
"float" => (float) $time, "float" => (float) $time,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time), "date" => gmdate("Y-m-d", $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time), "time" => gmdate("H:i:s", $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time), "datetime" => gmdate("Y-m-d H:i:s", $time),
"binary" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time), "binary" => gmdate("Y-m-d H:i:s", $time),
"string" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time), "string" => gmdate("Y-m-d H:i:s", $time),
"boolean" => 1, "boolean" => 1,
]; ];
$this->checkBinding($input, $exp); $this->checkBinding($input, $exp);

4
tests/lib/Tools.php

@ -20,8 +20,8 @@ trait Tools {
} }
function assertTime($exp, $test) { function assertTime($exp, $test) {
$exp = $this->dateTransform($exp); $exp = $this->dateTransform($exp, "unix");
$test = $this->dateTransform($test); $test = $this->dateTransform($test, "unix");
$this->assertSame($exp, $test); $this->assertSame($exp, $test);
} }

Loading…
Cancel
Save