From 5af4572ae18a2a3d03475f5d522d4358715c9002 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 05:39:05 +0100 Subject: [PATCH] test(chat): verify invalid requests and diagnostic name lookup failures --- src/chat/chat_core.h | 12 ++++++++---- tests/test_chat_member.c | 23 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/chat/chat_core.h b/src/chat/chat_core.h index 3b0562f3..8c22b7fb 100644 --- a/src/chat/chat_core.h +++ b/src/chat/chat_core.h @@ -258,18 +258,21 @@ struct voice_played_req { uint64_t author_node_id; }; -// Сбор статуса (NTP + connections) и отправка в GUI. arg = struct UTUN_INSTANCE* (inst) +/* Снять текстовый статус NTP/connections:arg — заимствованный inst,ответ STATUS_REFRESH без конечного NUL. + * Динамический буфер живёт до возврата обработчика;отказ памяти/форматирования логируется без частичного события. */ void chat_core_collect_status_trampoline(void* arg); // Лёгкий запрос нагрузки uasync для GUI. arg = inst, ответ CHAT_EVT_CPU_LOAD. void chat_core_collect_cpu_load_trampoline(void* arg); -// Сбор списка ETCP-соединений и отправка в GUI (binary packet). arg = inst +/* Список до250 ETCP-соединений:arg — заимствованный inst,ответ CONN_LIST [count:u16 native][entry:46]*. + * Entry:peer:u64,flags:u8,links:u8,rtt:u16,inflight_KiB:u16,name:32;числа native,links/inflight насыщаются. */ void chat_core_collect_conn_list_trampoline(void* arg); struct chat_node_arg { struct UTUN_INSTANCE* inst; uint64_t node_id; }; -// Сбор полных метрик одного соединения (arg = u_malloc struct chat_node_arg) +/* Снять текстовые метрики соединения:arg — u_malloc chat_node_arg,забирается/освобождается,inst заимствован. + * Ответ CONN_METRICS без NUL,динамический буфер только до возврата обработчика;при OOM нет частичного события. */ void chat_core_collect_conn_metrics_trampoline(void* arg); /* Диагностика карточки участника: запрос/codec находятся в chat_member.h. */ @@ -288,7 +291,8 @@ int chat_core_get_messages_json(struct UTUN_INSTANCE* inst, const char* ch_id, i * connected означает наличие транспорта в реестре, а не его UP или готовность группы. */ int chat_core_get_members_json(struct UTUN_INSTANCE* inst, const char* ch_id, char* buf, size_t buf_size, size_t* out_len); -// Имя узла из таблицы nodes +/* Имя nodes с NUL,при необходимости обрезанное до sz-1 байт:0 — прочитано/узел отсутствует,-1 — ошибка. + * out/sz>0 обязательны;отсутствие узла и ошибка оставляют пустую строку (если буфер допустим). */ int chat_core_get_node_name(struct UTUN_INSTANCE* inst, uint64_t node_id, char* out, size_t sz); // Ссылка на описание avatars/.json внутри дерева своего UID/канала. server=0 удаляет ссылку. diff --git a/tests/test_chat_member.c b/tests/test_chat_member.c index f1834447..860fdfc4 100644 --- a/tests/test_chat_member.c +++ b/tests/test_chat_member.c @@ -150,6 +150,15 @@ static void snapshots(struct UTUN_INSTANCE* inst) { } uint8_t* wire = NULL; int count = 0; assert(chat_core_get_member_list(NULL, channel, &wire, &count) < 0 && !wire && !count); assert(!chat_core_member_online(NULL, channel, 7)); + for (int rtt = 0; rtt < 2; rtt++) { + struct chat_member_list_req* req = u_calloc(1, sizeof(*req)); assert(req); strcpy(req->ch_id, channel); + if (rtt) chat_core_request_member_rtt_trampoline(req); else chat_core_request_member_list_trampoline(req); + req = u_malloc(sizeof(*req)); assert(req); req->inst = inst; memset(req->ch_id, '1', sizeof(req->ch_id)); + if (rtt) chat_core_request_member_rtt_trampoline(req); else chat_core_request_member_list_trampoline(req); + } + struct chat_member_radio_mute_req* mute_req = u_malloc(sizeof(*mute_req)); assert(mute_req); + mute_req->inst = inst; mute_req->node_id = 7; memset(mute_req->ch_id, '1', sizeof(mute_req->ch_id)); + chat_core_toggle_member_radio_mute_trampoline(mute_req); on_member_props_changed(7, NULL, channel, inst); unsigned before = member_events; target = "SELECT p.node_id"; fault = F_FINALIZE; on_member_props_changed(7, NULL, channel, inst); assert(fault == F_NONE && before == member_events); on_member_props_changed(7, NULL, channel, inst); assert(member_events == before + 1); @@ -211,6 +220,18 @@ static void details(struct UTUN_INSTANCE* inst) { DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS member detail:4 SQL faults,complete addressed error replies and invalid request codec"); } +static void node_names(struct UTUN_INSTANCE* inst) { + target = "SELECT name FROM nodes"; char out[128]; + for (enum fault point = F_PREPARE; point <= F_FINALIZE; point++) { + if (point == F_CHANGES) continue; + strcpy(out, "previous"); bind_index = 1; fault = point; + assert(chat_core_get_node_name(inst, inst->node_id, out, sizeof(out)) < 0 && !out[0] && fault == F_NONE); + assert(!chat_core_get_node_name(inst, inst->node_id, out, sizeof(out)) && !strcmp(out, inst->name)); + } + out[0] = 'x'; assert(chat_core_get_node_name(inst, inst->node_id, out, 0) < 0 && out[0] == 'x'); + DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS node names:4 SQL faults/retries,cleared failure output and zero-size guard"); +} + static void diagnostics(struct UTUN_INSTANCE* inst) { struct ETCP_CONN connection = {.instance = inst, .peer_node_id = 9, .unacked_bytes = UINT32_MAX}; struct ETCP_LINK* links = u_calloc(300, sizeof(*links)); assert(links); connection.links = links; @@ -274,7 +295,7 @@ int main(void) { inst->my_keys.public_key, inst->my_ed25519_pubkey)); snprintf(database, sizeof(database), "%s/chats.db", directory); assert(sqlite3_open_v2(database, &reader, SQLITE_OPEN_READONLY, NULL) == SQLITE_OK); chat_event_set_handler(inst, event); radio_set_mute_cb(inst, mute, NULL); - snapshots(inst); mutes(inst); details(inst); diagnostics(inst); member_limit(inst); + snapshots(inst); mutes(inst); details(inst); node_names(inst); diagnostics(inst); member_limit(inst); assert(!radio_set_member_muted(inst, group_id, 7, 0)); unsigned display = member_events, audio = mute_events; target = "COMMIT /* radio mute */"; fault = F_EXEC; fail_rollback = 1; inst->running = 1; assert(chat_core_toggle_member_radio_mute(inst, channel, 7) < 0 && fault == F_NONE && !fail_rollback && !inst->running);