test(chat): cover startup admission, saved channel activation, and owner-close recovery

This commit is contained in:
evgeny committed 2026-10-27 11:48:09 +01:00
1 parent 1c6d203754
commit 8151fc5a9c
1 file changed
+59 -4
+59 -4
View File
@@ -152,6 +152,57 @@ static void sql_faults(struct UTUN_INSTANCE* inst, const char* path) {
printf("PASS lifecycle:%u SQL faults/retries,commit veto,context/properties/routes allocation;exact DB,callbacks,and ready preserved\n", cases);
}
static sqlite3_int64 number(sqlite3* db, const char* sql) {
sqlite3_stmt* st = NULL; assert(sqlite3_prepare_v2(db, sql, -1, &st, NULL) == SQLITE_OK && sqlite3_step(st) == SQLITE_ROW);
sqlite3_int64 result = sqlite3_column_int64(st, 0); assert(sqlite3_finalize(st) == SQLITE_OK); return result;
}
static void validation(struct UTUN_INSTANCE* inst, const char* path) {
uint8_t before[32], after[32]; snapshot(reader, before); ready = 0;
assert(sqlite3_exec(inst->topo_sqlite_db, "BEGIN;CREATE TABLE lifecycle_outer(value);INSERT INTO lifecycle_outer VALUES(7)", NULL, NULL, NULL) == SQLITE_OK);
assert(chat_core_init(inst, path) < 0 && !inst->chat_core && !sqlite3_get_autocommit(inst->topo_sqlite_db));
assert(number(inst->topo_sqlite_db, "SELECT value FROM lifecycle_outer") == 7);
assert(sqlite3_exec(inst->topo_sqlite_db, "ROLLBACK", NULL, NULL, NULL) == SQLITE_OK);
char long_path[513]; memset(long_path, 'x', sizeof(long_path)); long_path[512] = 0;
assert(chat_core_init(inst, long_path) < 0 && chat_core_init(NULL, path) < 0 && chat_core_init(inst, NULL) < 0);
chat_core_destroy(NULL); snapshot(reader, after); assert(!memcmp(before, after, 32) && !ready && !inst->chat_core);
assert(number(reader, "SELECT count(*) FROM accounts") == 0);
const char* modes[] = {"ABORT", "IGNORE"};
for (size_t i = 0; i < 2; i++) {
char sql[256]; snprintf(sql, sizeof(sql), "CREATE TRIGGER reject_identity BEFORE INSERT ON local_identity BEGIN SELECT RAISE(%s%s);END",
modes[i], i ? "" : ",'injected identity rejection'");
assert(sqlite3_exec(inst->topo_sqlite_db, sql, NULL, NULL, NULL) == SQLITE_OK); refused(inst, path);
assert(sqlite3_exec(inst->topo_sqlite_db, "DROP TRIGGER reject_identity", NULL, NULL, NULL) == SQLITE_OK); retry(inst, path);
}
puts("PASS lifecycle:foreign transaction preserved,NULL/oversized path rejected,ABORT/IGNORE rollback,no demonstration contacts");
}
static void activation(struct UTUN_INSTANCE* inst, const char* path) {
ready = 0; assert(!chat_service_start(inst) && ready == 2);
uint64_t groups[2] = {chat_core_create_channel_auto(inst, "first saved"), chat_core_create_channel_auto(inst, "second saved")};
assert(groups[0] && groups[1]);
struct chat_msg_submit request = {.inst = inst, .data = (uint8_t*)"startup retained", .data_len = 16};
snprintf(request.channel_id, sizeof(request.channel_id), "%llu", (unsigned long long)groups[0]); strcpy(request.content_type, "text/plain");
assert(!chat_core_submit_message(inst, &request)); chat_service_stop(inst);
char sql[256]; snprintf(sql, sizeof(sql), "SELECT rowid FROM channels WHERE channel_id='%llu'", (unsigned long long)groups[0]);
sqlite3_int64 row = number(reader, sql);
const char* invalid[] = {"'01'", "CAST(X'313200' AS TEXT)", "'9223372036854775808'"};
for (size_t i = 0; i < sizeof(invalid) / sizeof(invalid[0]); i++) {
snprintf(sql, sizeof(sql), "UPDATE channels SET channel_id=%s WHERE rowid=%lld", invalid[i], (long long)row);
assert(sqlite3_exec(inst->topo_sqlite_db, sql, NULL, NULL, NULL) == SQLITE_OK); refused(inst, path);
snprintf(sql, sizeof(sql), "UPDATE channels SET channel_id='%llu' WHERE rowid=%lld", (unsigned long long)groups[0], (long long)row);
assert(sqlite3_exec(inst->topo_sqlite_db, sql, NULL, NULL, NULL) == SQLITE_OK);
}
ready = 0; fail_sync = 2;
assert(chat_service_start(inst) < 0 && !fail_sync && !ready && !inst->chat_core && !inst->chat_started && inst->core_started);
assert(!topo_groups_find(inst->topo_groups, groups[0]) && !topo_groups_find(inst->topo_groups, groups[1]));
assert(inst->nodeinfo_cbks == preserved_callback && number(reader, "SELECT count(*) FROM channels") == 2);
snprintf(sql, sizeof(sql), "SELECT count(*) FROM msg_%llu", (unsigned long long)groups[0]); assert(number(reader, sql) == 1);
assert(!chat_service_start(inst) && ready == 2 && chat_core_count(inst, request.channel_id) == 1);
assert(topo_groups_find(inst->topo_groups, groups[0]) && topo_groups_find(inst->topo_groups, groups[1])); chat_service_stop(inst);
puts("PASS lifecycle:corrupt catalog rejected;second channel activation failure cleans all runtime,keeps data,and retries without false ready");
}
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);
@@ -164,10 +215,14 @@ 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);
ready = 0;
assert(!chat_service_start(inst) && ready == 2); chat_service_stop(inst);
retry(inst, database); sql_faults(inst, database); validation(inst, database); activation(inst, database);
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);
assert(!ready && !sqlite3_get_autocommit(inst->topo_sqlite_db) && inst->nodeinfo_cbks == preserved_callback);
snapshot(reader, after); assert(!memcmp(before, after, 32)); utun_instance_destroy(inst);
snapshot(reader, after); assert(!memcmp(before, after, 32));
assert(sqlite3_close(reader) == SQLITE_OK);
utun_instance_destroy(inst); uasync_poll(ua, 0); uasync_destroy(ua, 0); assert(u_get_allocated_count() == baseline);
uasync_poll(ua, 0); uasync_destroy(ua, 0); assert(u_get_allocated_count() == baseline);
puts("PASS lifecycle:subscription failure aborts startup without ready events;core survives and retry succeeds"); return 0;
}