/* Реальный адаптер, SQLite и транзакции; без копии алгоритма и без сетевых таймингов. */ #include "../src/chat/member_sync.c" #include static void setup(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { memset(inst, 0, sizeof(*inst)); inst->ua = ua; inst->node_id = 1; assert(sqlite3_open(":memory:", &inst->topo_sqlite_db) == SQLITE_OK); assert(topo_node_sqlite_init(inst->topo_sqlite_db) == 0); uint8_t key[32] = {1}, signature[64] = {0}; assert(topo_node_sqlite_channel_put(inst->topo_sqlite_db, "1001", "test", 1, key, NULL, key, NULL, signature) == 0); assert(member_sync_init(inst) == 0); } static void put(struct UTUN_INSTANCE* inst, uint64_t id) { uint8_t key[32] = {1}; assert(member_sync_put(inst, "1001", id, key, key, NULL, 0, NULL, 0, "{}", NULL, NULL, 0, 0, NULL) >= 0); } static void root(struct UTUN_INSTANCE* inst, uint8_t hash[32]) { assert(merkle_sync_read_hash(inst, "1001", 0, 0, hash) == 0); } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); size_t baseline = u_get_allocated_count(); struct UASYNC* ua = uasync_create(); assert(ua); struct UTUN_INSTANCE source, target; setup(&source, ua); setup(&target, ua); put(&source, 1); put(&source, 2); uint8_t raw[2049], *page = raw + 1; /* проверяем невыровненный буфер */ size_t len = 300; uint64_t next; int more; assert(_member_get_page(&source, "1001", 0, 0, 0, page, &len, &next, &more) == 0); assert(page[0] == 0 && page[1] == 1 && next == 1 && more == 1 && len <= 300); struct member_wire w; size_t used; assert(member_decode(page + 2, len - 2, &w, &used) == 0 && w.rec.node_id == 1 && used == len - 2); len = 300; assert(_member_get_page(&source, "1001", 0, 1, next, page, &len, &next, &more) == 0 && next == 2 && !more); puts("PASS: bounded member pages, canonical integers and unaligned buffers"); len = 2048; assert(_member_get_page(&source, "1001", 0, 0, 0, page, &len, &next, &more) == 0 && page[1] == 2 && !more); assert(_member_apply_items(&target, "1001", 1, page, len - 1) < 0 && member_sync_count(&target, "1001") == 0); sqlite3* db = target.topo_sqlite_db; assert(sqlite3_exec(db, "CREATE TRIGGER fail_second BEFORE INSERT ON peers_1001 WHEN NEW.node_id=2 " "BEGIN SELECT RAISE(ABORT,'injected second record failure'); END", NULL, NULL, NULL) == SQLITE_OK); assert(_member_apply_items(&target, "1001", 1, page, len) < 0); uint8_t hash[32], empty[32] = {0}; root(&target, hash); assert(member_sync_count(&target, "1001") == 0 && !memcmp(hash, empty, 32) && sqlite3_get_autocommit(db)); assert(sqlite3_exec(db, "DROP TRIGGER fail_second", NULL, NULL, NULL) == SQLITE_OK); assert(_member_apply_items(&target, "1001", 1, page, len) == 0 && member_sync_count(&target, "1001") == 2); uint8_t expected[32]; root(&source, expected); root(&target, hash); assert(!memcmp(hash, expected, 32)); puts("PASS: malformed page rejected; second-record error rolls back data and tree; complete page converges"); assert(sqlite3_exec(db, "UPDATE peers_1001 SET x25519_pubkey=X'01' WHERE node_id=1", NULL, NULL, NULL) == SQLITE_OK); assert(merkle_sync_recompute_path(&target, "1001", 1) < 0); root(&target, hash); assert(!memcmp(hash, expected, 32)); len = 2048; assert(_member_get_page(&target, "1001", 0, 0, 0, page, &len, &next, &more) < 0); puts("PASS: corrupt BLOB cannot be hashed or serialized"); member_sync_destroy(&target); member_sync_destroy(&source); assert(sqlite3_close(target.topo_sqlite_db) == SQLITE_OK && sqlite3_close(source.topo_sqlite_db) == SQLITE_OK); uasync_poll(ua, 0); uasync_destroy(ua, 0); assert(u_get_allocated_count() == baseline); puts("ALL PASS: member adapter"); return 0; }