#![cfg(feature = "testing")]
use rusqlite::Connection;
use sqlite_diff_rs::testing::{apply_changeset, get_all_rows};
use sqlite_diff_rs::{
ChangeDelete, ChangeSet, ChangesetFormat, DiffOps, Insert, Reverse, SimpleTable, Update,
};
#[test]
fn test_reverse_single_insert() {
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(insert);
let reversed = changeset.clone().reverse();
assert_eq!(reversed.len(), 1);
let changeset_bytes = changeset.build();
let reversed_bytes = reversed.build();
assert_ne!(changeset_bytes, reversed_bytes);
}
#[test]
fn test_reverse_single_delete() {
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let delete = ChangeDelete::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().delete(delete);
let reversed = changeset.clone().reverse();
assert_eq!(reversed.len(), 1);
}
#[test]
fn test_reverse_single_update() {
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let update = Update::<SimpleTable, ChangesetFormat, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64, 1i64)
.unwrap() .set(1, "Alice", "Alicia")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().update(update);
let reversed = changeset.clone().reverse();
assert_eq!(reversed.len(), 1);
}
#[test]
fn test_reverse_multiple_operations() {
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let insert1 = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let insert2 = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 2i64)
.unwrap()
.set(1, "Bob")
.unwrap();
let delete = ChangeDelete::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 3i64)
.unwrap()
.set(1, "Charlie")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new()
.insert(insert1)
.insert(insert2)
.delete(delete);
let reversed = changeset.clone().reverse();
assert_eq!(reversed.len(), 3);
}
#[test]
fn test_double_reverse_is_identity() {
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let original: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(insert);
let double_reversed = original.clone().reverse().reverse();
assert_eq!(original, double_reversed);
}
#[test]
fn test_double_reverse_complex() {
let schema = SimpleTable::new("users", &["id", "name", "age"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap()
.set(2, 30i64)
.unwrap();
let update = Update::<SimpleTable, ChangesetFormat, String, Vec<u8>>::from(schema.clone())
.set(0, 2i64, 2i64)
.unwrap()
.set(1, "Bob", "Robert")
.unwrap()
.set(2, 25i64, 26i64)
.unwrap();
let delete = ChangeDelete::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 3i64)
.unwrap()
.set(1, "Charlie")
.unwrap()
.set(2, 35i64)
.unwrap();
let original: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new()
.insert(insert)
.update(update)
.delete(delete);
let double_reversed = original.clone().reverse().reverse();
assert_eq!(original, double_reversed);
}
#[test]
fn test_apply_and_reverse_insert() {
let conn1 = Connection::open_in_memory().unwrap();
let conn2 = Connection::open_in_memory().unwrap();
let schema_sql = "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)";
conn1.execute(schema_sql, []).unwrap();
conn2.execute(schema_sql, []).unwrap();
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(insert);
let changeset_bytes = changeset.build();
apply_changeset(&conn1, &changeset_bytes).unwrap();
assert_eq!(get_all_rows(&conn1, "users").len(), 1);
assert_eq!(get_all_rows(&conn2, "users").len(), 0);
let reversed = changeset.reverse();
let reversed_bytes = reversed.build();
apply_changeset(&conn1, &reversed_bytes).unwrap();
assert_eq!(get_all_rows(&conn1, "users").len(), 0);
assert_eq!(get_all_rows(&conn2, "users").len(), 0);
}
#[test]
fn test_apply_and_reverse_delete() {
let conn1 = Connection::open_in_memory().unwrap();
let conn2 = Connection::open_in_memory().unwrap();
let schema_sql = "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)";
conn1.execute(schema_sql, []).unwrap();
conn2.execute(schema_sql, []).unwrap();
conn1
.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')", [])
.unwrap();
conn2
.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')", [])
.unwrap();
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let delete = ChangeDelete::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().delete(delete);
let changeset_bytes = changeset.build();
apply_changeset(&conn1, &changeset_bytes).unwrap();
assert_eq!(get_all_rows(&conn1, "users").len(), 0);
assert_eq!(get_all_rows(&conn2, "users").len(), 1);
let reversed = changeset.reverse();
let reversed_bytes = reversed.build();
apply_changeset(&conn1, &reversed_bytes).unwrap();
assert_eq!(get_all_rows(&conn1, "users").len(), 1);
assert_eq!(get_all_rows(&conn2, "users").len(), 1);
let rows1 = get_all_rows(&conn1, "users");
let rows2 = get_all_rows(&conn2, "users");
assert_eq!(rows1, rows2);
}
#[test]
fn test_apply_and_reverse_update() {
let conn1 = Connection::open_in_memory().unwrap();
let conn2 = Connection::open_in_memory().unwrap();
let schema_sql = "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)";
conn1.execute(schema_sql, []).unwrap();
conn2.execute(schema_sql, []).unwrap();
conn1
.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')", [])
.unwrap();
conn2
.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')", [])
.unwrap();
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let update = Update::<SimpleTable, ChangesetFormat, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64, 1i64)
.unwrap()
.set(1, "Alice", "Alicia")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().update(update);
let changeset_bytes = changeset.build();
apply_changeset(&conn1, &changeset_bytes).unwrap();
let rows1 = get_all_rows(&conn1, "users");
let rows2 = get_all_rows(&conn2, "users");
assert_ne!(rows1, rows2);
assert!(rows1[0][1].contains("Alicia"));
assert!(rows2[0][1].contains("Alice"));
let reversed = changeset.reverse();
let reversed_bytes = reversed.build();
apply_changeset(&conn1, &reversed_bytes).unwrap();
let rows1 = get_all_rows(&conn1, "users");
let rows2 = get_all_rows(&conn2, "users");
assert_eq!(rows1, rows2);
assert!(rows1[0][1].contains("Alice"));
}
#[test]
fn test_apply_and_reverse_multiple_operations() {
let conn1 = Connection::open_in_memory().unwrap();
let conn2 = Connection::open_in_memory().unwrap();
let schema_sql = "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)";
conn1.execute(schema_sql, []).unwrap();
conn2.execute(schema_sql, []).unwrap();
conn1
.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')", [])
.unwrap();
conn1
.execute("INSERT INTO users (id, name) VALUES (2, 'Bob')", [])
.unwrap();
conn2
.execute("INSERT INTO users (id, name) VALUES (1, 'Alice')", [])
.unwrap();
conn2
.execute("INSERT INTO users (id, name) VALUES (2, 'Bob')", [])
.unwrap();
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 3i64)
.unwrap()
.set(1, "Charlie")
.unwrap();
let update = Update::<SimpleTable, ChangesetFormat, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64, 1i64)
.unwrap()
.set(1, "Alice", "Alicia")
.unwrap();
let delete = ChangeDelete::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 2i64)
.unwrap()
.set(1, "Bob")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new()
.insert(insert)
.update(update)
.delete(delete);
let changeset_bytes = changeset.build();
let original_rows = get_all_rows(&conn1, "users");
apply_changeset(&conn1, &changeset_bytes).unwrap();
let modified_rows = get_all_rows(&conn1, "users");
assert_ne!(original_rows, modified_rows);
let reversed = changeset.reverse();
let reversed_bytes = reversed.build();
apply_changeset(&conn1, &reversed_bytes).unwrap();
let final_rows = get_all_rows(&conn1, "users");
assert_eq!(original_rows, final_rows);
let conn2_rows = get_all_rows(&conn2, "users");
assert_eq!(final_rows, conn2_rows);
}
#[test]
fn test_reverse_with_composite_primary_key() {
let schema = SimpleTable::new("orders", &["user_id", "order_id", "status"], &[0, 1]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, 100i64)
.unwrap()
.set(2, "pending")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(insert);
let reversed = changeset.clone().reverse();
let double_reversed = reversed.reverse();
assert_eq!(changeset, double_reversed);
}
#[test]
fn test_reverse_with_null_values() {
let schema = SimpleTable::new("items", &["id", "description", "price"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set_null(1)
.unwrap() .set(2, 9.99f64)
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new().insert(insert);
let reversed = changeset.clone().reverse();
let double_reversed = reversed.reverse();
assert_eq!(changeset, double_reversed);
}
#[test]
fn test_reverse_empty_changeset() {
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> = ChangeSet::new();
let reversed = changeset.clone().reverse();
assert_eq!(reversed.len(), 0);
assert!(reversed.is_empty());
}
#[test]
fn test_reverse_consolidated_operations() {
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let update = Update::<SimpleTable, ChangesetFormat, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64, 1i64)
.unwrap()
.set(1, "Alice", "Alicia")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> =
ChangeSet::new().insert(insert).update(update);
assert_eq!(changeset.len(), 1);
let reversed = changeset.clone().reverse();
assert_eq!(reversed.len(), 1);
let double_reversed = reversed.reverse();
assert_eq!(changeset, double_reversed);
}
#[test]
fn test_reverse_cancelled_operations() {
let schema = SimpleTable::new("users", &["id", "name"], &[0]);
let insert = Insert::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let delete = ChangeDelete::<_, String, Vec<u8>>::from(schema.clone())
.set(0, 1i64)
.unwrap()
.set(1, "Alice")
.unwrap();
let changeset: ChangeSet<SimpleTable, String, Vec<u8>> =
ChangeSet::new().insert(insert).delete(delete);
assert_eq!(changeset.len(), 0);
let reversed = changeset.clone().reverse();
assert_eq!(reversed.len(), 0);
}