From 84bf74e03434ef491f70f0790c912e6f6f0254b5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 17:52:52 +0100 Subject: [PATCH] Limit commit fault injection to the member save transaction --- tools/chatgui/tests/test_member_dialogs.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/chatgui/tests/test_member_dialogs.cpp b/tools/chatgui/tests/test_member_dialogs.cpp index b43e58d2..dad02d84 100644 --- a/tools/chatgui/tests/test_member_dialogs.cpp +++ b/tools/chatgui/tests/test_member_dialogs.cpp @@ -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(arg); const char* sql = sqlite3_sql(static_cast(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(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(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") {