93 lines
5.6 KiB
C
93 lines
5.6 KiB
C
/* Реальные core/SQLite/локальная регистрация invite;проверяем отказы каждого SQL-этапа чтения. */
|
|
#define _GNU_SOURCE
|
|
#include "../src/chat/invite_build.c"
|
|
#include <assert.h>
|
|
|
|
enum fault { NONE, PREPARE, BIND, STEP, FINALIZE };
|
|
static enum fault fault;
|
|
static const char* target;
|
|
static unsigned skip_steps, calls;
|
|
static int last_result;
|
|
static uint64_t expected_channel;
|
|
static struct UTUN_INSTANCE* active;
|
|
static int matches(sqlite3_stmt* st) { return st && target && !strncmp(sqlite3_sql(st), target, strlen(target)); }
|
|
extern int __real_sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
|
|
int __wrap_sqlite3_prepare_v2(sqlite3* db, const char* sql, int bytes, sqlite3_stmt** st, const char** tail) {
|
|
if (fault == PREPARE && target && !strncmp(sql, target, strlen(target))) { fault = NONE; *st = NULL; return SQLITE_IOERR; }
|
|
return __real_sqlite3_prepare_v2(db, sql, bytes, st, tail);
|
|
}
|
|
extern int __real_sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
|
|
int __wrap_sqlite3_bind_int64(sqlite3_stmt* st, int index, sqlite3_int64 value) {
|
|
if (fault == BIND && matches(st)) { fault = NONE; return SQLITE_IOERR; }
|
|
return __real_sqlite3_bind_int64(st, index, value);
|
|
}
|
|
extern int __real_sqlite3_step(sqlite3_stmt*);
|
|
int __wrap_sqlite3_step(sqlite3_stmt* st) {
|
|
if (fault == STEP && matches(st)) {
|
|
if (skip_steps) skip_steps--; else { fault = NONE; return SQLITE_IOERR; }
|
|
}
|
|
return __real_sqlite3_step(st);
|
|
}
|
|
extern int __real_sqlite3_finalize(sqlite3_stmt*);
|
|
int __wrap_sqlite3_finalize(sqlite3_stmt* st) {
|
|
int fail = fault == FINALIZE && matches(st); if (fail) fault = NONE;
|
|
int rc = __real_sqlite3_finalize(st); return fail ? SQLITE_IOERR : rc;
|
|
}
|
|
static void ready(void* arg, int result, const char* link) {
|
|
(void)arg; calls++; last_result = result;
|
|
if (result == CHAT_JOIN_OK) {
|
|
struct InviteData decoded; char error[128];
|
|
assert(link && !invite_link_decode(link, strlen(link), &decoded, error, sizeof(error)));
|
|
assert(decoded.channelId == expected_channel && decoded.nodeId == active->node_id && !strcmp(decoded.password, "p"));
|
|
assert(chat_join_lookup_inviter(active, expected_channel, decoded.join_key) == active->node_id);
|
|
} else assert(!link);
|
|
}
|
|
static int read_query(unsigned query, struct UTUN_INSTANCE* inst, uint64_t channel) {
|
|
uint64_t ids[4] = {77,88,99,111}, before[4]; memcpy(before, ids, sizeof(ids));
|
|
uint8_t key[32], old[32]; memset(key, 0xa5, sizeof(key)); memcpy(old, key, sizeof(key));
|
|
struct InviteAddr addresses[INVITE_ADDR_MAX]; struct InviteReality reality;
|
|
int rc;
|
|
if (!query) {
|
|
rc = chat_invite_candidate_nodes(inst, channel, ids, 4);
|
|
if (rc < 0) assert(!memcmp(ids, before, sizeof(ids)));
|
|
} else if (query == 1) rc = node_has_both_public(inst, inst->node_id);
|
|
else if (query == 2) {
|
|
rc = get_node_pubkey(inst, inst->node_id, 0, key); if (rc < 0) assert(!memcmp(old, key, sizeof(key)));
|
|
} else rc = collect_node_addrs_from_db(inst, inst->node_id, addresses, INVITE_ADDR_MAX, 0, &reality);
|
|
return rc;
|
|
}
|
|
int main(void) {
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_CHAT_SYNC, DEBUG_LEVEL_DEBUG);
|
|
size_t baseline = u_get_allocated_count(); utun_instance_set_tun_init_enabled(0);
|
|
struct UASYNC* ua = uasync_create(); assert(ua);
|
|
active = utun_instance_create_from_str(ua, "[global]\nmy_node_name=invite test\n[server: udp]\naddr=127.0.0.1:0\n"
|
|
"type=public\n[ntp]\nenabled=no\n[chatserver]\ngroup_autoconnect=0\nstorage_autoload=0\n");
|
|
assert(active && !utun_core_start(active) && !chat_service_start(active));
|
|
expected_channel = chat_core_create_channel_auto(active, "invite test"); assert(expected_channel);
|
|
const char* queries[] = {"SELECT node_id,node_type", "SELECT family FROM node_addresses", "SELECT x25519_pubkey", "SELECT family,socket_id"};
|
|
unsigned cases = 0;
|
|
for (unsigned q = 0; q < 4; q++) for (enum fault point = PREPARE; point <= FINALIZE; point++) {
|
|
if (!q && point == BIND) continue;
|
|
target = queries[q]; fault = point; skip_steps = !q && point == STEP ? 1 : 0;
|
|
assert(read_query(q, active, expected_channel) < 0 && fault == NONE);
|
|
assert(read_query(q, active, expected_channel) >= 0); cases++;
|
|
}
|
|
target = queries[0]; fault = FINALIZE; uint64_t selected = 77;
|
|
assert(chat_invite_best_node(active, expected_channel, &selected) < 0 && fault == NONE && selected == 77);
|
|
assert(!chat_invite_best_node(active, expected_channel, &selected) && selected == active->node_id);
|
|
target = NULL;
|
|
struct chat_invite_build* pending = chat_invite_build_link(active, expected_channel, 0, "p", ready, NULL);
|
|
assert(pending && !calls);
|
|
for (unsigned i = 0; i < 100 && !calls; i++) uasync_poll(ua, 1);
|
|
assert(calls == 1 && last_result == CHAT_JOIN_OK);
|
|
pending = chat_invite_build_link(active, expected_channel, active->node_id, "p", ready, NULL); assert(pending);
|
|
chat_invite_build_cancel(pending); assert(calls == 2 && last_result == CHAT_JOIN_CANCELLED);
|
|
assert(!chat_invite_build_link(active, expected_channel, 42, NULL, ready, NULL));
|
|
selected = 77; assert(chat_invite_best_node(NULL, expected_channel, &selected) < 0 && selected == 77);
|
|
assert(chat_invite_best_node(active, 0, &selected) < 0 && selected == 77);
|
|
utun_instance_destroy(active); active = NULL; uasync_poll(ua, 0); uasync_destroy(ua, 0);
|
|
assert(u_get_allocated_count() == baseline);
|
|
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS invite:%u SQL fault/retries,atomic outputs,shared address projection,self ACK/cancel,invalid target", cases);
|
|
return 0;
|
|
}
|