diff --git a/tests/test_node_conn_direct.c b/tests/test_node_conn_direct.c index a5a903a8..8b624b3c 100644 --- a/tests/test_node_conn_direct.c +++ b/tests/test_node_conn_direct.c @@ -360,6 +360,31 @@ static int setup(void) { config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb); return 0; } + +/* Предварительный отбор не создаёт транспорт; reverse допускается при недоступном исходящем адресе. */ +static void test_compatibility(void) { + struct TOPO_NODE* peer = topo_node_registry_find(g_a->topo_groups, nid_b); + struct TOPO_NODE* self = topo_node_registry_find(g_a->topo_groups, nid_a); + if (!self) { + if (inject_node(g_a, nid_a, pubkey_a, pa) != 0) { fail("inject self compatibility snapshot"); return; } + self = topo_node_registry_find(g_a->topo_groups, nid_a); + } + struct ETCP_SOCKET* socket = test_etcp_bound_socket(g_a, "s1"); + if (!peer || !self || !socket || !peer->v4_sock_meta || !self->v4_sock_meta) { fail("compatibility fixture"); return; } + if (!node_conn_direct_compatible(g_a, nid_b) || instance_find_conn(g_a, nid_b)) { fail("direct compatibility mutated transport"); return; } + struct sockaddr_storage saved_interface = socket->interface_addr; + struct sockaddr_in* interface = (struct sockaddr_in*)&socket->interface_addr; + interface->sin_addr.s_addr = htonl(UINT32_C(0x08080808)); + uint8_t saved_cfg = peer->v4_sock_meta->config_type, saved_self = self->v4_sock_meta->config_type; + peer->v4_sock_meta->config_type = CFG_SERVER_TYPE_LOCAL; + int reverse = node_conn_direct_compatible(g_a, nid_b); + self->v4_sock_meta->config_type = CFG_SERVER_TYPE_LOCAL; + int incompatible = node_conn_direct_compatible(g_a, nid_b); + peer->v4_sock_meta->config_type = saved_cfg; self->v4_sock_meta->config_type = saved_self; + socket->interface_addr = saved_interface; + if (!reverse || incompatible || instance_find_conn(g_a, nid_b)) { fail("reverse/local compatibility or read-only contract"); return; } + fprintf(stderr, "[PASS] compatibility: direct, reverse-only, incompatible local and no transport mutation\n"); +} static void cleanup(void) { test_unlink(ca); test_unlink(cb); test_rmdir(tdir); } int main(void) { @@ -377,6 +402,7 @@ int main(void) { (unsigned long long)nid_a, pa, (unsigned long long)nid_b, pb); fflush(stderr); utun_instance_init(g_a); utun_instance_init(g_b); if (inject_node(g_a, nid_b, pubkey_b, pb) != 0) { fail("inject_node"); goto done; } + test_compatibility(); if (g_result == 2) goto done; if (inject_node(g_b, nid_a, pubkey_a, pa) != 0) { fail("inject_node A→B"); goto done; } fprintf(stderr, "injected B→A and A→B registries\n"); fflush(stderr); uasync_call_soon(ua, NULL, t1);