diff --git a/tests/test_bgp_paths.c b/tests/test_bgp_paths.c index bd1d5f20..6a320ee6 100644 --- a/tests/test_bgp_paths.c +++ b/tests/test_bgp_paths.c @@ -10,6 +10,7 @@ #include "etcp_api.h" #include "etcp_router.h" #include "route_crypto.h" +#include "route_lib.h" #include "node_conn_direct.h" #include "../lib/debug_config.h" #include "test_utils.h" @@ -183,6 +184,30 @@ static void metric_jitter(void) { assert(delivered == before); /* RTT is telemetry, not routing state. */ } +/* Новый набор подсетей обязан заменять старый на всех узлах, включая пустой набор. */ +static void subnet_replacement(void) { + struct CFG_ROUTE_ENTRY subnet = {.ip.family = AF_INET, .netmask = 24}; + for (int phase = 0; phase < 3; phase++) { + subnet.ip.addr.v4.s_addr = htonl(phase == 0 ? 0x0a630000 : 0x0a640000); + nodes[0]->config->my_subnets = phase < 2 ? &subnet : NULL; + assert(topo_group_update_my_nodeinfo(nodes[0], groups[0]) == (phase < 2)); + nodes[0]->config->my_subnets = NULL; + for (struct ll_entry* e = groups[0]->senders_list->head; e; e = e->next) { + struct TOPO_GROUP_CONN_ITEM* p = (struct TOPO_GROUP_CONN_ITEM*)e->data; + assert(topo_group_send_nodeinfo(groups[0], groups[0]->local_node, p->conn) == 0); + } + settle(); verify(); + for (int i = 0; i < N; i++) { + struct ROUTE_ENTRY* old = route_lookup(nodes[i]->rt, 0x0a630001); + struct ROUTE_ENTRY* current = route_lookup(nodes[i]->rt, 0x0a640001); + assert((old != NULL) == (phase == 0) && (current != NULL) == (phase == 1)); + if (old) assert(old->v_node_info->node_id == nodes[0]->node_id); + if (current) assert(current->v_node_info->node_id == nodes[0]->node_id); + } + } + DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP IPv4 subnet replacement and removal passed on all four nodes"); +} + static void service_receive(struct ETCP_CONN* conn, struct ll_entry* e) { if (e->len > ROUTER_SVC_HDR_SIZE) { assert(conn->instance == nodes[3]); @@ -336,6 +361,7 @@ int main(void) { create(); unsigned triangle = edge(0, 1) | edge(1, 2) | edge(0, 2) | edge(2, 3); change(triangle); settle(); verify(); + subnet_replacement(); metric_jitter(); /* One-sided REINIT with routes retained, then simultaneous REINIT at all peers. */ etcp_fire_conn_status(conns[0][1], ETCP_CONN_STATUS_REINIT); settle(); verify(); diff --git a/tests/test_topo_node_wire.c b/tests/test_topo_node_wire.c index a3663684..f110e528 100644 --- a/tests/test_topo_node_wire.c +++ b/tests/test_topo_node_wire.c @@ -27,9 +27,12 @@ struct wire_fixture { #ifdef __linux__ static unsigned fail_at, attempts, inside_pool; static int track_allocations; +static int lifecycle_faults; static int allocation_fails(const char *where) { - if (!track_allocations || (!strstr(where, "topo_node.c:") && !strstr(where, "topo_node_sqlite.c:"))) return 0; + if (!track_allocations || (!strstr(where, "topo_node.c:") && !strstr(where, "topo_node_sqlite.c:") && + !(lifecycle_faults && (strstr(where, "topo_group.c:") || strstr(where, "conn_mgr_core.c:") || + strstr(where, "broadcast.c:") || strstr(where, "radio.c:"))))) return 0; attempts++; if (attempts != fail_at) return 0; DEBUG_WARN(DEBUG_CATEGORY_BGP, "[fault] NODEINFO allocation %u rejected at %s", attempts, where); @@ -338,6 +341,39 @@ static void test_local_allocation_failures(struct wire_fixture *f) { assert(route_lookup(f->inst.rt, 0x0a020001)->v_node_info == old && !route_lookup(f->inst.rt, 0x0a030001)); } fail_at = 0; + /* Ошибка публикации radio не должна делать следующую такую же команду no-op. */ + struct TOPO_GROUP chat = {.instance = &f->inst, .group_id = 44, .group_type = TOPO_GROUP_TYPE_CHAT}; + assert(topo_group_update_my_nodeinfo(&f->inst, &chat) == 0); + struct TOPO_GROUP_NODE* radio_before = chat.local_node; + attempts = 0; fail_at = 1; track_allocations = 1; + topo_group_set_radio(&chat, 1); + track_allocations = 0; fail_at = 0; + assert(!chat.radio_active && chat.local_node == radio_before && !chat.local_node->radio); + topo_group_set_radio(&chat, 1); + assert(chat.radio_active && chat.local_node->radio); + topo_nodeq_free_group_fields(&f->groups, chat.local_node); u_free(chat.local_node); + + /* Ни local_node, ни один из обязательных контекстов не может отсутствовать у возвращённой группы. */ + f->groups.group_list = queue_new(f->inst.ua, 16, 0, 8, "creation_faults"); assert(f->groups.group_list); + attempts = 0; lifecycle_faults = 1; track_allocations = 1; + struct TOPO_GROUP* created = topo_groups_create_group(&f->groups, 43, TOPO_GROUP_TYPE_UTUN, NULL); + track_allocations = 0; + assert(created && created->local_node && created->conn_mgr && created->broadcast && created->radio); + unsigned creation_allocations = attempts; + topo_groups_remove_group(&f->groups, 43); uasync_poll(f->inst.ua, 0); + for (unsigned nth = 1; nth <= creation_allocations; nth++) { + size_t before = live_allocations(), routes = f->inst.rt->count; + uint32_t before_refs = node->group_ref_count; + attempts = 0; fail_at = nth; track_allocations = 1; + created = topo_groups_create_group(&f->groups, 43, TOPO_GROUP_TYPE_UTUN, NULL); + track_allocations = 0; uasync_poll(f->inst.ua, 0); + assert(!created && !topo_groups_find(&f->groups, 43)); + assert(node->group_ref_count == before_refs && f->inst.rt->count == routes && live_allocations() == before); + } + lifecycle_faults = 0; fail_at = 0; + queue_free(f->groups.group_list); f->groups.group_list = NULL; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "[PASS] group creation: %u allocation failures roll back all resources; radio retry preserved", + creation_allocations); topo_nodeq_free_group_fields(&f->groups, old); u_free(old); f->group.local_node = NULL; topo_node_registry_unref(&f->groups, f->inst.node_id); assert(!f->groups.node_registry->head); queue_free(f->groups.node_registry); f->groups.node_registry = NULL;