@@ -12485,6 +12485,81 @@ bool do_test_delete_resurrect_ordering (int nclients, bool print_result, bool cl
1248512485 return result;
1248612486}
1248712487
12488+ bool do_test_delete_resurrect_multi_cycle (int nclients, bool print_result, bool cleanup_databases) {
12489+ sqlite3 *db[3] = {NULL, NULL, NULL};
12490+ bool result = false;
12491+
12492+ time_t timestamp = time(NULL);
12493+ int saved_counter = test_counter;
12494+
12495+ for (int i = 0; i < 3; i++) {
12496+ db[i] = do_create_database_file(i, timestamp, test_counter++);
12497+ if (!db[i]) return false;
12498+
12499+ int rc = sqlite3_exec(db[i], "CREATE TABLE test_tbl (id TEXT PRIMARY KEY, val TEXT);", NULL, NULL, NULL);
12500+ if (rc != SQLITE_OK) goto finalize;
12501+ rc = sqlite3_exec(db[i], "SELECT cloudsync_init('test_tbl');", NULL, NULL, NULL);
12502+ if (rc != SQLITE_OK) goto finalize;
12503+ }
12504+
12505+ // Initial insert and full sync
12506+ if (sqlite3_exec(db[0], "INSERT INTO test_tbl VALUES ('row1', 'original');", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12507+ if (!do_merge_using_payload(db[0], db[1], false, true)) goto finalize;
12508+ if (!do_merge_using_payload(db[0], db[2], false, true)) goto finalize;
12509+
12510+ // Cycle 1: A deletes, B resurrects, C receives resurrection before stale delete.
12511+ if (sqlite3_exec(db[0], "DELETE FROM test_tbl WHERE id = 'row1';", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12512+ if (!do_merge_using_payload(db[0], db[1], true, true)) goto finalize;
12513+
12514+ if (sqlite3_exec(db[1], "INSERT INTO test_tbl VALUES ('row1', 'resurrected_by_b');", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12515+ if (!do_merge_using_payload(db[1], db[2], true, true)) goto finalize;
12516+ if (!do_merge_using_payload(db[0], db[2], true, true)) goto finalize;
12517+ if (!do_merge_using_payload(db[1], db[0], true, true)) goto finalize;
12518+
12519+ // Cycle 2: B deletes, A resurrects, C again receives resurrection before stale delete.
12520+ if (sqlite3_exec(db[1], "DELETE FROM test_tbl WHERE id = 'row1';", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12521+ if (!do_merge_using_payload(db[1], db[0], true, true)) goto finalize;
12522+
12523+ if (sqlite3_exec(db[0], "INSERT INTO test_tbl VALUES ('row1', 'resurrected_by_a_again');", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12524+ if (!do_merge_using_payload(db[0], db[2], true, true)) goto finalize;
12525+ if (!do_merge_using_payload(db[1], db[2], true, true)) goto finalize;
12526+ if (!do_merge_using_payload(db[0], db[1], true, true)) goto finalize;
12527+
12528+ const char *query = "SELECT * FROM test_tbl ORDER BY id;";
12529+ result = do_compare_queries(db[0], query, db[1], query, -1, -1, print_result);
12530+ if (result) result = do_compare_queries(db[0], query, db[2], query, -1, -1, print_result);
12531+
12532+ if (result) {
12533+ for (int i = 0; i < 3; i++) {
12534+ int64_t count = do_select_int(db[i], "SELECT COUNT(*) FROM test_tbl WHERE id = 'row1';");
12535+ int64_t live_sentinel = do_select_int(db[i], "SELECT COUNT(*) FROM test_tbl_cloudsync WHERE col_name = '__[RIP]__' AND col_version % 2 = 1;");
12536+
12537+ if (count != 1) {
12538+ printf("delete_resurrect_multi_cycle: expected row1 to exist on db[%d], count=%" PRId64 "\n", i, count);
12539+ result = false;
12540+ break;
12541+ }
12542+
12543+ if (live_sentinel != 1) {
12544+ printf("delete_resurrect_multi_cycle: expected 1 live sentinel on db[%d], got=%" PRId64 "\n", i, live_sentinel);
12545+ result = false;
12546+ break;
12547+ }
12548+ }
12549+ }
12550+
12551+ finalize:
12552+ for (int i = 0; i < 3; i++) {
12553+ if (db[i]) close_db(db[i]);
12554+ if (cleanup_databases) {
12555+ char buf[256];
12556+ do_build_database_path(buf, i, timestamp, saved_counter++);
12557+ file_delete_internal(buf);
12558+ }
12559+ }
12560+ return result;
12561+ }
12562+
1248812563bool do_test_large_composite_pk (int nclients, bool print_result, bool cleanup_databases) {
1248912564 sqlite3 *db[2] = {NULL, NULL};
1249012565 bool result = false;
@@ -12804,6 +12879,7 @@ int main (int argc, const char * argv[]) {
1280412879 result += test_report("Payload Idempotency Test:", do_test_payload_idempotency(2, print_result, cleanup_databases));
1280512880 result += test_report("CL Tiebreak Test:", do_test_causal_length_tiebreak(3, print_result, cleanup_databases));
1280612881 result += test_report("Delete/Resurrect Order:", do_test_delete_resurrect_ordering(3, print_result, cleanup_databases));
12882+ result += test_report("Delete/Resurrect Multi-Cycle:", do_test_delete_resurrect_multi_cycle(3, print_result, cleanup_databases));
1280712883 result += test_report("Large Composite PK Test:", do_test_large_composite_pk(2, print_result, cleanup_databases));
1280812884 result += test_report("Schema Hash Mismatch:", do_test_schema_hash_mismatch(2, print_result, cleanup_databases));
1280912885 result += test_report("Stale Table Settings:", do_test_stale_table_settings(cleanup_databases));
0 commit comments