From 2b37d071b54342e9605032dc6a573bb1ff8dfb82 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 18:56:36 +0100 Subject: [PATCH] Model admitted CHAT members in media test fixtures --- tests/media_delivery_fixture.h | 9 +++++-- tests/media_members_fixture.h | 45 ++++++++++++++++++++++++++++++++++ tests/test_media_replication.c | 15 ++---------- 3 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 tests/media_members_fixture.h diff --git a/tests/media_delivery_fixture.h b/tests/media_delivery_fixture.h index 32a142b8d..3747bfeac 100644 --- a/tests/media_delivery_fixture.h +++ b/tests/media_delivery_fixture.h @@ -10,6 +10,7 @@ * Стенд сохраняет владение router: пакет потребляется даже при ошибке. Ответы доставляются отдельно, * поэтому можно проверить задержки, дубликаты, чужие ответы и состояние между пачками. */ #include "test_utils.h" +#include "media_members_fixture.h" #include "../src/utun_instance.h" #include "../src/chat/chat_core_priv.h" #include "../src/media_delivery/media_index.h" @@ -59,7 +60,8 @@ int __wrap_RAND_bytes(unsigned char* out, int count) { * В обоих исходах потребляет entry/dgram, как настоящий router; sent_hook вызывается только при успехе. */ int __wrap_etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t dst, struct ll_entry* entry, int force, int mode) { - (void)force; (void)mode; + (void)force; + REQUIRE(mode == (ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED)); int cmd = entry->dgram[1], rc = cmd == fail_send ? -1 : 0; if (cmd == MEDIA_SUBCMD_SYNC_REQ || cmd == MEDIA_SUBCMD_SYNC_RESP || cmd == MEDIA_SUBCMD_CHANGED) REQUIRE(!force); if (cmd == MEDIA_SUBCMD_BLOCK_REQ) request_count++; @@ -117,6 +119,9 @@ static struct UTUN_INSTANCE* instance(uint64_t node) { inst->ua = ua; inst->node_id = node; inst->chat_core = u_calloc(1, sizeof(struct chat_core_ctx)); REQUIRE(inst->chat_core); REQUIRE(sqlite3_open(":memory:", &inst->topo_sqlite_db) == SQLITE_OK); + const uint64_t peers[] = {SERVER, SERVER + 1, SERVER + 2, CLIENT, 0x300, 0x301, 0x302, 0x303, 0x304, 0x400, 0x900, + 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012}; + media_test_group(inst, GROUP, peers, sizeof(peers) / sizeof(*peers)); REQUIRE(!etcp_router_init(inst) && !media_index_init(inst->topo_sqlite_db) && !media_delivery_init(inst)); uint8_t pubkey[32]; size_t length = sizeof(pubkey); EVP_PKEY* key = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, author_key, 32); REQUIRE(key); @@ -133,7 +138,7 @@ static struct UTUN_INSTANCE* instance(uint64_t node) { /* Уничтожить media/router/SQLite/chat_core этого стенда и выполнить poll(0); inst обязателен, повтор не допускается. * Дополнительными ресурсами, которые тест сам добавил в inst (например topo_groups), владеет caller. */ static void destroy(struct UTUN_INSTANCE* inst) { - media_delivery_destroy(inst); etcp_router_destroy(inst); sqlite3_close(inst->topo_sqlite_db); + media_delivery_destroy(inst); etcp_router_destroy(inst); media_test_groups_free(inst); sqlite3_close(inst->topo_sqlite_db); u_free(inst->chat_core); u_free(inst); uasync_poll(ua, 0); } diff --git a/tests/media_members_fixture.h b/tests/media_members_fixture.h new file mode 100644 index 000000000..89bb9612d --- /dev/null +++ b/tests/media_members_fixture.h @@ -0,0 +1,45 @@ +/* Минимальная CHAT-топология и результат проверки member_sync для unit-тестов медиа. + * Не моделирует подписи участников: сетевые тесты используют test_membership.h. */ +#ifndef MEDIA_MEMBERS_FIXTURE_H +#define MEDIA_MEMBERS_FIXTURE_H +#include "../src/utun_instance.h" +#include "../src/routing_layer/topo_group.h" +#include "../lib/mem.h" +#include "../lib/debug_config.h" +#include +#include + +static void media_test_group(struct UTUN_INSTANCE* inst, uint64_t id, const uint64_t* peers, size_t count) { + if (!inst->topo_groups) { + inst->topo_groups = u_calloc(1, sizeof(*inst->topo_groups)); + if (!inst->topo_groups) abort(); + inst->topo_groups->instance = inst; + inst->topo_groups->group_list = queue_new(inst->ua, 16, 0, 8, "media_test_groups"); + if (!inst->topo_groups->group_list) abort(); + } + if (!topo_groups_find(inst->topo_groups, id)) { + struct TOPO_GROUP* g = (void*)queue_entry_new(sizeof(*g) - sizeof(struct ll_entry)); + if (!g) abort(); + g->instance = inst; g->group_id = id; g->group_type = TOPO_GROUP_TYPE_CHAT; + snprintf(g->channel_id, sizeof(g->channel_id), "%llu", (unsigned long long)id); + if (queue_data_put_with_index(inst->topo_groups->group_list, &g->ll)) abort(); + } + char sql[512]; + snprintf(sql, sizeof(sql), "CREATE TABLE IF NOT EXISTS peers_%llu(node_id INTEGER PRIMARY KEY,source INTEGER DEFAULT 0," + "deleted INTEGER DEFAULT 0,admitted INTEGER DEFAULT 1,node_type INTEGER DEFAULT 0,adm_tags TEXT DEFAULT '{}')", + (unsigned long long)id); + if (sqlite3_exec(inst->topo_sqlite_db, sql, NULL, NULL, NULL) != SQLITE_OK) abort(); + for (size_t i = 0; i <= count; i++) { + snprintf(sql, sizeof(sql), "INSERT OR IGNORE INTO peers_%llu(node_id) VALUES(%lld)", + (unsigned long long)id, (long long)(i == count ? inst->node_id : peers[i])); + if (sqlite3_exec(inst->topo_sqlite_db, sql, NULL, NULL, NULL) != SQLITE_OK) abort(); + } +} + +/* Только для минимальных групп этого стенда, после остановки media/router. */ +static void media_test_groups_free(struct UTUN_INSTANCE* inst) { + if (!inst->topo_groups) return; + queue_clear(inst->topo_groups->group_list); queue_free(inst->topo_groups->group_list); + u_free(inst->topo_groups); inst->topo_groups = NULL; +} +#endif diff --git a/tests/test_media_replication.c b/tests/test_media_replication.c index 8ad87b66d..1d3b4bf76 100644 --- a/tests/test_media_replication.c +++ b/tests/test_media_replication.c @@ -58,18 +58,9 @@ static void write_le(void* ptr, uint64_t v, unsigned n) { /* Только транспорт управляемый: каталог, роли, router-очереди и обработчики — настоящие. * Таблица peers отражает результат уже проверенного member_sync; криптография членства проверяется его тестами. */ static void add_group(struct UTUN_INSTANCE* inst, uint64_t group) { - if (!inst->topo_groups) { - inst->topo_groups = u_calloc(1, sizeof(*inst->topo_groups)); REQUIRE(inst->topo_groups); - inst->topo_groups->instance = inst; - inst->topo_groups->group_list = queue_new(ua, 16, 0, 8, "test_groups"); REQUIRE(inst->topo_groups->group_list); - } - struct ll_entry* e = queue_entry_new(sizeof(struct TOPO_GROUP) - sizeof(struct ll_entry)); REQUIRE(e); - struct TOPO_GROUP* g = (struct TOPO_GROUP*)e; - g->instance = inst; g->group_id = group; g->group_type = TOPO_GROUP_TYPE_CHAT; - snprintf(g->channel_id, sizeof(g->channel_id), "%llu", (unsigned long long)group); - queue_data_put_with_index(inst->topo_groups->group_list, e); + media_test_group(inst, group, NULL, 0); char command[512]; - snprintf(command, sizeof(command), "CREATE TABLE peers_%llu(node_id INTEGER PRIMARY KEY,adm_tags TEXT,admitted INTEGER DEFAULT 1);" + snprintf(command, sizeof(command), "DELETE FROM peers_%llu;" "INSERT INTO peers_%llu(node_id,adm_tags) VALUES(256,'{\"supernode\":\"yes\"}'),(512,'{\"supernode\":\"yes\"}'),(768,'{\"supernode\":\"yes\"}')", (unsigned long long)group, (unsigned long long)group); sql(inst->md.db, command); @@ -81,9 +72,7 @@ static struct UTUN_INSTANCE* node(uint64_t id) { } static void free_node(struct UTUN_INSTANCE* inst) { - struct TOPO_GROUPS* groups = inst->topo_groups; destroy(inst); - if (groups) { queue_free(groups->group_list); u_free(groups); } } static struct media_super_peer* peer(struct UTUN_INSTANCE* inst, uint64_t remote, uint64_t group) {