File indexing completed on 2024-11-10 04:40:19

0001 /*
0002     SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
0003     SPDX-FileCopyrightText: 2012 Volker Krause <vkrause@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "dbinitializertest.h"
0009 #include "unittestschema.h"
0010 #include <QIODevice>
0011 
0012 #define DBINITIALIZER_UNITTEST
0013 #include "storage/dbinitializer.cpp"
0014 #undef DBINITIALIZER_UNITTEST
0015 
0016 #include "shared/aktest.h"
0017 
0018 #define QL1S(x) QLatin1StringView(x)
0019 
0020 using namespace Akonadi::Server;
0021 
0022 Q_DECLARE_METATYPE(QList<DbIntrospector::ForeignKey>)
0023 
0024 class StatementCollector : public TestInterface
0025 {
0026 public:
0027     void execStatement(const QString &statement) override
0028     {
0029         statements.push_back(statement);
0030     }
0031 
0032     QStringList statements;
0033 };
0034 
0035 class DbFakeIntrospector : public DbIntrospector
0036 {
0037 public:
0038     explicit DbFakeIntrospector(const QSqlDatabase &database)
0039         : DbIntrospector(database)
0040         , m_hasTable(false)
0041         , m_hasIndex(false)
0042         , m_tableEmpty(true)
0043     {
0044     }
0045 
0046     bool hasTable(const QString &tableName) override
0047     {
0048         Q_UNUSED(tableName)
0049         return m_hasTable;
0050     }
0051     bool hasIndex(const QString &tableName, const QString &indexName) override
0052     {
0053         Q_UNUSED(tableName)
0054         Q_UNUSED(indexName)
0055         return m_hasIndex;
0056     }
0057     bool hasColumn(const QString &tableName, const QString &columnName) override
0058     {
0059         Q_UNUSED(tableName)
0060         Q_UNUSED(columnName)
0061         return false;
0062     }
0063     bool isTableEmpty(const QString &tableName) override
0064     {
0065         Q_UNUSED(tableName)
0066         return m_tableEmpty;
0067     }
0068     QList<ForeignKey> foreignKeyConstraints(const QString &tableName) override
0069     {
0070         Q_UNUSED(tableName)
0071         return m_foreignKeys;
0072     }
0073 
0074     QString getAutoIncrementValueQuery(const QString &tableName, const QString &columnName) override
0075     {
0076         Q_UNUSED(tableName);
0077         Q_UNUSED(columnName);
0078         return {};
0079     }
0080 
0081     QString updateAutoIncrementValueQuery(const QString &tableName, const QString &columnName, qint64 value) override
0082     {
0083         Q_UNUSED(tableName);
0084         Q_UNUSED(columnName);
0085         Q_UNUSED(value);
0086 
0087         return {};
0088     }
0089 
0090     QList<ForeignKey> m_foreignKeys;
0091     bool m_hasTable;
0092     bool m_hasIndex;
0093     bool m_tableEmpty;
0094 };
0095 
0096 void DbInitializerTest::initTestCase()
0097 {
0098     Q_INIT_RESOURCE(akonadidb);
0099 }
0100 
0101 void DbInitializerTest::testRun_data()
0102 {
0103     QTest::addColumn<QString>("driverName");
0104     QTest::addColumn<QString>("filename");
0105     QTest::addColumn<bool>("hasTable");
0106     QTest::addColumn<QList<DbIntrospector::ForeignKey>>("fks");
0107 
0108     QList<DbIntrospector::ForeignKey> fks;
0109 
0110     QTest::newRow("mysql") << "QMYSQL"
0111                            << ":dbinit_mysql" << false << fks;
0112     QTest::newRow("sqlite") << "QSQLITE"
0113                             << ":dbinit_sqlite" << false << fks;
0114     QTest::newRow("psql") << "QPSQL"
0115                           << ":dbinit_psql" << false << fks;
0116 
0117     DbIntrospector::ForeignKey fk;
0118     fk.name = QL1S("myForeignKeyIdentifier");
0119     fk.column = QL1S("collectionId");
0120     fk.refTable = QL1S("CollectionTable");
0121     fk.refColumn = QL1S("id");
0122     fk.onUpdate = QL1S("RESTRICT");
0123     fk.onDelete = QL1S("CASCADE");
0124     fks.push_back(fk);
0125 
0126     QTest::newRow("mysql (incremental)") << "QMYSQL"
0127                                          << ":dbinit_mysql_incremental" << true << fks;
0128     QTest::newRow("sqlite (incremental)") << "QSQLITE"
0129                                           << ":dbinit_sqlite_incremental" << true << fks;
0130     QTest::newRow("psql (incremental)") << "QPSQL"
0131                                         << ":dbinit_psql_incremental" << true << fks;
0132 }
0133 
0134 void DbInitializerTest::testRun()
0135 {
0136     QFETCH(QString, driverName);
0137     QFETCH(QString, filename);
0138     QFETCH(bool, hasTable);
0139     QFETCH(QList<DbIntrospector::ForeignKey>, fks);
0140 
0141     QFile file(filename);
0142     QVERIFY(file.open(QFile::ReadOnly));
0143 
0144     if (QSqlDatabase::drivers().contains(driverName)) {
0145         QSqlDatabase db = QSqlDatabase::addDatabase(driverName, driverName);
0146         UnitTestSchema schema;
0147         DbInitializer::Ptr initializer = DbInitializer::createInstance(db, &schema);
0148         QVERIFY(bool(initializer));
0149 
0150         StatementCollector collector;
0151         initializer->setTestInterface(&collector);
0152         auto introspector = new DbFakeIntrospector(db);
0153         introspector->m_hasTable = hasTable;
0154         introspector->m_hasIndex = hasTable;
0155         introspector->m_tableEmpty = !hasTable;
0156         introspector->m_foreignKeys = fks;
0157         initializer->setIntrospector(DbIntrospector::Ptr(introspector));
0158 
0159         QVERIFY(initializer->run());
0160         QVERIFY(initializer->updateIndexesAndConstraints());
0161         QVERIFY(!collector.statements.isEmpty());
0162 
0163         for (const QString &statement : std::as_const(collector.statements)) {
0164             const QString expected = readNextStatement(&file).simplified();
0165 
0166             QString normalized = statement.simplified();
0167             normalized.replace(QLatin1StringView(" ,"), QLatin1StringView(","));
0168             normalized.replace(QLatin1StringView(" )"), QLatin1StringView(")"));
0169             QCOMPARE(normalized, expected);
0170         }
0171 
0172         QVERIFY(initializer->errorMsg().isEmpty());
0173     }
0174 }
0175 
0176 QString DbInitializerTest::readNextStatement(QIODevice *io)
0177 {
0178     QString statement;
0179     while (!io->atEnd()) {
0180         const QString line = QString::fromUtf8(io->readLine());
0181         if (line.trimmed().isEmpty() && !statement.isEmpty()) {
0182             return statement;
0183         }
0184         statement += line;
0185     }
0186 
0187     return statement;
0188 }
0189 
0190 AKTEST_MAIN(DbInitializerTest)
0191 
0192 #include "moc_dbinitializertest.cpp"