Verify poll commit acknowledgments survive snapshot faults and reserve memory before changes

This commit is contained in:
evgeny committed 2026-10-27 03:49:38 +01:00
1 parent 9876677a5f
commit 739ee0802f
2 files changed
+32 -2

No files matched your search

+1 -1
View File
@@ -1321,7 +1321,7 @@ test_chat_poll_SOURCES = test_chat_poll.c
test_chat_poll_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/lib
test_chat_poll_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_chat_poll_LDFLAGS = -Wl,--wrap=sqlite3_prepare_v2 -Wl,--wrap=sqlite3_bind_blob -Wl,--wrap=sqlite3_bind_text \
-Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_finalize
-Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_finalize -Wl,--wrap=u_malloc_impl
test_chat_poll_network_SOURCES = test_chat_poll_network.c
test_chat_poll_network_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/lib
+31 -1
View File
@@ -11,6 +11,15 @@ static enum poll_fault fault;
static enum poll_query query;
static unsigned query_number, matching_queries, fault_bind, changed_events;
static sqlite3_stmt* fault_statement;
static int fail_ack_allocation;
extern void* __real_u_malloc_impl(uint32_t, const char*);
void* __wrap_u_malloc_impl(uint32_t size, const char* location) {
if (fail_ack_allocation && strstr(location, "chat_poll.c")) {
fail_ack_allocation = 0; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "poll test: committed ack allocation failed bytes=%u", size); return NULL;
}
return __real_u_malloc_impl(size, location);
}
static int query_matches(const char* sql) {
const char* prefix[] = {"SELECT json_valid(?1)", "SELECT json_type(?1,?2),json_extract(?1,?2)",
@@ -134,8 +143,29 @@ static void sql_faults(struct UTUN_INSTANCE* inst, const char* channel, const ch
char* result = command(inst, react, -1); u_free(result);
assert(chat_feedback_get(inst, channel, message, inst->node_id, CHAT_FEEDBACK_REACTION, &actual) == 1 && actual.ver == saved.ver);
assert(actual.data_len == saved.data_len && !memcmp(actual.data, saved.data, saved.data_len));
/* Snapshot после commit может отказать: ответ остаётся успехом, повторяем только чтение. */
snprintf(react, sizeof(react), "{\"cmd\":\"react\",\"ch\":\"%s\",\"message_id\":\"%s\",\"emoji\":0}", channel, hex);
for (unsigned publish = 0; publish < 2; publish++) {
const char* request = publish ? create : react;
for (enum poll_fault point = P_PREPARE; point <= P_FINALIZE; point++) {
int messages = message_count(inst, channel);
query = P_SNAPSHOT; query_number = 1; matching_queries = 0; fault_statement = NULL; fault_bind = 1; fault = point;
result = command(inst, request, 0); assert(fault == P_NONE && !fault_statement);
assert(json_number(inst->topo_sqlite_db, result, "$.committed") == 1 &&
json_number(inst->topo_sqlite_db, result, "$.snapshot_pending") == 1);
char acknowledged[65], read[256]; json_string(inst->topo_sqlite_db, result, "$.message_id", acknowledged, sizeof(acknowledged));
u_free(result);
assert(message_count(inst, channel) == messages + (int)publish);
snprintf(read, sizeof(read), "{\"cmd\":\"feedback\",\"ch\":\"%s\",\"message_id\":\"%s\"}", channel, acknowledged);
result = command(inst, read, 0); u_free(result);
assert(message_count(inst, channel) == messages + (int)publish);
}
int messages = message_count(inst, channel); unsigned events = changed_events;
fail_ack_allocation = 1; result = command(inst, request, -1); u_free(result);
assert(!fail_ack_allocation && changed_events == events && message_count(inst, channel) == messages);
}
chat_event_set_handler(inst, NULL);
printf("PASS: %u poll SQL faults preserve messages, reactions, Merkle root and events; retries succeed; emoji must be complete text\n", cases);
printf("PASS: %u poll SQL faults,8 post-commit read failures,2 pre-commit allocation failures; state/events preserved, read-only retry, complete emoji text\n", cases);
}
/* Проверяем границы, каноничность и невозможность интерпретации битого UTF-8. */