Browse Source

fix(test): create db tables in defined order, verify indexes

indexes need to be created after their corresponding table is created.
QMap doesn't enforce insertion order, just key order, so use a vector.
Also verify indexes from sql_master instead of only tables.
reviewable/pr5873/r2
Anthony Bilinski 6 years ago
parent
commit
05064771ab
No known key found for this signature in database
GPG Key ID: 2AA8E0DA1B31FB3C
  1. 44
      test/persistence/dbschema_test.cpp

44
test/persistence/dbschema_test.cpp

@ -25,8 +25,20 @@
#include <QtTest/QtTest> #include <QtTest/QtTest>
#include <QString> #include <QString>
#include <algorithm>
#include <memory> #include <memory>
struct SqliteMasterEntry {
QString name;
QString sql;
};
bool operator==(const SqliteMasterEntry& lhs, const SqliteMasterEntry& rhs)
{
return lhs.name == rhs.name &&
lhs.sql == rhs.sql;
}
class TestDbSchema : public QObject class TestDbSchema : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -41,8 +53,8 @@ private slots:
void cleanupTestCase(); void cleanupTestCase();
private: private:
bool initSucess{false}; bool initSucess{false};
void createSchemaAtVersion(std::shared_ptr<RawDatabase>, const QMap<QString, QString>& schema); void createSchemaAtVersion(std::shared_ptr<RawDatabase>, const std::vector<SqliteMasterEntry>& schema);
void verifyDb(std::shared_ptr<RawDatabase> db, const QMap<QString, QString>& expectedSql); void verifyDb(std::shared_ptr<RawDatabase> db, const std::vector<SqliteMasterEntry>& expectedSql);
}; };
const QString testFileList[] = { const QString testFileList[] = {
@ -57,7 +69,7 @@ const QString testFileList[] = {
// db schemas can be select with "SELECT name, sql FROM sqlite_master;" on the database. // db schemas can be select with "SELECT name, sql FROM sqlite_master;" on the database.
const QMap<QString, QString> schema0 { const std::vector<SqliteMasterEntry> schema0 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"}, {"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"}, {"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"history", "CREATE TABLE history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, chat_id INTEGER NOT NULL, sender_alias INTEGER NOT NULL, message BLOB NOT NULL)"}, {"history", "CREATE TABLE history (id INTEGER PRIMARY KEY, timestamp INTEGER NOT NULL, chat_id INTEGER NOT NULL, sender_alias INTEGER NOT NULL, message BLOB NOT NULL)"},
@ -65,7 +77,7 @@ const QMap<QString, QString> schema0 {
}; };
// added file transfer history // added file transfer history
const QMap<QString, QString> schema1 { const std::vector<SqliteMasterEntry> schema1 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"}, {"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"}, {"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"}, {"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"},
@ -74,7 +86,7 @@ const QMap<QString, QString> schema1 {
}; };
// move stuck faux offline messages do a table of "broken" messages // move stuck faux offline messages do a table of "broken" messages
const QMap<QString, QString> schema2 { const std::vector<SqliteMasterEntry> schema2 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"}, {"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"}, {"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"}, {"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"},
@ -87,7 +99,7 @@ const QMap<QString, QString> schema2 {
const auto schema3 = schema2; const auto schema3 = schema2;
// create index in history table on chat_id to improve query speed. Not a real schema upgrade. // create index in history table on chat_id to improve query speed. Not a real schema upgrade.
const QMap<QString, QString> schema4 { const std::vector<SqliteMasterEntry> schema4 {
{"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"}, {"aliases", "CREATE TABLE aliases (id INTEGER PRIMARY KEY, owner INTEGER, display_name BLOB NOT NULL, UNIQUE(owner, display_name))"},
{"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"}, {"faux_offline_pending", "CREATE TABLE faux_offline_pending (id INTEGER PRIMARY KEY)"},
{"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"}, {"file_transfers", "CREATE TABLE file_transfers (id INTEGER PRIMARY KEY, chat_id INTEGER NOT NULL, file_restart_id BLOB NOT NULL, file_name BLOB NOT NULL, file_path BLOB NOT NULL, file_hash BLOB NOT NULL, file_size INTEGER NOT NULL, direction INTEGER NOT NULL, file_state INTEGER NOT NULL)"},
@ -116,28 +128,32 @@ void TestDbSchema::cleanupTestCase()
} }
} }
void TestDbSchema::verifyDb(std::shared_ptr<RawDatabase> db, const QMap<QString, QString>& expectedSql) void TestDbSchema::verifyDb(std::shared_ptr<RawDatabase> db, const std::vector<SqliteMasterEntry>& expectedSql)
{ {
QVERIFY(db->execNow(RawDatabase::Query(QStringLiteral( QVERIFY(db->execNow(RawDatabase::Query(QStringLiteral(
"SELECT name, sql FROM sqlite_master " "SELECT name, sql FROM sqlite_master;"),
"WHERE type='table';"),
[&](const QVector<QVariant>& row) { [&](const QVector<QVariant>& row) {
const QString tableName = row[0].toString(); const QString tableName = row[0].toString();
if (row[1].isNull()) {
// implicit indexes are automatically created for primary key constraints and unique constraints
// so their existence is already covered by the table creation SQL
return;
}
QString tableSql = row[1].toString(); QString tableSql = row[1].toString();
QVERIFY(expectedSql.contains(tableName));
// table and column names can be quoted. UPDATE TEABLE automatically quotes the new names, but this // table and column names can be quoted. UPDATE TEABLE automatically quotes the new names, but this
// has no functional impact on the schema. Strip quotes for comparison so that our created schema // has no functional impact on the schema. Strip quotes for comparison so that our created schema
// matches schema made from UPDATE TABLEs. // matches schema made from UPDATE TABLEs.
const QString unquotedTableSql = tableSql.remove("\""); const QString unquotedTableSql = tableSql.remove("\"");
QVERIFY(expectedSql.value(tableName) == unquotedTableSql); SqliteMasterEntry entry{tableName, unquotedTableSql};
QVERIFY(std::find(expectedSql.begin(), expectedSql.end(), entry) != expectedSql.end());
}))); })));
} }
void TestDbSchema::createSchemaAtVersion(std::shared_ptr<RawDatabase> db, const QMap<QString, QString>& schema) void TestDbSchema::createSchemaAtVersion(std::shared_ptr<RawDatabase> db, const std::vector<SqliteMasterEntry>& schema)
{ {
QVector<RawDatabase::Query> queries; QVector<RawDatabase::Query> queries;
for (auto const& tableCreation : schema.values()) { for (auto const& entry : schema) {
queries += tableCreation; queries += entry.sql;
} }
QVERIFY(db->execNow(queries)); QVERIFY(db->execNow(queries));
} }

Loading…
Cancel
Save