From f11ab51ccc87a6f192c822c8df1ee5cf1ac34bb2 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 17:51:51 +0100 Subject: [PATCH] Check stale forms, read failures and member command memory ownership --- tools/chatgui/tests/test_member_dialogs.cpp | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tools/chatgui/tests/test_member_dialogs.cpp b/tools/chatgui/tests/test_member_dialogs.cpp index 22aea257..b43e58d2 100644 --- a/tools/chatgui/tests/test_member_dialogs.cpp +++ b/tools/chatgui/tests/test_member_dialogs.cpp @@ -16,6 +16,7 @@ extern "C" { #include "utun_instance.h" #include "chat/chat_core.h" #include "debug_config.h" +#include "mem.h" } #ifdef UTUN_TEST_MEMBER_ALLOC_FAILURES @@ -129,11 +130,14 @@ static bool save(QDialog* dialog) { return QMetaObject::invokeMethod(dialog, "on class MemberDialogTest : public QObject { Q_OBJECT + size_t allocationBaseline = 0; private slots: void initTestCase() { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_CHAT_SYNC, DEBUG_LEVEL_DEBUG); gui_bridge_init(); } + void init() { allocationBaseline = u_get_allocated_count(); } + void cleanup() { QCoreApplication::processEvents(); QCOMPARE(u_get_allocated_count(), allocationBaseline); } void saves_data() { QTest::addColumn("properties"); QTest::addColumn("failure"); for (bool properties : {false, true}) for (const char* failure : {"none", "prepare", "step", "commit", "missing"}) @@ -213,6 +217,47 @@ private slots: } else { QCOMPARE(accepted.count(), 0); QVERIFY(!errorLabel(dialog.get())->text().isEmpty()); } if (other) { core.db.setDb(core.instance->topo_sqlite_db); QCOMPARE(sqlite3_close(other), SQLITE_OK); } } + void failedRead_data() { + QTest::addColumn("properties"); + QTest::newRow("rename") << false; QTest::newRow("properties") << true; + } + void failedRead() { + QFETCH(bool, properties); MemberCore core; QVERIFY(core.start()); const auto before = core.snapshot(); + QCOMPARE(core.run([&] { return sqlite3_set_authorizer(core.db.m_db, + [](void*, int action, const char*, const char* column, const char*, const char*) { + if (action == SQLITE_READ && column && !strcmp(column, "local_nick")) { + DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "[INJECT] initial nickname read denied"); return SQLITE_DENY; + } + return SQLITE_OK; + }, nullptr); }), SQLITE_OK); + auto dialog = makeDialog(core, properties); QSignalSpy accepted(dialog.get(), &QDialog::accepted); + QCOMPARE(core.run([&] { return sqlite3_set_authorizer(core.db.m_db, nullptr, nullptr); }), SQLITE_OK); + QVERIFY(save(dialog.get())); QCOMPARE(accepted.count(), 0); QVERIFY(!errorLabel(dialog.get())->text().isEmpty()); + QCOMPARE(core.snapshot(), before); + } + void staleOwnerAndUnchangedOwner() { + MemberCore core; QVERIFY(core.start()); auto dialog = makeDialog(core, true); + QCOMPARE(core.run([&] { return chat_core_save_member(core.instance, qPrintable(core.channel), core.node, + "Newer nickname", "{\"owner_test\":\"newer\"}"); }), 0); + const auto before = core.snapshot(); QSignalSpy accepted(dialog.get(), &QDialog::accepted); + dialog->findChild()->setText("Stale nickname"); QVERIFY(save(dialog.get())); core.resume(); + QTRY_VERIFY_WITH_TIMEOUT(!errorLabel(dialog.get())->text().isEmpty(), 3000); core.pause(); + QCOMPARE(accepted.count(), 0); QCOMPARE(core.snapshot(), before); + int result = -99; QObject context; + QVERIFY(gui_bridge_save_member(core.db.m_db, core.channel, core.node, "", "{}", &context, [&](int rc) { result = rc; })); + core.resume(); QTRY_COMPARE_WITH_TIMEOUT(result, 0, 3000); core.pause(); + QVERIFY(core.snapshot().startsWith('\n')); // No-op owner-блок не мешает снятию локального ника. + } + void validation() { + MemberCore core; QVERIFY(core.start()); QObject context; bool called = false; + const auto callback = [&](int) { called = true; }; + QVERIFY(!gui_bridge_save_member(core.db.m_db, "01", core.node, "Nick", {}, &context, callback)); + QVERIFY(!gui_bridge_save_member(core.db.m_db, core.channel, 0, "Nick", {}, &context, callback)); + QVERIFY(!gui_bridge_save_member(core.db.m_db, core.channel, core.node, QString(QChar(0)), {}, &context, callback)); + QVERIFY(!gui_bridge_save_member(core.db.m_db, core.channel, core.node, "Nick", QByteArray(8193, 'x'), &context, callback)); + QVERIFY(!gui_bridge_save_member(core.db.m_db, core.channel, core.node, "Nick", {}, nullptr, callback)); + QCoreApplication::processEvents(); QVERIFY(!called); + } #ifdef UTUN_TEST_MEMBER_ALLOC_FAILURES void allocations_data() { QTest::addColumn("properties"); QTest::addColumn("queue");