Browse Source

Partial rewrite of database table comparison

Contents still need to be sorted for tests to pass.
dbtest
J. King 2 years ago
parent
commit
51ce4ae92b
  1. 146
      tests/cases/Database/TestDatabase.php
  2. 224
      tests/lib/AbstractTest.php

146
tests/cases/Database/TestDatabase.php

@ -10,152 +10,6 @@ use JKingWeb\Arsse\Database;
/** @covers \JKingWeb\Arsse\Database */
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;
public function setUp(): void {

224
tests/lib/AbstractTest.php

@ -31,6 +31,152 @@ use Laminas\Diactoros\Response\XmlResponse;
abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
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 $confMock;
protected $langMock;
@ -260,47 +406,61 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
return true;
}
public function compareExpectations(Driver $drv, array $expected): bool {
public function compareExpectations(Driver $drv, array $expected): void {
foreach ($expected as $table => $info) {
$cols = array_map(function($v) {
// serialize the rows of the expected output
$types = array_values($info['columns']);
$exp = [];
$dates = [];
foreach ($info['rows'] as $r) {
$row = [];
foreach ($r as $c => $v) {
if ($types[$c] === "datetime") {
$dates[] = $v;
}
if ($v === null) {
$row[] = "";
} elseif (static::$stringOutput || is_string($v)) {
$row[] = '"'.str_replace('"', '""', (string) $v).'"';
} else {
$row[] = (string) $v;
}
}
$exp[] = implode(",", $row);
}
// serialize the rows of the actual output
$cols = implode(",", array_map(function($v) {
return '"'.str_replace('"', '""', $v).'"';
}, array_keys($info['columns']));
$cols = implode(",", $cols);
$types = $info['columns'];
}, array_keys($info['columns'])));
$data = $drv->prepare("SELECT $cols from $table")->run()->getAll();
$cols = array_keys($info['columns']);
foreach ($info['rows'] as $index => $row) {
$this->assertCount(sizeof($cols), $row, "The number of columns in array index $index of expectations for table $table does not match its definition");
$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;
$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 ($row === $test) {
$data[$index] = $test;
break;
if ($v === null) {
$row[] = "";
} elseif (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");
$found = array_search($row, $data, true);
unset($data[$found]);
$act[] = implode(",", $row);
}
$this->assertSame([], $data, "Actual table $table contains extra rows not in expectations");
$this->assertSame($exp, $act, "Actual table $table does not match expectations");
}
return true;
}
public function primeExpectations(array $source, array $tableSpecs): array {

Loading…
Cancel
Save