mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Partial rewrite of database table comparison
Contents still need to be sorted for tests to pass.
This commit is contained in:
parent
9ac615e4a4
commit
51ce4ae92b
2 changed files with 195 additions and 181 deletions
|
@ -10,152 +10,6 @@ use JKingWeb\Arsse\Database;
|
||||||
|
|
||||||
/** @covers \JKingWeb\Arsse\Database */
|
/** @covers \JKingWeb\Arsse\Database */
|
||||||
class TestDatabase extends \JKingWeb\Arsse\Test\AbstractTest {
|
class TestDatabase extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
protected const COL_DEFS = [
|
|
||||||
'arsse_meta' => [
|
|
||||||
'key' => "strict str",
|
|
||||||
'value' => "str",
|
|
||||||
],
|
|
||||||
'arsse_users' => [
|
|
||||||
'id' => "strict str",
|
|
||||||
'password' => "str",
|
|
||||||
'num' => "strict int",
|
|
||||||
'admin' => "strict bool",
|
|
||||||
],
|
|
||||||
'arsse_user_meta' => [
|
|
||||||
'owner' => "strict str",
|
|
||||||
'key' => "strict str",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
'value' => "str",
|
|
||||||
],
|
|
||||||
'arsse_sessions' => [
|
|
||||||
'id' => "strict str",
|
|
||||||
'created' => "strict datetime",
|
|
||||||
'expires' => "strict datetime",
|
|
||||||
'user' => "strict str",
|
|
||||||
],
|
|
||||||
'arsse_tokens' => [
|
|
||||||
'id' => "strict str",
|
|
||||||
'class' => "strict str",
|
|
||||||
'user' => "strict str",
|
|
||||||
'created' => "strict datetime",
|
|
||||||
'expires' => "datetime",
|
|
||||||
'data' => "str",
|
|
||||||
],
|
|
||||||
'arsse_feeds' => [
|
|
||||||
'id' => "int",
|
|
||||||
'url' => "strict str",
|
|
||||||
'title' => "str",
|
|
||||||
'source' => "str",
|
|
||||||
'updated' => "datetime",
|
|
||||||
'modified' => "datetime",
|
|
||||||
'next_fetch' => "datetime",
|
|
||||||
'orphaned' => "datetime",
|
|
||||||
'etag' => "strict str",
|
|
||||||
'err_count' => "strict int",
|
|
||||||
'err_msg' => "str",
|
|
||||||
'username' => "strict str",
|
|
||||||
'password' => "strict str",
|
|
||||||
'size' => "strict int",
|
|
||||||
'icon' => "int",
|
|
||||||
],
|
|
||||||
'arsse_icons' => [
|
|
||||||
'id' => "int",
|
|
||||||
'url' => "strict str",
|
|
||||||
'modified' => "datetime",
|
|
||||||
'etag' => "strict str",
|
|
||||||
'next_fetch' => "datetime",
|
|
||||||
'orphaned' => "datetime",
|
|
||||||
'type' => "str",
|
|
||||||
'data' => "blob",
|
|
||||||
],
|
|
||||||
'arsse_articles' => [
|
|
||||||
'id' => "int",
|
|
||||||
'feed' => "strict int",
|
|
||||||
'url' => "str",
|
|
||||||
'title' => "str",
|
|
||||||
'author' => "str",
|
|
||||||
'published' => "datetime",
|
|
||||||
'edited' => "datetime",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
'guid' => "str",
|
|
||||||
'url_title_hash' => "strict str",
|
|
||||||
'url_content_hash' => "strict str",
|
|
||||||
'title_content_hash' => "strict str",
|
|
||||||
'content_scraped' => "str",
|
|
||||||
'content' => "str",
|
|
||||||
],
|
|
||||||
'arsse_editions' => [
|
|
||||||
'id' => "int",
|
|
||||||
'article' => "strict int",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
],
|
|
||||||
'arsse_enclosures' => [
|
|
||||||
'article' => "strict int",
|
|
||||||
'url' => "str",
|
|
||||||
'type' => "str",
|
|
||||||
],
|
|
||||||
'arsse_categories' => [
|
|
||||||
'article' => "strict int",
|
|
||||||
'name' => "str",
|
|
||||||
],
|
|
||||||
'arsse_marks' => [
|
|
||||||
'article' => "strict int",
|
|
||||||
'subscription' => "strict int",
|
|
||||||
'read' => "strict bool",
|
|
||||||
'starred' => "strict bool",
|
|
||||||
'modified' => "datetime",
|
|
||||||
'note' => "strict str",
|
|
||||||
'touched' => "strict bool",
|
|
||||||
'hidden' => "strict bool",
|
|
||||||
],
|
|
||||||
'arsse_subscriptions' => [
|
|
||||||
'id' => "int",
|
|
||||||
'owner' => "strict str",
|
|
||||||
'feed' => "strict int",
|
|
||||||
'added' => "strict datetime",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
'title' => "str",
|
|
||||||
'order_type' => "strict int",
|
|
||||||
'pinned' => "strict bool",
|
|
||||||
'folder' => "int",
|
|
||||||
'keep_rule' => "str",
|
|
||||||
'block_rule' => "str",
|
|
||||||
'scrape' => "strict bool",
|
|
||||||
],
|
|
||||||
'arsse_folders' => [
|
|
||||||
'id' => "int",
|
|
||||||
'owner' => "strict str",
|
|
||||||
'parent' => "int",
|
|
||||||
'name' => "strict str",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
],
|
|
||||||
'arsse_tags' => [
|
|
||||||
'id' => "int",
|
|
||||||
'owner' => "strict str",
|
|
||||||
'name' => "strict str",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
],
|
|
||||||
'arsse_tag_members' => [
|
|
||||||
'tag' => "strict int",
|
|
||||||
'subscription' => "strict int",
|
|
||||||
'assigned' => "strict bool",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
],
|
|
||||||
'arsse_labels' => [
|
|
||||||
'id' => "int",
|
|
||||||
'owner' => "strict str",
|
|
||||||
'name' => "strict str",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
],
|
|
||||||
'arsse_label_members' => [
|
|
||||||
'label' => "strict int",
|
|
||||||
'article' => "strict int",
|
|
||||||
'subscription' => "strict int",
|
|
||||||
'assigned' => "strict bool",
|
|
||||||
'modified' => "strict datetime",
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $db = null;
|
protected $db = null;
|
||||||
|
|
||||||
public function setUp(): void {
|
public function setUp(): void {
|
||||||
|
|
|
@ -31,6 +31,152 @@ use Laminas\Diactoros\Response\XmlResponse;
|
||||||
abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
|
abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
|
||||||
use \DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
|
use \DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
|
||||||
|
|
||||||
|
protected const COL_DEFS = [
|
||||||
|
'arsse_meta' => [
|
||||||
|
'key' => "str",
|
||||||
|
'value' => "str",
|
||||||
|
],
|
||||||
|
'arsse_users' => [
|
||||||
|
'id' => "str",
|
||||||
|
'password' => "str",
|
||||||
|
'num' => "int",
|
||||||
|
'admin' => "bool",
|
||||||
|
],
|
||||||
|
'arsse_user_meta' => [
|
||||||
|
'owner' => "str",
|
||||||
|
'key' => "str",
|
||||||
|
'modified' => "datetime",
|
||||||
|
'value' => "str",
|
||||||
|
],
|
||||||
|
'arsse_sessions' => [
|
||||||
|
'id' => "str",
|
||||||
|
'created' => "datetime",
|
||||||
|
'expires' => "datetime",
|
||||||
|
'user' => "str",
|
||||||
|
],
|
||||||
|
'arsse_tokens' => [
|
||||||
|
'id' => "str",
|
||||||
|
'class' => "str",
|
||||||
|
'user' => "str",
|
||||||
|
'created' => "datetime",
|
||||||
|
'expires' => "datetime",
|
||||||
|
'data' => "str",
|
||||||
|
],
|
||||||
|
'arsse_feeds' => [
|
||||||
|
'id' => "int",
|
||||||
|
'url' => "str",
|
||||||
|
'title' => "str",
|
||||||
|
'source' => "str",
|
||||||
|
'updated' => "datetime",
|
||||||
|
'modified' => "datetime",
|
||||||
|
'next_fetch' => "datetime",
|
||||||
|
'orphaned' => "datetime",
|
||||||
|
'etag' => "str",
|
||||||
|
'err_count' => "int",
|
||||||
|
'err_msg' => "str",
|
||||||
|
'username' => "str",
|
||||||
|
'password' => "str",
|
||||||
|
'size' => "int",
|
||||||
|
'icon' => "int",
|
||||||
|
],
|
||||||
|
'arsse_icons' => [
|
||||||
|
'id' => "int",
|
||||||
|
'url' => "str",
|
||||||
|
'modified' => "datetime",
|
||||||
|
'etag' => "str",
|
||||||
|
'next_fetch' => "datetime",
|
||||||
|
'orphaned' => "datetime",
|
||||||
|
'type' => "str",
|
||||||
|
'data' => "blob",
|
||||||
|
],
|
||||||
|
'arsse_articles' => [
|
||||||
|
'id' => "int",
|
||||||
|
'feed' => "int",
|
||||||
|
'url' => "str",
|
||||||
|
'title' => "str",
|
||||||
|
'author' => "str",
|
||||||
|
'published' => "datetime",
|
||||||
|
'edited' => "datetime",
|
||||||
|
'modified' => "datetime",
|
||||||
|
'guid' => "str",
|
||||||
|
'url_title_hash' => "str",
|
||||||
|
'url_content_hash' => "str",
|
||||||
|
'title_content_hash' => "str",
|
||||||
|
'content_scraped' => "str",
|
||||||
|
'content' => "str",
|
||||||
|
],
|
||||||
|
'arsse_editions' => [
|
||||||
|
'id' => "int",
|
||||||
|
'article' => "int",
|
||||||
|
'modified' => "datetime",
|
||||||
|
],
|
||||||
|
'arsse_enclosures' => [
|
||||||
|
'article' => "int",
|
||||||
|
'url' => "str",
|
||||||
|
'type' => "str",
|
||||||
|
],
|
||||||
|
'arsse_categories' => [
|
||||||
|
'article' => "int",
|
||||||
|
'name' => "str",
|
||||||
|
],
|
||||||
|
'arsse_marks' => [
|
||||||
|
'article' => "int",
|
||||||
|
'subscription' => "int",
|
||||||
|
'read' => "bool",
|
||||||
|
'starred' => "bool",
|
||||||
|
'modified' => "datetime",
|
||||||
|
'note' => "str",
|
||||||
|
'touched' => "bool",
|
||||||
|
'hidden' => "bool",
|
||||||
|
],
|
||||||
|
'arsse_subscriptions' => [
|
||||||
|
'id' => "int",
|
||||||
|
'owner' => "str",
|
||||||
|
'feed' => "int",
|
||||||
|
'added' => "datetime",
|
||||||
|
'modified' => "datetime",
|
||||||
|
'title' => "str",
|
||||||
|
'order_type' => "int",
|
||||||
|
'pinned' => "bool",
|
||||||
|
'folder' => "int",
|
||||||
|
'keep_rule' => "str",
|
||||||
|
'block_rule' => "str",
|
||||||
|
'scrape' => "bool",
|
||||||
|
],
|
||||||
|
'arsse_folders' => [
|
||||||
|
'id' => "int",
|
||||||
|
'owner' => "str",
|
||||||
|
'parent' => "int",
|
||||||
|
'name' => "str",
|
||||||
|
'modified' => "datetime",
|
||||||
|
],
|
||||||
|
'arsse_tags' => [
|
||||||
|
'id' => "int",
|
||||||
|
'owner' => "str",
|
||||||
|
'name' => "str",
|
||||||
|
'modified' => "datetime",
|
||||||
|
],
|
||||||
|
'arsse_tag_members' => [
|
||||||
|
'tag' => "int",
|
||||||
|
'subscription' => "int",
|
||||||
|
'assigned' => "bool",
|
||||||
|
'modified' => "datetime",
|
||||||
|
],
|
||||||
|
'arsse_labels' => [
|
||||||
|
'id' => "int",
|
||||||
|
'owner' => "str",
|
||||||
|
'name' => "str",
|
||||||
|
'modified' => "datetime",
|
||||||
|
],
|
||||||
|
'arsse_label_members' => [
|
||||||
|
'label' => "int",
|
||||||
|
'article' => "int",
|
||||||
|
'subscription' => "int",
|
||||||
|
'assigned' => "bool",
|
||||||
|
'modified' => "datetime",
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
protected $objMock;
|
protected $objMock;
|
||||||
protected $confMock;
|
protected $confMock;
|
||||||
protected $langMock;
|
protected $langMock;
|
||||||
|
@ -260,47 +406,61 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function compareExpectations(Driver $drv, array $expected): bool {
|
public function compareExpectations(Driver $drv, array $expected): void {
|
||||||
foreach ($expected as $table => $info) {
|
foreach ($expected as $table => $info) {
|
||||||
$cols = array_map(function($v) {
|
// serialize the rows of the expected output
|
||||||
return '"'.str_replace('"', '""', $v).'"';
|
$types = array_values($info['columns']);
|
||||||
}, array_keys($info['columns']));
|
$exp = [];
|
||||||
$cols = implode(",", $cols);
|
$dates = [];
|
||||||
$types = $info['columns'];
|
foreach ($info['rows'] as $r) {
|
||||||
$data = $drv->prepare("SELECT $cols from $table")->run()->getAll();
|
$row = [];
|
||||||
$cols = array_keys($info['columns']);
|
foreach ($r as $c => $v) {
|
||||||
foreach ($info['rows'] as $index => $row) {
|
if ($types[$c] === "datetime") {
|
||||||
$this->assertCount(sizeof($cols), $row, "The number of columns in array index $index of expectations for table $table does not match its definition");
|
$dates[] = $v;
|
||||||
$row = array_combine($cols, $row);
|
|
||||||
foreach ($data as $index => $test) {
|
|
||||||
foreach ($test as $col => $value) {
|
|
||||||
switch ($types[$col]) {
|
|
||||||
case "datetime":
|
|
||||||
$test[$col] = $this->approximateTime($row[$col], $value);
|
|
||||||
break;
|
|
||||||
case "int":
|
|
||||||
$test[$col] = ValueInfo::normalize($value, ValueInfo::T_INT | ValueInfo::M_DROP | valueInfo::M_NULL);
|
|
||||||
break;
|
|
||||||
case "float":
|
|
||||||
$test[$col] = ValueInfo::normalize($value, ValueInfo::T_FLOAT | ValueInfo::M_DROP | valueInfo::M_NULL);
|
|
||||||
break;
|
|
||||||
case "bool":
|
|
||||||
$test[$col] = (int) ValueInfo::normalize($value, ValueInfo::T_BOOL | ValueInfo::M_DROP | valueInfo::M_NULL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ($row === $test) {
|
if ($v === null) {
|
||||||
$data[$index] = $test;
|
$row[] = "";
|
||||||
break;
|
} elseif (static::$stringOutput || is_string($v)) {
|
||||||
|
$row[] = '"'.str_replace('"', '""', (string) $v).'"';
|
||||||
|
} else {
|
||||||
|
$row[] = (string) $v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->assertContains($row, $data, "Actual Table $table does not contain record at expected array index $index");
|
$exp[] = implode(",", $row);
|
||||||
$found = array_search($row, $data, true);
|
|
||||||
unset($data[$found]);
|
|
||||||
}
|
}
|
||||||
$this->assertSame([], $data, "Actual table $table contains extra rows not in expectations");
|
// serialize the rows of the actual output
|
||||||
|
$cols = implode(",", array_map(function($v) {
|
||||||
|
return '"'.str_replace('"', '""', $v).'"';
|
||||||
|
}, array_keys($info['columns'])));
|
||||||
|
$data = $drv->prepare("SELECT $cols from $table")->run()->getAll();
|
||||||
|
$types = $info['columns'];
|
||||||
|
$act = [];
|
||||||
|
foreach ($data as $r) {
|
||||||
|
$row = [];
|
||||||
|
foreach ($r as $c => $v) {
|
||||||
|
if ($types[$c] === "datetime") {
|
||||||
|
if (array_search($v, $dates, true) === false) {
|
||||||
|
$v = Date::transform(Date::sub("PT1S", $v), "sql");
|
||||||
|
if (array_search($v, $dates, true) === false) {
|
||||||
|
$v = Date::transform(Date::add("PT2S", $v), "sql");
|
||||||
|
if (array_search($v, $dates, true) === false) {
|
||||||
|
$v = Date::transform(Date::sub("PT1S", $v), "sql");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($v === null) {
|
||||||
|
$row[] = "";
|
||||||
|
} elseif (is_string($v)) {
|
||||||
|
$row[] = '"'.str_replace('"', '""', (string) $v).'"';
|
||||||
|
} else {
|
||||||
|
$row[] = (string) $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$act[] = implode(",", $row);
|
||||||
|
}
|
||||||
|
$this->assertSame($exp, $act, "Actual table $table does not match expectations");
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function primeExpectations(array $source, array $tableSpecs): array {
|
public function primeExpectations(array $source, array $tableSpecs): array {
|
||||||
|
|
Loading…
Reference in a new issue