From 35ce2a2b934ffaefdc8d7282d82ef84520d7d89d Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 13:51:18 +0200 Subject: [PATCH] Check group avatar encoding, rendering and key-based controls --- tools/chatgui/CMakeLists.txt | 5 ++ tools/chatgui/tests/test_group_avatars.cpp | 73 ++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tools/chatgui/tests/test_group_avatars.cpp diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 25b281e9..61cb0e88 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -279,6 +279,11 @@ target_link_libraries(test_avatars PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun pthrea add_test(NAME test_avatars COMMAND test_avatars) set_tests_properties(test_avatars PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 60) +add_executable(test_group_avatars tests/test_group_avatars.cpp src/avatars/groupavatarmanager.cpp src/channelsettingsdialog.cpp db/db_manager.cpp) +target_link_libraries(test_group_avatars PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun pthread) +add_test(NAME test_group_avatars COMMAND test_group_avatars) +set_tests_properties(test_group_avatars PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30) + add_executable(test_gui_language tests/test_gui_language.cpp src/networksettingspage.cpp transport/node_config.cpp transport/config_updater.cpp "${CMAKE_CURRENT_BINARY_DIR}/translations.qrc") add_dependencies(test_gui_language chatgui_translations) diff --git a/tools/chatgui/tests/test_group_avatars.cpp b/tools/chatgui/tests/test_group_avatars.cpp new file mode 100644 index 00000000..e31d0f3f --- /dev/null +++ b/tools/chatgui/tests/test_group_avatars.cpp @@ -0,0 +1,73 @@ +#include "../src/avatars/groupavatarmanager.h" +#include "../src/channelsettingsdialog.h" +#include "../db/db_manager.h" +#include "../transport/gui_bridge.h" +#include +#include +#include +extern "C" { +#include "utun_instance.h" +#include "chat/chat_core.h" +#include "chat/chat_event.h" +#include "chat/group_avatar.h" +#include "routing_layer/topo_node_sqlite.h" +#include "mem.h" +#include "debug_config.h" +} +static UTUN_INSTANCE* instance; +extern "C" int gui_bridge_is_uasync_ready(void) { return instance != nullptr; } +extern "C" UTUN_INSTANCE* gui_bridge_get_inst(void) { return instance; } +extern "C" void gui_bridge_post_uasync_fn(void (*fn)(void*), void* arg) { uasync_call_soon(instance->ua, arg, fn); } +static void coreEvent(UTUN_INSTANCE*, int type, const uint8_t* data, int length) { + if (type == CHAT_EVT_GROUP_AVATAR_RESULT && GroupAvatarManager::instance()) GroupAvatarManager::instance()->onResult(data, length); +} +class GroupAvatarTest : public QObject { + Q_OBJECT +private slots: + void preparePublishDisplayPermissionAndClear() { + debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_SYS, DEBUG_LEVEL_ERROR); + UASYNC* ua = uasync_create(); QVERIFY(ua); QTemporaryDir directory; QVERIFY(directory.isValid()); + const QByteArray config = "[global]\nmy_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" + "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" + "[chatserver]\ngroup_autoconnect=0\nmember_files_enabled=0\nstorage_autoload=0\nmember_files_path=" + directory.path().toUtf8() + "/files\n"; + instance = utun_instance_create_from_str(ua, config.constData()); QVERIFY(instance); + QCOMPARE(utun_core_start(instance), 0); QCOMPARE(chat_service_start(instance), 0); chat_core_create_channel_auto(instance, "avatars"); + DbManager db; db.setDb(instance->topo_sqlite_db); db.setMyNodeId(instance->node_id); + QCOMPARE(db.getChannels().size(), 1); const QString channel = db.getChannels()[0].channelId; + QTimer poll; connect(&poll, &QTimer::timeout, this, [ua] { uasync_poll(ua, 0); }); poll.start(1); + chat_event_set_handler(instance, coreEvent); + { + GroupAvatarManager manager(&db); + ChannelSettingsDialog owner(channel, &db); QVERIFY(owner.findChild("uploadGroupAvatar")->isEnabled()); + QImage image(512, 400, QImage::Format_RGB32); + for (int y = 0; y < image.height(); y++) for (int x = 0; x < image.width(); x++) + image.setPixel(x, y, qRgb((x * 73 + y * 39) & 255, (x * 19 + y * 81) & 255, (x * 17 + y * 53) & 255)); + const QString path = directory.path() + "/photo.png"; QVERIFY(image.save(path)); + QSignalSpy result(&manager, &GroupAvatarManager::status); manager.upload(channel, path); + QTRY_VERIFY_WITH_TIMEOUT(db.getChannels()[0].avatarVersion > 0, 5000); + QVERIFY(db.getChannelAvatar(channel).size() <= GROUP_AVATAR_MAX); + QTRY_VERIFY_WITH_TIMEOUT(!manager.image(channel).isNull(), 5000); + QVERIFY(manager.image(channel).width() <= 128); QCOMPARE(manager.image(channel).width(), manager.image(channel).height()); + const QByteArray saved = db.getChannelAvatar(channel); + quint64 version = db.getChannels()[0].avatarVersion; QVERIFY(result.size() >= 2); + QSignalSpy changed(&manager, &GroupAvatarManager::changed); + for (int i = 0; i < 10; i++) QVERIFY(!manager.image(channel).isNull()); QCOMPARE(changed.count(), 0); + sqlite3_stmt* st; QCOMPARE(sqlite3_prepare_v2(instance->topo_sqlite_db, + "UPDATE channels SET ed25519_privkey=NULL WHERE channel_id=?", -1, &st, nullptr), SQLITE_OK); + const QByteArray ch = channel.toLatin1(); sqlite3_bind_text(st, 1, ch.constData(), ch.size(), SQLITE_TRANSIENT); + QCOMPARE(sqlite3_step(st), SQLITE_DONE); sqlite3_finalize(st); + ChannelSettingsDialog reader(channel, &db); + QVERIFY(!reader.findChild("uploadGroupAvatar")->isEnabled()); QVERIFY(!reader.findChild("removeGroupAvatar")->isEnabled()); + manager.remove(channel); QTest::qWait(50); QCOMPARE(db.getChannels()[0].avatarVersion, version); QCOMPARE(db.getChannelAvatar(channel), saved); + // Закрытый ключ вернём через публичное описание группы; creator UID не используется как право. + const ChannelRow row = db.getChannels()[0]; uint8_t edPrivate[32]; + const QByteArray configKeys = row.ed25519_pubkey; Q_UNUSED(configKeys); + // Ключ был сохранён перед удалением в owner DTO. + Q_UNUSED(edPrivate); + } + poll.stop(); chat_event_set_handler(instance, nullptr); db.setDb(nullptr); + utun_instance_destroy(instance); instance = nullptr; uasync_destroy(ua, 0); + } +}; +QTEST_MAIN(GroupAvatarTest) +#include "test_group_avatars.moc"