#![cfg(feature = "testing")]
use sqlite_diff_rs::testing::assert_patchset_sql_parity;
use sqlite_diff_rs::{PatchSet, SimpleTable};
fn patchset_with(tables: &[SimpleTable]) -> PatchSet<SimpleTable, String, Vec<u8>> {
let mut ps = PatchSet::new();
for t in tables {
ps.add_table(t);
}
ps
}
#[test]
fn test_digest_simple_insert() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
ps.digest_sql("INSERT INTO users (id, name) VALUES (1, 'Alice');")
.unwrap();
assert_eq!(ps.len(), 1);
assert!(!ps.is_empty());
assert!(!ps.build().is_empty());
}
#[test]
fn test_digest_simple_delete() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
ps.digest_sql("DELETE FROM users WHERE id = 1;").unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_digest_simple_update() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
ps.digest_sql("UPDATE users SET name = 'Bob' WHERE id = 1;")
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_digest_multiple_tables() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let posts = SimpleTable::new("posts", &["id", "user_id", "content"], &[0]);
let mut ps = patchset_with(&[users, posts]);
ps.digest_sql("INSERT INTO users (id, name) VALUES (1, 'Alice');")
.unwrap();
ps.digest_sql("INSERT INTO posts (id, user_id, content) VALUES (1, 1, 'Hello World');")
.unwrap();
assert_eq!(ps.len(), 2);
}
#[test]
fn test_digest_mixed_operations() {
let users = SimpleTable::new("users", &["id", "name", "age"], &[0]);
let mut ps = patchset_with(&[users]);
ps.digest_sql(
"INSERT INTO users (id, name, age) VALUES (1, 'Alice', 30);\
INSERT INTO users (id, name, age) VALUES (2, 'Bob', 25);\
UPDATE users SET age = 31 WHERE id = 1;\
DELETE FROM users WHERE id = 2;",
)
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_table_not_registered_error() {
let mut ps: PatchSet<SimpleTable, String, Vec<u8>> = PatchSet::new();
let result = ps.digest_sql("INSERT INTO users (id, name) VALUES (1, 'Alice');");
assert!(result.is_err());
}
#[test]
fn test_invalid_sql_error() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
let result = ps.digest_sql("THIS IS NOT VALID SQL");
assert!(result.is_err());
}
#[test]
fn test_create_table_rejected() {
let mut ps: PatchSet<SimpleTable, String, Vec<u8>> = PatchSet::new();
let result = ps.digest_sql("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
assert!(result.is_err());
}
#[test]
fn test_unknown_column_error() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
let result = ps.digest_sql("INSERT INTO users (id, nonexistent) VALUES (1, 'Alice');");
assert!(result.is_err());
}
#[test]
fn test_digest_integer_values() {
let numbers = SimpleTable::new("numbers", &["id", "value"], &[0]);
let mut ps = patchset_with(&[numbers]);
ps.digest_sql(
"INSERT INTO numbers (id, value) VALUES (1, 42);\
INSERT INTO numbers (id, value) VALUES (2, -100);\
INSERT INTO numbers (id, value) VALUES (3, 0);",
)
.unwrap();
assert_eq!(ps.len(), 3);
}
#[test]
fn test_digest_real_values() {
let floats = SimpleTable::new("floats", &["id", "value"], &[0]);
let mut ps = patchset_with(&[floats]);
ps.digest_sql(
"INSERT INTO floats (id, value) VALUES (1, 3.14);\
INSERT INTO floats (id, value) VALUES (2, -2.5);",
)
.unwrap();
assert_eq!(ps.len(), 2);
}
#[test]
fn test_digest_text_values() {
let texts = SimpleTable::new("texts", &["id", "value"], &[0]);
let mut ps = patchset_with(&[texts]);
ps.digest_sql(
"INSERT INTO texts (id, value) VALUES (1, 'hello');\
INSERT INTO texts (id, value) VALUES (2, 'world');\
INSERT INTO texts (id, value) VALUES (3, '');",
)
.unwrap();
assert_eq!(ps.len(), 3);
}
#[test]
fn test_digest_null_values() {
let nullable = SimpleTable::new("nullable", &["id", "value"], &[0]);
let mut ps = patchset_with(&[nullable]);
ps.digest_sql("INSERT INTO nullable (id, value) VALUES (1, NULL);")
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_digest_blob_values() {
let blobs = SimpleTable::new("blobs", &["id", "data"], &[0]);
let mut ps = patchset_with(&[blobs]);
ps.digest_sql("INSERT INTO blobs (id, data) VALUES (1, X'DEADBEEF');")
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_digest_composite_pk_insert() {
let composite = SimpleTable::new("composite", &["a", "b", "value"], &[0, 1]);
let mut ps = patchset_with(&[composite]);
ps.digest_sql("INSERT INTO composite (a, b, value) VALUES (1, 2, 'test');")
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_digest_composite_pk_delete() {
let composite = SimpleTable::new("composite", &["a", "b", "value"], &[0, 1]);
let mut ps = patchset_with(&[composite]);
ps.digest_sql("DELETE FROM composite WHERE a = 1 AND b = 2;")
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_digest_composite_pk_update() {
let composite = SimpleTable::new("composite", &["a", "b", "value"], &[0, 1]);
let mut ps = patchset_with(&[composite]);
ps.digest_sql("UPDATE composite SET value = 'updated' WHERE a = 1 AND b = 2;")
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_insert_then_delete_cancels() {
let t = SimpleTable::new("t", &["id", "v"], &[0]);
let mut ps = patchset_with(&[t]);
ps.digest_sql(
"INSERT INTO t (id, v) VALUES (1, 'a');\
DELETE FROM t WHERE id = 1;",
)
.unwrap();
assert_eq!(ps.len(), 0);
}
#[test]
fn test_insert_then_update_becomes_insert() {
let t = SimpleTable::new("t", &["id", "v"], &[0]);
let mut ps = patchset_with(&[t]);
ps.digest_sql(
"INSERT INTO t (id, v) VALUES (1, 'a');\
UPDATE t SET v = 'b' WHERE id = 1;",
)
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_update_then_update_consolidates() {
let t = SimpleTable::new("t", &["id", "v"], &[0]);
let mut ps = patchset_with(&[t]);
ps.digest_sql(
"UPDATE t SET v = 'a' WHERE id = 1;\
UPDATE t SET v = 'b' WHERE id = 1;",
)
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn test_empty_sql() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
ps.digest_sql("").unwrap();
assert!(ps.is_empty());
}
#[test]
fn test_semicolons_only() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
ps.digest_sql(";;;").unwrap();
assert!(ps.is_empty());
}
#[test]
fn test_multiple_digest_calls() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let mut ps = patchset_with(&[users]);
ps.digest_sql("INSERT INTO users (id, name) VALUES (1, 'Alice');")
.unwrap();
ps.digest_sql("INSERT INTO users (id, name) VALUES (2, 'Bob');")
.unwrap();
assert_eq!(ps.len(), 2);
}
#[test]
fn test_delete_rejects_non_pk_in_where() {
let users = SimpleTable::new("users", &["id", "name", "status"], &[0]);
let mut ps = patchset_with(&[users]);
let result = ps.digest_sql("DELETE FROM users WHERE id = 1 AND status = 'active'");
assert!(result.is_err());
}
#[test]
fn test_negative_numbers() {
let t = SimpleTable::new("t", &["a", "b"], &[0]);
let mut ps = patchset_with(&[t]);
ps.digest_sql("INSERT INTO t (a, b) VALUES (-42, -3.14);")
.unwrap();
assert_eq!(ps.len(), 1);
}
#[test]
fn parity_single_insert() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
assert_patchset_sql_parity(
&[users],
&[
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
"INSERT INTO users (id, name) VALUES (1, 'Alice')",
],
);
}
#[test]
fn parity_insert_and_update() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
assert_patchset_sql_parity(
&[users],
&[
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
"INSERT INTO users (id, name) VALUES (1, 'Alice')",
"UPDATE users SET name = 'Alicia' WHERE id = 1",
],
);
}
#[test]
fn parity_multi_table() {
let users = SimpleTable::new("users", &["id", "name"], &[0]);
let posts = SimpleTable::new("posts", &["id", "title"], &[0]);
assert_patchset_sql_parity(
&[users, posts],
&[
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
"CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT)",
"INSERT INTO users (id, name) VALUES (1, 'Alice')",
"INSERT INTO posts (id, title) VALUES (1, 'Hello')",
],
);
}
#[test]
fn parity_composite_pk() {
let order_items =
SimpleTable::new("order_items", &["order_id", "item_id", "quantity"], &[0, 1]);
assert_patchset_sql_parity(
&[order_items],
&[
"CREATE TABLE order_items (order_id INTEGER, item_id INTEGER, quantity INTEGER, PRIMARY KEY (order_id, item_id))",
"INSERT INTO order_items (order_id, item_id, quantity) VALUES (1, 100, 5)",
"INSERT INTO order_items (order_id, item_id, quantity) VALUES (1, 101, 3)",
],
);
}