|
|
|
|
@ -272,6 +272,67 @@ static void test_pull_lifecycle(void) {
|
|
|
|
|
puts("PASS: pull commits once; restart and disconnect abort exactly their own context"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Сессии и корни двух моделей одного канала не влияют друг на друга. */ |
|
|
|
|
static void test_dataset_isolation(void) { |
|
|
|
|
struct fixture f; t_init(&f); |
|
|
|
|
assert(merkle_sync_register(&f.inst, MT_DATA_FEEDBACK, &t_ops, &f) == 0); |
|
|
|
|
assert(merkle_sync_register(&f.inst, MT_DATA_FEEDBACK, &t_ops, &f) < 0); |
|
|
|
|
assert(merkle_sync_recompute_path(&f.inst, MT_DATA_MEMBERS, "1001", 0) == 1); |
|
|
|
|
uint8_t member[32], feedback[32], empty[32] = {0}; |
|
|
|
|
assert(merkle_sync_read_hash(&f.inst, MT_DATA_MEMBERS, "1001", 0, 0, member) == 0); |
|
|
|
|
assert(merkle_sync_read_hash(&f.inst, MT_DATA_FEEDBACK, "1001", 0, 0, feedback) == 0); |
|
|
|
|
assert(memcmp(member, empty, 32) && !memcmp(feedback, empty, 32)); |
|
|
|
|
assert(merkle_sync_recompute_path(&f.inst, MT_DATA_FEEDBACK, "1001", 0) == 1); |
|
|
|
|
struct ms_session* a = ms_create(f.inst.msync, &f.conn, MT_DATA_MEMBERS, 1001); assert(a); |
|
|
|
|
struct ms_session* b = ms_create(f.inst.msync, &f.conn, MT_DATA_FEEDBACK, 1001); assert(b); |
|
|
|
|
assert(a != b && ms_find(f.inst.msync, &f.conn, MT_DATA_MEMBERS, 1001) == a); |
|
|
|
|
a->state = b->state = MS_SERVE; |
|
|
|
|
merkle_sync_changed(&f.inst, MT_DATA_FEEDBACK, "1001"); |
|
|
|
|
assert(!a->dirty && !a->revision && b->dirty && b->revision == 1); |
|
|
|
|
merkle_sync_cancel(&f.inst, MT_DATA_FEEDBACK, 2, "1001"); |
|
|
|
|
assert(f.inst.msync->sessions == a && !a->next); |
|
|
|
|
merkle_sync_cancel_ns(&f.inst, "1001"); assert(!f.inst.msync->sessions); |
|
|
|
|
t_destroy(&f); |
|
|
|
|
puts("PASS: dataset roots, sessions, changes and cancellation are independent"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Два разных полных ключа с одинаковыми первыми 64 битами проходят пагинацию. */ |
|
|
|
|
static int t_full_page(void* ctx, const char* ns, uint64_t prefix, int av, const merkle_key_t* after, |
|
|
|
|
uint8_t* data, size_t* len, merkle_key_t* next, int* more) { |
|
|
|
|
(void)ctx; (void)ns; (void)prefix; |
|
|
|
|
assert(*len >= 1); |
|
|
|
|
*next = *after; ms_write64(next->bytes, 1); |
|
|
|
|
next->bytes[MT_KEY_SIZE - 1] = av ? 2 : 1; |
|
|
|
|
data[0] = next->bytes[MT_KEY_SIZE - 1]; *len = 1; *more = !av; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_full_cursor(void) { |
|
|
|
|
struct fixture f; t_init(&f); |
|
|
|
|
struct merkle_sync_data_ops ops = t_ops; ops.get_page = t_full_page; |
|
|
|
|
assert(merkle_sync_register(&f.inst, MT_DATA_FEEDBACK, &ops, &f) == 0); |
|
|
|
|
struct ms_session* s = ms_create(f.inst.msync, &f.conn, MT_DATA_FEEDBACK, 1001); assert(s); |
|
|
|
|
s->state = MS_SERVE; |
|
|
|
|
uint8_t query[MS_QUERY_SIZE] = { MT_MAX_LEVEL }; |
|
|
|
|
assert(ms_serve_query(s, 1, query, sizeof(query)) == 0); |
|
|
|
|
assert(s->tx->dgram[22] == MT_DATA_FEEDBACK && s->tx->dgram[MS_HEADER] == 1); |
|
|
|
|
memcpy(query + 10, s->tx->dgram + MS_HEADER + 1, MT_KEY_SIZE); query[9] = 1; |
|
|
|
|
ms_discard_tx(s); |
|
|
|
|
assert(ms_serve_query(s, 2, query, sizeof(query)) == 0); |
|
|
|
|
assert(s->tx->dgram[MS_HEADER] == 0 && s->tx->dgram[MS_HEADER + MT_KEY_SIZE] == 2); |
|
|
|
|
uint8_t page[MS_PAGE_HEADER + 1]; memcpy(page, s->tx->dgram + MS_HEADER, sizeof(page)); |
|
|
|
|
ms_discard_tx(s); |
|
|
|
|
s->state = MS_PULL; s->stack[0].level = MT_MAX_LEVEL; s->waiting = 1; s->request_id = 1; |
|
|
|
|
memcpy(s->after.bytes, query + 10, MT_KEY_SIZE); s->after_valid = 1; |
|
|
|
|
page[0] = 1; |
|
|
|
|
assert(ms_receive_page(s, 1, page, sizeof(page)) == 0); |
|
|
|
|
assert(s->after.bytes[MT_KEY_SIZE - 1] == 2 && s->request_id == 2); |
|
|
|
|
ms_discard_tx(s); |
|
|
|
|
assert(ms_receive_page(s, 2, page, sizeof(page)) == MT_ERR_PROTOCOL); |
|
|
|
|
t_destroy(&f); |
|
|
|
|
puts("PASS: full cursors preserve colliding 64-bit prefixes and reject backwards progress"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int main(void) { |
|
|
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); |
|
|
|
|
size_t baseline = u_get_allocated_count(); |
|
|
|
|
@ -279,6 +340,7 @@ int main(void) {
|
|
|
|
|
test_page_size_and_cursor(); test_readonly_tree(); test_backpressure_and_disconnect(); test_peer_restart(); |
|
|
|
|
test_send_rejection(); |
|
|
|
|
test_error_handoff(); test_blocked_error_timeout(); test_pull_lifecycle(); |
|
|
|
|
test_dataset_isolation(); test_full_cursor(); |
|
|
|
|
assert(u_get_allocated_count() == baseline); |
|
|
|
|
puts("ALL PASS: protocol regressions, no tracked allocations leaked"); |
|
|
|
|
return 0; |
|
|
|
|
|