use fsqlite_core::connection::Connection;
use fsqlite_types::value::SqliteValue;
fn scalar_text(rows: &[fsqlite_core::connection::Row]) -> String {
assert_eq!(rows.len(), 1, "expected one row");
match &rows[0].values()[0] {
SqliteValue::Text(s) => s.to_string(),
other => panic!("expected TEXT, got {other:?}"),
}
}
#[test]
fn bd_pgqsk_l5_encoding_setter_spellings_and_rejection() {
asupersync::test_utils::run_test(|| async {
for (spelling, expected) in [
("UTF-8", "UTF-8"),
("UTF8", "UTF-8"),
("utf8", "UTF-8"),
("UTF-16", "UTF-16le"),
("UTF16", "UTF-16le"),
("UTF-16le", "UTF-16le"),
("UTF16le", "UTF-16le"),
("UTF-16be", "UTF-16be"),
("UTF16be", "UTF-16be"),
] {
let conn = Connection::open(":memory:").await.unwrap();
conn.execute(&format!("PRAGMA encoding = '{spelling}';"))
.await
.unwrap_or_else(|e| panic!("'{spelling}' should be accepted: {e:?}"));
let got = scalar_text(&conn.query("PRAGMA encoding;").await.unwrap());
assert_eq!(
got, expected,
"spelling '{spelling}' resolves to {expected}"
);
}
for bad in [
"bogus",
"UTF-32",
"latin1",
"U-T-F-8",
"UTF--16",
"utf-16-le",
] {
let conn = Connection::open(":memory:").await.unwrap();
let err = conn
.execute(&format!("PRAGMA encoding = '{bad}';"))
.await
.expect_err(&format!("'{bad}' must be rejected"));
assert!(
err.to_string().contains("unsupported encoding"),
"'{bad}' error should mention unsupported encoding: {err}"
);
}
let conn = Connection::open(":memory:").await.unwrap();
let err = conn
.execute("PRAGMA encoding = 5;")
.await
.expect_err("non-string encoding must be rejected");
assert!(
err.to_string().contains("unsupported encoding"),
"non-string error should mention unsupported encoding: {err}"
);
});
}
#[test]
fn bd_pgqsk_l5_encoding_valid_name_noop_on_nonempty_db() {
asupersync::test_utils::run_test(|| async {
let conn = Connection::open(":memory:").await.unwrap();
conn.execute("CREATE TABLE t(a);").await.unwrap();
conn.execute("PRAGMA encoding = 'UTF-16le';").await.unwrap();
assert_eq!(
scalar_text(&conn.query("PRAGMA encoding;").await.unwrap()),
"UTF-8",
"encoding cannot change on a non-empty DB"
);
let err = conn
.execute("PRAGMA encoding = 'bogus';")
.await
.expect_err("bogus must be rejected on a non-empty DB too");
assert!(
err.to_string().contains("unsupported encoding"),
"got: {err}"
);
});
}
#[test]
fn bd_pgqsk_m7_empty_attach_adopts_main_encoding() {
asupersync::test_utils::run_test(|| async {
let dir = tempfile::tempdir().unwrap();
let main_path = dir.path().join("main16.db");
let aux_path = dir.path().join("aux_new.db");
let main_str = main_path.to_string_lossy().into_owned();
let aux_str = aux_path.to_string_lossy().into_owned();
let conn = Connection::open(&main_str).await.unwrap();
conn.execute("PRAGMA encoding = 'UTF-16le';").await.unwrap();
conn.execute("CREATE TABLE m(a);").await.unwrap();
conn.execute(&format!("ATTACH '{aux_str}' AS aux;"))
.await
.unwrap_or_else(|e| panic!("empty cross-encoding ATTACH must succeed: {e:?}"));
conn.execute("CREATE TABLE aux.x(y TEXT);").await.unwrap();
conn.execute("INSERT INTO aux.x VALUES ('café');")
.await
.unwrap();
let got = conn.query("SELECT y FROM aux.x;").await.unwrap();
assert_eq!(scalar_text(&got), "café", "attached row round-trips");
conn.close().await.unwrap();
let stock = rusqlite::Connection::open(&aux_path).unwrap();
let aux_encoding: String = stock
.query_row("PRAGMA encoding;", [], |r| r.get(0))
.unwrap();
assert_eq!(
aux_encoding, "UTF-16le",
"empty aux must adopt AND persist main's UTF-16le encoding"
);
let integrity: String = stock
.query_row("PRAGMA integrity_check;", [], |r| r.get(0))
.unwrap();
assert_eq!(integrity, "ok", "stock integrity_check on the aux image");
let val: String = stock
.query_row("SELECT y FROM x;", [], |r| r.get(0))
.unwrap();
assert_eq!(val, "café", "stock reads the attached-then-written row");
});
}
#[test]
fn bd_lzbku_unwritten_empty_aux_reattaches_to_utf8_main() {
asupersync::test_utils::run_test(|| async {
let dir = tempfile::tempdir().unwrap();
let main16 = dir.path().join("m16.db").to_string_lossy().into_owned();
let main8 = dir.path().join("m8.db").to_string_lossy().into_owned();
let aux = dir.path().join("aux.db");
let aux_str = aux.to_string_lossy().into_owned();
let c16 = Connection::open(&main16).await.unwrap();
c16.execute("PRAGMA encoding = 'UTF-16le';").await.unwrap();
c16.execute("CREATE TABLE m(a);").await.unwrap();
c16.execute(&format!("ATTACH '{aux_str}' AS aux;"))
.await
.unwrap_or_else(|e| panic!("empty cross-encoding ATTACH must succeed: {e:?}"));
c16.execute("DETACH aux;").await.unwrap();
c16.close().await.unwrap();
let c8 = Connection::open(&main8).await.unwrap();
c8.execute("CREATE TABLE m8(a);").await.unwrap();
c8.execute(&format!("ATTACH '{aux_str}' AS aux;"))
.await
.unwrap_or_else(|e| {
panic!(
"re-ATTACH of an un-materialized empty aux to a UTF-8 main must succeed: {e:?}"
)
});
c8.close().await.unwrap();
});
}
#[test]
fn bd_lzbku_readonly_main_attaches_empty_aux() {
asupersync::test_utils::run_test(|| async {
let dir = tempfile::tempdir().unwrap();
let main16 = dir.path().join("m16.db").to_string_lossy().into_owned();
let aux = dir.path().join("aux.db");
let aux_str = aux.to_string_lossy().into_owned();
{
let w = Connection::open(&main16).await.unwrap();
w.execute("PRAGMA encoding = 'UTF-16le';").await.unwrap();
w.execute("CREATE TABLE m(a);").await.unwrap();
w.close().await.unwrap();
}
{
let a = Connection::open(&aux_str).await.unwrap();
a.execute("PRAGMA user_version = 1;").await.unwrap();
a.close().await.unwrap();
}
let ro = Connection::open_schema_only(&main16).await.unwrap();
ro.execute(&format!("ATTACH '{aux_str}' AS aux;"))
.await
.unwrap_or_else(|e| {
panic!("ATTACH of an empty aux to a read-only main must succeed: {e:?}")
});
ro.close().await.unwrap();
});
}
#[test]
fn bd_dbpl2_encoding_reset_on_empty_then_locked_after_schema() {
asupersync::test_utils::run_test(|| async {
let conn = Connection::open(":memory:").await.unwrap();
conn.execute("PRAGMA encoding = 'UTF-16le';").await.unwrap();
conn.execute("PRAGMA encoding = 'UTF-8';").await.unwrap();
assert_eq!(
scalar_text(&conn.query("PRAGMA encoding;").await.unwrap()),
"UTF-8",
"encoding is re-settable while the DB is empty"
);
let conn2 = Connection::open(":memory:").await.unwrap();
conn2
.execute("PRAGMA encoding = 'UTF-16le';")
.await
.unwrap();
conn2.execute("CREATE TABLE t(a);").await.unwrap();
conn2.execute("PRAGMA encoding = 'UTF-8';").await.unwrap();
assert_eq!(
scalar_text(&conn2.query("PRAGMA encoding;").await.unwrap()),
"UTF-16le",
"encoding is locked once a schema object exists"
);
});
}