From d89a86c9dd309cb466fe0b7c265d93a4148f5b40 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 03:38:21 +0100 Subject: [PATCH] Cover feedback page and hash SQL errors without empty success --- tests/test_chat_feedback.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/tests/test_chat_feedback.c b/tests/test_chat_feedback.c index 2323a65d..f4349e9c 100644 --- a/tests/test_chat_feedback.c +++ b/tests/test_chat_feedback.c @@ -6,12 +6,17 @@ static const uint8_t owner_private[32] = {9}; static int events; enum sql_fault { F_NONE, F_BEGIN, F_PREPARE, F_BIND, F_STEP, F_CHANGES, F_FINALIZE, F_COMMIT }; -enum sql_operation { F_GET, F_PUT }; +enum sql_operation { F_GET, F_PUT, F_PAGE, F_HASH }; static enum sql_fault injected; static enum sql_operation injected_operation; static int injected_bind, failed_rollback, last_put; static int fault_statement(const char* sql) { + if (injected_operation == F_PAGE || injected_operation == F_HASH) { + const char* prefix = "SELECT message_id,node_id,kind,ver,data,signature,record_key,bucket FROM feedback_1001 WHERE bucket=?"; + return sql && !strncmp(sql, prefix, strlen(prefix)) && + ((strstr(sql, " AND (?=0 OR record_key>?)") != NULL) == (injected_operation == F_PAGE)); + } const char* prefix = injected_operation == F_GET ? "SELECT message_id,node_id,kind,ver,data,signature FROM feedback_1001 " : "INSERT INTO feedback_1001(record_key,bucket,message_id,node_id,kind,ver,data,signature)"; return sql && !strncmp(sql, prefix, strlen(prefix)); @@ -58,7 +63,8 @@ int __wrap_sqlite3_bind_int(sqlite3_stmt* st, int index, int value) { } extern int __real_sqlite3_step(sqlite3_stmt*); int __wrap_sqlite3_step(sqlite3_stmt* st) { - const char* sql = sqlite3_sql(st); last_put = !strncmp(sql, "INSERT INTO feedback_1001(", 26); + const char* sql = sqlite3_sql(st); const char* put = "INSERT INTO feedback_1001("; + last_put = !strncmp(sql, put, strlen(put)); if (take_fault(F_STEP, sql, 0)) return SQLITE_IOERR; return __real_sqlite3_step(st); } @@ -324,6 +330,18 @@ static void fault_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* dat assert(!merkle_tree_get(feedback_reader->topo_sqlite_db, "1:1001", 0, 0, reader) && !memcmp(reader, writer, 32)); events++; } +/* Те же операции, которыми Merkle получает лист и пересчитывает его хеш. */ +static int feedback_read(struct UTUN_INSTANCE* inst, const struct chat_feedback_record* record, enum sql_operation operation) { + merkle_key_t key, after = {{0}}, next; assert(!fb_key(1001, record, &key)); + uint64_t prefix = merkle_sync_level_prefix(fb_read64(key.bytes), MT_MAX_LEVEL); + if (operation == F_PAGE) { + uint8_t bytes[FB_WIRE_MAX + 2]; size_t length = sizeof(bytes); int more; + return fb_page(inst, "1001", prefix, 0, &after, bytes, &length, &next, &more); + } + EVP_MD_CTX* hash = EVP_MD_CTX_new(); assert(hash && EVP_DigestInit_ex(hash, EVP_sha256(), NULL) == 1); + int rc = fb_hash(inst, "1001", MT_MAX_LEVEL, prefix, hash); EVP_MD_CTX_free(hash); return rc; +} + static void sql_fault_regressions(struct UASYNC* ua) { char path[] = "/tmp/utun-feedback-fault-XXXXXX"; int fd = mkstemp(path); assert(fd >= 0); close(fd); struct UTUN_INSTANCE author, target, reader; memset(&reader, 0, sizeof(reader)); @@ -361,6 +379,20 @@ static void sql_fault_regressions(struct UASYNC* ua) { } } } + unsigned reads = 0; + for (enum sql_operation operation = F_PAGE; operation <= F_HASH; operation++) { + for (enum sql_fault point = F_PREPARE; point <= F_FINALIZE; point++) { + if (point == F_CHANGES) continue; + int bindings = point == F_BIND ? (operation == F_PAGE ? 3 : 1) : 1; + for (int bind = 1; bind <= bindings; bind++) { + root(&target, before); saved = events; + injected_operation = operation; injected_bind = bind; injected = point; + assert(feedback_read(&target, ¤t, operation) < 0 && injected == F_NONE && events == saved); + assert_stored(&reader, ¤t); root(&target, after); assert(!memcmp(before, after, 32)); + assert(feedback_read(&target, ¤t, operation) >= 0); reads++; + } + } + } const char* triggers[] = { "CREATE TEMP TRIGGER reject_feedback BEFORE INSERT ON feedback_1001 BEGIN SELECT RAISE(ABORT,'feedback'); END", "CREATE TEMP TRIGGER reject_feedback BEFORE INSERT ON feedback_1001 BEGIN SELECT RAISE(IGNORE); END" @@ -398,7 +430,7 @@ static void sql_fault_regressions(struct UASYNC* ua) { member_sync_destroy(&target); assert(sqlite3_close(target.topo_sqlite_db) == SQLITE_OK); assert_stored(&reader, ¤t); member_sync_destroy(&author); assert(sqlite3_close(author.topo_sqlite_db) == SQLITE_OK); assert(sqlite3_close(reader.topo_sqlite_db) == SQLITE_OK); assert(!remove(path)); feedback_reader = NULL; - DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS feedback:%u SQL faults/retries, stale-state lookup failure, ABORT/IGNORE/veto, foreign no-op/page, failed rollback stops owner; reader sees exact committed records/roots before events", cases); + DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS feedback:%u write/%u read faults and retries, stale lookup, ABORT/IGNORE/veto, foreign no-op/page, failed rollback; reader checks records/roots before events", cases, reads); } /* Диагностика копии профиля: без изменения записей или доступа к приватным ключам. */