Test final-page update races and replication read failures

This commit is contained in:
evgeny committed 2026-10-22 21:16:59 +02:00
1 parent bc149497f3
commit 8230eb4de7
1 file changed
+47
+47
View File
@@ -228,6 +228,20 @@ static void test_atomic_and_validation(void) {
bad = response;
struct media_pkt_sync_entry* rows = (struct media_pkt_sync_entry*)(bad.data + MEDIA_SYNC_RESP_HDR_SIZE);
rows[1].status = 3; receive(a, &bad);
for (int fault = 0; fault < 8; fault++) {
bad = response;
struct media_pkt_sync_resp* h = (struct media_pkt_sync_resp*)bad.data;
rows = (struct media_pkt_sync_entry*)(bad.data + sizeof(*h));
if (fault == 0) bad.len++;
if (fault == 1) write_le(&h->num_entries, 33, 2);
if (fault == 2) h->status = 3;
if (fault == 3) write_le(&h->next_id, UINT64_MAX, 8);
if (fault == 4) rows[1].id = rows[0].id;
if (fault == 5) memset(&rows[0].node_id, 0, 8);
if (fault == 6) write_le(&rows[1].timestamp, UINT64_MAX, 8);
if (fault == 7) memcpy(rows[1].block_id, rows[0].block_id, 16);
receive(a, &bad);
}
REQUIRE(sp->request_pending && sp->deadline_tb == deadline && !scalar(a->md.db, "SELECT COUNT(*) FROM super_sync"));
REQUIRE(!scalar(a->md.db, "SELECT COUNT(*) FROM block_availability"));
const char* failures[] = {
@@ -304,6 +318,38 @@ static struct packet respond_to(struct UTUN_INSTANCE* source, const struct packe
static int reject_commit(void* arg) { (void)arg; return 1; }
static int deny_rows(void* arg, int action, const char* table, const char* column, const char* db, const char* trigger) {
(void)arg; (void)column; (void)db; (void)trigger;
return action == SQLITE_READ && table && !strcmp(table, "block_availability") ? SQLITE_DENY : SQLITE_OK;
}
/* Обновление после последнего snapshot: обе перестановки CHANGED/RESP и потеря уведомления. */
static void test_hints_and_read_error(void) {
for (int order = 0; order < 3; order++) {
struct UTUN_INSTANCE* a = node(SERVER), *b = node(CLIENT);
begin_pull(a, CLIENT, GROUP);
struct packet q = request_from(a, CLIENT);
struct media_super_peer* sp = peer(a, CLIENT, GROUP);
uint64_t deadline = sp->deadline_tb;
REQUIRE(sqlite3_set_authorizer(b->md.db, deny_rows, NULL) == SQLITE_OK);
struct packet error = respond_to(b, &q);
REQUIRE(((struct media_pkt_sync_resp*)error.data)->status == MEDIA_SYNC_DB_ERROR && error.len == MEDIA_SYNC_RESP_HDR_SIZE);
receive(a, &error); REQUIRE(sp->request_pending && sp->deadline_tb == deadline);
REQUIRE(sqlite3_set_authorizer(b->md.db, NULL, NULL) == SQLITE_OK);
struct packet empty = respond_to(b, &q);
struct media_index_result r = result(1); seed(b, &r, 1, 1, 0);
struct packet hint = {.from = CLIENT, .to = SERVER, .group = GROUP, .len = 1, .data = {MEDIA_SUBCMD_CHANGED}};
if (order == 0) receive(a, &hint);
receive(a, &empty);
if (order == 1) receive(a, &hint);
if (order != 0) { packet_count = 0; expire(sp); } /* order=2: потерянный hint, истечение idle-сверки. */
q = request_from(a, CLIENT);
struct packet filled = respond_to(b, &q); receive(a, &filled);
REQUIRE(scalar(a->md.db, "SELECT COUNT(*) FROM block_availability") == 1 && !sp->request_pending);
free_node(a); free_node(b); media_index_result_free(&r); reset(); capture_blocks = 1;
}
}
/* Потери запроса/ответа, backoff с тем же UUID, ошибка commit и пересоздание БД источника. */
static void test_retry_and_epoch(void) {
struct UTUN_INSTANCE* a = node(SERVER), *b = node(CLIENT);
@@ -405,6 +451,7 @@ int main(void) {
test_atomic_and_validation();
test_convergence_and_lifecycle();
test_retry_and_epoch();
test_hints_and_read_error();
test_backpressure();
REQUIRE(cm_opened == cm_closed);
uasync_destroy(ua, 0);