From 33556fefea4f983ab2a05086a8a0097cccc0464d Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 08:19:58 +0100 Subject: [PATCH] test(chat): exercise channel deletion from a topology notification --- tests/test_chat_lifecycle.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/tests/test_chat_lifecycle.c b/tests/test_chat_lifecycle.c index 45151c2a..936cd7f6 100644 --- a/tests/test_chat_lifecycle.c +++ b/tests/test_chat_lifecycle.c @@ -21,6 +21,9 @@ static int argument, last_target; static sqlite3* reader; static struct nodeinfo_cbk_entry* preserved_callback; static unsigned ready; +static uint64_t route_group; +static int route_delete, route_inspect; +static unsigned route_seen, route_events; extern int __real_member_sync_add_props_cbk(struct UTUN_INSTANCE*, node_props_changed_fn, void*); int __wrap_member_sync_add_props_cbk(struct UTUN_INSTANCE* inst, node_props_changed_fn callback, void* arg) { if (fail_props) { fail_props = 0; fail_malloc = 1; } @@ -97,7 +100,13 @@ static void snapshot(sqlite3* db, uint8_t out[32]) { } static void event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int bytes) { - (void)data; (void)bytes; + if (type == CHAT_EVT_MEMBER_UPDATED && route_group && bytes > 0 && data[0] < 21 && bytes > data[0]) { + char ch[21]; memcpy(ch, data + 1, data[0]); ch[data[0]] = 0; + if (strtoull(ch, NULL, 10) == route_group) { + route_events++; + if (route_delete) { route_delete = 0; chat_core_delete_channel(inst, ch); } + } + } if (type != CHAT_EVT_DB_READY && type != CHAT_EVT_MY_NODE_ID) return; assert(chat_core_is_initialized(inst) && sqlite3_get_autocommit(inst->topo_sqlite_db)); sqlite3_stmt* st = NULL; @@ -129,7 +138,11 @@ static int deny_accounts(void* arg, int action, const char* name, const char* a, } static int veto(void* arg) { (*(unsigned*)arg)++; return 1; } -static void preserved(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, void* arg) { (void)group; (void)node; (void)arg; } +static void preserved(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, void* arg) { + if (route_inspect) { + assert(group->group_id == route_group && node->node_id == ((struct UTUN_INSTANCE*)arg)->node_id); route_seen++; + } +} static void sql_faults(struct UTUN_INSTANCE* inst, const char* path) { const char* queries[] = {"INSERT OR REPLACE INTO local_identity", "SELECT value FROM ui_state WHERE key='media_base'"}; unsigned cases = 0; @@ -205,6 +218,19 @@ static void activation(struct UTUN_INSTANCE* inst, const char* path) { puts("PASS lifecycle:corrupt catalog rejected;second channel activation failure cleans all runtime,keeps data,and retries without false ready"); } +static void route_notifications(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { + ready = 0; assert(!chat_service_start(inst) && ready == 2); + route_group = chat_core_create_channel_auto(inst, "delete after route dispatch"); assert(route_group); uasync_poll(ua, 0); + struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, route_group); assert(group && group->local_node); + route_seen = route_events = 0; route_inspect = route_delete = 1; + topo_fire_nodeinfo_cbk(inst, group, group->local_node); + assert(group->local_node->node_id == inst->node_id && route_seen == 1 && route_delete && !route_events); + route_inspect = 0; uasync_poll(ua, 0); + assert(!route_delete && route_events == 1 && !topo_groups_find(inst->topo_groups, route_group)); + route_group = 0; chat_service_stop(inst); + puts("PASS lifecycle:route observers and caller retain live group/node until dispatch returns;deferred event may delete channel"); +} + int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); size_t baseline = u_get_allocated_count(); utun_instance_set_tun_init_enabled(0); struct UASYNC* ua = uasync_create(); assert(ua); @@ -217,7 +243,7 @@ int main(void) { assert(!utun_add_nodeinfo_cbk(inst, preserved, inst)); preserved_callback = inst->nodeinfo_cbks; unsigned denied = 0; assert(sqlite3_set_authorizer(inst->topo_sqlite_db, deny_accounts, &denied) == SQLITE_OK); refused(inst, database); assert(denied == 1); assert(sqlite3_set_authorizer(inst->topo_sqlite_db, NULL, NULL) == SQLITE_OK); - retry(inst, database); sql_faults(inst, database); validation(inst, database); activation(inst, database); + retry(inst, database); sql_faults(inst, database); validation(inst, database); activation(inst, database); route_notifications(inst, ua); uint8_t before[32], after[32]; snapshot(reader, before); ready = 0; target = "COMMIT /* chat init */"; fault = F_EXEC; fail_rollback = 1; inst->running = 1; assert(chat_core_init(inst, database) < 0 && fault == F_NONE && !fail_rollback && !inst->running && !inst->chat_core);