Browse Source

tests: discover live endpoints in forked JOIN and network polls

master
evgeny 3 days ago
parent
commit
7ae2f74643
  1. 59
      tests/test_chat_join_e2e.c
  2. 15
      tests/test_chat_poll_network.c

59
tests/test_chat_join_e2e.c

@ -35,6 +35,8 @@
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "../lib/platform_compat.h"
#include "test_net.h"
#include <errno.h>
#include <sqlite3.h>
#define OPENSSL_API_COMPAT 0x10100000L
@ -72,7 +74,6 @@ struct e2e_shared {
uint64_t owner;
uint64_t nid[3];
uint8_t x_pub[3][32], x_priv[3][32], ed_pub[3][32];
int port[3];
int degraded;
};
@ -149,7 +150,7 @@ static int wf(const char* p, const char* f, ...) {
/* ── генерация данных сценария + конфигов (родитель) ── */
static void fill_shared(int base_port, int degraded) {
static void fill_shared(int degraded) {
memset(&g_sh, 0, sizeof(g_sh));
g_sh.degraded = degraded;
snprintf(g_sh.ch_id, sizeof(g_sh.ch_id), "4242424242424242");
@ -159,7 +160,6 @@ static void fill_shared(int base_port, int degraded) {
uint8_t ed_priv[32];
x25519_to_ed25519(g_sh.x_priv[i], ed_priv, g_sh.ed_pub[i]);
g_sh.nid[i] = sc_derive_node_id_from_pubkey(g_sh.x_pub[i]);
g_sh.port[i] = base_port + i * 1000;
}
gen_x25519(g_sh.ch_x_pub, g_sh.ch_x_priv);
@ -189,15 +189,51 @@ static void write_configs(const char* dir) {
wf(path,
"[global]\ntun_ip=10.98.%d.1/24\ntun_ifname=tun%d0\ndb_path=%s/db%s\n"
"my_private_key=%s\nmy_public_key=%s\n"
"[server: s1]\naddr=127.0.0.1:%d\ntype=public\n"
"[server: s1]\naddr=127.0.0.1:0\ntype=public\n"
"[chatserver]\nstorage_autoload=0\n[allowed_keys]\nallow_all=1\n",
i, i, dir, i == IDX_A ? "a" : i == IDX_C ? "c" : "j",
priv, pub, g_sh.port[i]);
priv, pub);
}
}
/* ── poll-хелперы (дети) ── */
// Родитель не выбирает порты заранее: A публикует endpoint своего уже открытого слушателя.
static int publish_bootstrap_port(struct UTUN_INSTANCE* inst, const char* dir) {
char path[768], temporary[768];
snprintf(path, sizeof(path), "%s/a.port", dir); snprintf(temporary, sizeof(temporary), "%s/a.port.tmp", dir);
uint16_t port = ss_get_port(&test_etcp_bound_socket(inst, "s1")->local_addr);
FILE* f = fopen(temporary, "w");
if (!f) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: endpoint file open failed: %s", strerror(errno)); return 0; }
int ok = fprintf(f, "%u\n", port) > 0;
if (fclose(f)) ok = 0;
if (!ok || rename(temporary, path)) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: endpoint publication failed: %s", strerror(errno)); return 0;
}
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "JOIN fixture: bootstrap listener port=%u published", port);
return 1;
}
static uint16_t wait_bootstrap_port(struct UTUN_INSTANCE* inst, const char* dir) {
char path[768]; snprintf(path, sizeof(path), "%s/a.port", dir);
uint64_t deadline = get_time_tb() + 50000;
while (get_time_tb() < deadline) {
FILE* f = fopen(path, "r");
if (f) {
unsigned port = 0; int count = fscanf(f, "%u", &port), error = fclose(f);
if (count != 1 || !port || port > UINT16_MAX || error) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: invalid bootstrap endpoint count=%d port=%u error=%d", count, port, error);
return 0;
}
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "JOIN fixture: bootstrap listener port=%u received", port);
return (uint16_t)port;
}
if (errno != ENOENT) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: endpoint read failed: %s", strerror(errno)); return 0; }
poll_checked(inst);
}
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: bootstrap endpoint publication timeout"); return 0;
}
static int wait_conn(struct UTUN_INSTANCE* inst, uint64_t nid, int max_iter) {
for (int a = 0; a < max_iter; a++) {
struct ETCP_CONN* c = instance_find_conn(inst, nid);
@ -375,9 +411,11 @@ static int check_gui_superseded(struct UTUN_INSTANCE* inst) {
/* Реальный JSON control socket: два параллельных invite, затем отмена при quit. */
static int check_headless_invites(struct UTUN_INSTANCE* inst) {
int port = g_sh.port[IDX_A] + 2100, ok = 0;
int ok = 0;
socket_t fd = SOCKET_INVALID;
if (chat_headless_control_init(inst->ua, inst, "127.0.0.1", port) != 0) return 0;
if (chat_headless_control_init(inst->ua, inst, "127.0.0.1", 0) != 0) return 0;
uint16_t port = chat_headless_control_get_port(inst);
if (!port) return 0;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == SOCKET_INVALID) goto out;
struct sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons((uint16_t)port);
@ -488,6 +526,7 @@ static int child_main(const char* role, const char* dir, int invalid_key) {
utun_instance_destroy(inst); uasync_destroy(ua, 0); return 1;
}
chat_event_set_handler(inst, join_event_handler);
if (!strcmp(role, "a") && !publish_bootstrap_port(inst, dir)) return 1;
if (strcmp(role, "c") == 0) {
scenario_dir = dir;
connection_join_recv = inst->router_bindings.callbacks[ETCP_RT_ID_JOIN];
@ -497,7 +536,9 @@ static int child_main(const char* role, const char* dir, int invalid_key) {
/* Fixture bootstrap between the existing members uses core NCD, without UTUN. */
struct NODE_CONN_DIRECT* bootstrap = NULL;
if (!strcmp(role, "c") && !g_sh.degraded) {
struct TOPO_ADDR4 address = { .addr = {127, 0, 0, 1}, .port = g_sh.port[IDX_A], .protocol = TOPO_PROTO_UDP };
uint16_t port = wait_bootstrap_port(inst, dir);
if (!port) return 1;
struct TOPO_ADDR4 address = { .addr = {127, 0, 0, 1}, .port = port, .protocol = TOPO_PROTO_UDP };
struct TOPO_NODE node = { .node_id = g_sh.nid[IDX_A], .v4_addrs = &address };
memcpy(node.public_key, g_sh.x_pub[IDX_A], 32);
if (node_conn_direct_open_node(inst, node.node_id, NULL, NULL, &bootstrap, &node, NULL) == NCD_ERR) return 1;
@ -649,7 +690,7 @@ static int run_scenario(const char* name, int degraded, int invalid_key) {
char dir[512]; snprintf(dir, sizeof(dir), "/tmp/utun_e2e_%s_XXXXXX", name);
(void)!mkdtemp(dir);
fill_shared(56000 + (getpid() % 2000), degraded);
fill_shared(degraded);
write_configs(dir);
const char* roles[4];

15
tests/test_chat_poll_network.c

@ -14,6 +14,7 @@
#include "../lib/platform_compat.h"
#include "../lib/socket_compat.h"
#include "../lib/debug_config.h"
#include "test_net.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
@ -24,15 +25,6 @@ static struct UTUN_INSTANCE* nodes[3];
static char channel[21], message_hex[65];
static uint8_t message_id[32];
/* Порт выбирается ОС, затем сразу используется новым ядром. */
static unsigned free_port(void) {
socket_t fd = socket(AF_INET, SOCK_DGRAM, 0); assert(fd != SOCKET_INVALID);
struct sockaddr_in address = {0}; address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
assert(bind(fd, (struct sockaddr*)&address, sizeof(address)) == 0); socklen_t size = sizeof(address);
assert(getsockname(fd, (struct sockaddr*)&address, &size) == 0); unsigned port = ntohs(address.sin_port);
socket_close_wrapper(fd); return port;
}
static void hex(const uint8_t* data, size_t len, char* out) {
for (size_t i = 0; i < len; i++) snprintf(out + i * 2, 3, "%02x", data[i]);
}
@ -134,7 +126,10 @@ int main(void) {
debug_set_category_level(DEBUG_CATEGORY_CHAT_SYNC, DEBUG_LEVEL_DEBUG);
debug_set_category_level(DEBUG_CATEGORY_MEMBER_SYNC, DEBUG_LEVEL_INFO);
ua = uasync_create(); assert(ua); unsigned ports[3];
for (int i = 0; i < 3; i++) { ports[i] = free_port(); nodes[i] = create_node(ports[i]); }
for (int i = 0; i < 3; i++) {
nodes[i] = create_node(0);
ports[i] = ss_get_port(&test_etcp_bound_socket(nodes[i], "udp")->local_addr);
}
chat_core_create_channel_auto(nodes[0], "Poll network");
sqlite3_stmt* st = NULL;
assert(sqlite3_prepare_v2(nodes[0]->topo_sqlite_db, "SELECT channel_id FROM channels", -1, &st, NULL) == SQLITE_OK);

Loading…
Cancel
Save