Add SQLite and Qt database maintenance regression tests
This commit is contained in:
1 parent
5233513fcc
commit
6ba86bb1df
2 files changed
+141
No files matched your search
@@ -0,0 +1,135 @@
|
||||
/* Настоящие SQLite/DbManager/Qt-страница: optimize без строк, строгие integrity-проверки и SQL-отказы. */
|
||||
#include "../db/db_manager.h"
|
||||
#include "../src/databasesettingspage.h"
|
||||
#include <QtTest>
|
||||
#include <QTemporaryDir>
|
||||
extern "C" {
|
||||
#include "debug_config.h"
|
||||
}
|
||||
|
||||
using Operation = IntegrityResult (DbManager::*)() const;
|
||||
static const Operation operations[] = {&DbManager::integrityCheckQuick, &DbManager::integrityCheckFull, &DbManager::integrityOptimize};
|
||||
static const char* titles[] = {"Quick Check", "Full Check", "Optimize"};
|
||||
static const char* slots[] = {"onQuickCheck", "onFullCheck", "onOptimize"};
|
||||
|
||||
struct Database {
|
||||
QTemporaryDir directory;
|
||||
sqlite3* handle = nullptr;
|
||||
DbManager manager;
|
||||
~Database() {
|
||||
manager.setDb(nullptr);
|
||||
if (handle && sqlite3_close(handle) != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "test DB close failed: statements remain");
|
||||
}
|
||||
bool execute(const char* sql) {
|
||||
char* error = nullptr;
|
||||
const int rc = sqlite3_exec(handle, sql, nullptr, nullptr, &error);
|
||||
if (rc != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "test SQL failed rc=%d: %s", rc, error ? error : sqlite3_errmsg(handle));
|
||||
sqlite3_free(error);
|
||||
return rc == SQLITE_OK;
|
||||
}
|
||||
bool open(bool disk = false) {
|
||||
if (!directory.isValid()) return false;
|
||||
const QByteArray path = disk ? directory.filePath("test.db").toUtf8() : QByteArray(":memory:");
|
||||
const int rc = sqlite3_open(path.constData(), &handle);
|
||||
if (rc != SQLITE_OK) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "test DB open failed rc=%d", rc); return false; }
|
||||
manager.setDb(handle);
|
||||
return execute("CREATE TABLE items(value INTEGER CHECK(value>0)); CREATE INDEX item_value ON items(value);"
|
||||
"INSERT INTO items VALUES(1),(2),(3); SELECT value FROM items WHERE value=2;");
|
||||
}
|
||||
};
|
||||
|
||||
static int denyPragma(void*, int action, const char*, const char*, const char*, const char*) {
|
||||
return action == SQLITE_PRAGMA ? SQLITE_DENY : SQLITE_OK;
|
||||
}
|
||||
|
||||
static QString statusText(DatabaseSettingsPage& page) {
|
||||
for (auto* label : page.findChildren<QLabel*>())
|
||||
for (const auto* title : titles) if (label->text().startsWith(QString::fromLatin1(title) + ':')) return label->text();
|
||||
return {};
|
||||
}
|
||||
|
||||
class DatabaseMaintenanceTest : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void initTestCase() {
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
|
||||
debug_set_category_level(DEBUG_CATEGORY_CONFIG, DEBUG_LEVEL_DEBUG);
|
||||
}
|
||||
void healthyOperations_data() {
|
||||
QTest::addColumn<int>("operation"); QTest::addColumn<bool>("disk");
|
||||
for (int operation = 0; operation < 3; ++operation)
|
||||
for (bool disk : {false, true})
|
||||
QTest::newRow(qPrintable(QString("%1-%2").arg(titles[operation]).arg(disk ? "file" : "memory"))) << operation << disk;
|
||||
}
|
||||
void healthyOperations() {
|
||||
QFETCH(int, operation); QFETCH(bool, disk);
|
||||
Database db; QVERIFY(db.open(disk));
|
||||
for (int repeat = 0; repeat < 2; ++repeat) {
|
||||
const auto result = (db.manager.*operations[operation])();
|
||||
QVERIFY2(result.ok, qPrintable(result.message));
|
||||
QCOMPARE(result.message, QString("ok")); QVERIFY(result.errors.isEmpty()); QVERIFY(result.elapsedMs >= 0);
|
||||
QVERIFY(!sqlite3_next_stmt(db.handle, nullptr));
|
||||
}
|
||||
}
|
||||
void missingDatabase_data() {
|
||||
QTest::addColumn<int>("operation");
|
||||
for (int operation = 0; operation < 3; ++operation) QTest::newRow(titles[operation]) << operation;
|
||||
}
|
||||
void missingDatabase() {
|
||||
QFETCH(int, operation);
|
||||
DbManager manager;
|
||||
const auto result = (manager.*operations[operation])();
|
||||
QVERIFY(!result.ok); QVERIFY(!result.message.isEmpty()); QCOMPARE(result.elapsedMs, 0);
|
||||
}
|
||||
void sqlFailure_data() {
|
||||
QTest::addColumn<int>("operation");
|
||||
for (int operation = 0; operation < 3; ++operation) QTest::newRow(titles[operation]) << operation;
|
||||
}
|
||||
void sqlFailure() {
|
||||
QFETCH(int, operation);
|
||||
Database db; QVERIFY(db.open());
|
||||
QCOMPARE(sqlite3_set_authorizer(db.handle, denyPragma, nullptr), SQLITE_OK);
|
||||
const auto result = (db.manager.*operations[operation])();
|
||||
QVERIFY(!result.ok); QVERIFY2(result.message.contains("authorized"), qPrintable(result.message));
|
||||
QVERIFY(!sqlite3_next_stmt(db.handle, nullptr));
|
||||
QCOMPARE(sqlite3_set_authorizer(db.handle, nullptr, nullptr), SQLITE_OK);
|
||||
const auto retry = (db.manager.*operations[operation])();
|
||||
QVERIFY2(retry.ok, qPrintable(retry.message));
|
||||
}
|
||||
void integrityViolation_data() {
|
||||
QTest::addColumn<int>("operation");
|
||||
QTest::newRow("quick") << 0; QTest::newRow("full") << 1;
|
||||
}
|
||||
void integrityViolation() {
|
||||
QFETCH(int, operation);
|
||||
Database db; QVERIFY(db.open());
|
||||
QVERIFY(db.execute("PRAGMA ignore_check_constraints=ON; INSERT INTO items VALUES(-1),(-2); PRAGMA ignore_check_constraints=OFF;"));
|
||||
const auto result = (db.manager.*operations[operation])();
|
||||
QVERIFY(!result.ok); QVERIFY2(result.message.contains("CHECK constraint failed"), qPrintable(result.message));
|
||||
QVERIFY(!result.errors.isEmpty()); QVERIFY(result.errors.first().contains("CHECK constraint failed"));
|
||||
}
|
||||
void pageShowsResult_data() {
|
||||
QTest::addColumn<int>("operation"); QTest::addColumn<bool>("denied");
|
||||
for (int operation = 0; operation < 3; ++operation)
|
||||
for (bool denied : {false, true})
|
||||
QTest::newRow(qPrintable(QString("%1-%2").arg(titles[operation]).arg(denied ? "denied" : "success"))) << operation << denied;
|
||||
}
|
||||
void pageShowsResult() {
|
||||
QFETCH(int, operation); QFETCH(bool, denied);
|
||||
Database db; QVERIFY(db.open());
|
||||
DatabaseSettingsPage page(&db.manager);
|
||||
if (denied) QCOMPARE(sqlite3_set_authorizer(db.handle, denyPragma, nullptr), SQLITE_OK);
|
||||
QVERIFY(QMetaObject::invokeMethod(&page, slots[operation], Qt::DirectConnection));
|
||||
const auto buttons = page.findChildren<QPushButton*>(); QCOMPARE(buttons.size(), 3);
|
||||
for (auto* button : buttons) QVERIFY(!button->isEnabled());
|
||||
QTRY_VERIFY_WITH_TIMEOUT(!statusText(page).isEmpty(), 1000);
|
||||
QVERIFY2(statusText(page).contains(denied ? ": FAILED" : ": OK"), qPrintable(statusText(page)));
|
||||
const auto details = page.findChild<QPlainTextEdit*>()->toPlainText();
|
||||
if (denied) QVERIFY2(details.contains("authorized"), qPrintable(details));
|
||||
else QCOMPARE(details, QString("ok"));
|
||||
for (auto* button : buttons) QVERIFY(button->isEnabled());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(DatabaseMaintenanceTest)
|
||||
#include "test_database_maintenance.moc"
|
||||
Reference in new issue
Block a user