Limit commit fault injection to the member save transaction

This commit is contained in:
evgeny committed 2026-10-28 00:58:59 +01:00
1 parent f11ab51ccc
commit 84bf74e034
1 file changed
+17 -4
+17 -4
View File
@@ -146,7 +146,7 @@ private slots:
void saves() {
QFETCH(bool, properties); QFETCH(QString, failure);
MemberCore core; QVERIFY(core.start()); auto dialog = makeDialog(core, properties); auto before = core.snapshot();
struct Probe { bool deny; int updates = 0; bool gui = false; } probe{failure == "prepare"};
struct Probe { bool deny; int updates = 0; bool gui = false, memberTransaction = false; int commitRefusals = 0; } probe{failure == "prepare"};
QCOMPARE(core.run([&] {
if (failure == "step") {
QByteArray sql = QString("CREATE TRIGGER refuse_nick BEFORE UPDATE OF local_nick ON peers_%1 "
@@ -157,9 +157,20 @@ private slots:
QByteArray sql = QString("DELETE FROM peers_%1").arg(core.channel).toUtf8();
if (sqlite3_exec(core.db.m_db, sql.constData(), nullptr, nullptr, nullptr) != SQLITE_OK) return -1;
}
if (failure == "commit") sqlite3_commit_hook(core.db.m_db, [](void*) {
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "[INJECT] member commit refused"); return 1;
}, nullptr);
if (failure == "commit") {
sqlite3_trace_v2(core.db.m_db, SQLITE_TRACE_STMT, [](unsigned, void* arg, void* statement, void*) {
auto& probe = *static_cast<Probe*>(arg); const char* sql = sqlite3_sql(static_cast<sqlite3_stmt*>(statement));
if (sql && !strncmp(sql, "BEGIN", 5)) probe.memberTransaction =
!strcmp(sql, "BEGIN IMMEDIATE /* tags */") || !strcmp(sql, "BEGIN IMMEDIATE /* nickname */");
return 0;
}, &probe);
sqlite3_commit_hook(core.db.m_db, [](void* arg) {
auto& probe = *static_cast<Probe*>(arg);
if (!probe.memberTransaction) return 0;
probe.memberTransaction = false; ++probe.commitRefusals;
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "[INJECT] member commit refused"); return 1;
}, &probe);
}
return sqlite3_set_authorizer(core.db.m_db, [](void* arg, int action, const char*, const char* column, const char*, const char*) {
auto& probe = *static_cast<Probe*>(arg);
if (action != SQLITE_UPDATE || !column || strcmp(column, "local_nick")) return SQLITE_OK;
@@ -177,6 +188,7 @@ private slots:
if (failure == "none") QTRY_COMPARE_WITH_TIMEOUT(accepted.count(), 1, 3000);
else QTRY_VERIFY_WITH_TIMEOUT(!errorLabel(dialog.get())->text().isEmpty(), 3000);
core.pause(); QVERIFY(!probe.gui);
if (failure == "commit") QCOMPARE(probe.commitRefusals, 1);
if (failure == "none") QVERIFY(core.snapshot().startsWith(QString::fromUtf8("Новый ник\n").toUtf8()));
else {
QCOMPARE(accepted.count(), 0); QCOMPARE(core.snapshot(), before);
@@ -186,6 +198,7 @@ private slots:
QVERIFY(sqlite3_next_stmt(core.db.m_db, nullptr) == nullptr);
QCOMPARE(core.run([&] {
sqlite3_set_authorizer(core.db.m_db, nullptr, nullptr); sqlite3_commit_hook(core.db.m_db, nullptr, nullptr);
sqlite3_trace_v2(core.db.m_db, 0, nullptr, nullptr);
return sqlite3_exec(core.db.m_db, "DROP TRIGGER IF EXISTS refuse_nick", nullptr, nullptr, nullptr);
}), SQLITE_OK);
if (failure != "none" && failure != "missing") {