Skip to content

Commit 2184a31

Browse files
committed
test(query_processing): cover URI memory rw mode and clean vacuum helpers
Add a regression test for file::memory:?mode=rw and simplify the VACUUM URI test helpers and names so the URI mode coverage is easier to follow.
1 parent f443033 commit 2184a31

File tree

1 file changed

+73
-41
lines changed

1 file changed

+73
-41
lines changed

tests/integration/query_processing/test_vacuum.rs

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,6 @@ fn escape_sqlite_string_literal(text: &str) -> String {
3737
text.replace('\'', "''")
3838
}
3939

40-
fn vacuum_into_literal(conn: &Arc<Connection>, dest_literal: &str) -> anyhow::Result<()> {
41-
conn.execute(format!(
42-
"VACUUM INTO '{}'",
43-
escape_sqlite_string_literal(dest_literal)
44-
))?;
45-
Ok(())
46-
}
47-
4840
fn hash_database_path_with_encryption(
4941
path: &Path,
5042
encryption_opts: Option<EncryptionOpts>,
@@ -70,7 +62,7 @@ fn can_read_query_from_uri(uri: &str, query: &str) -> bool {
7062
}
7163

7264
#[derive(Clone, Copy)]
73-
enum VacuumIntoTargetLiteral {
65+
enum VacuumIntoTarget {
7466
Plain,
7567
Uri { query: &'static str },
7668
}
@@ -83,14 +75,14 @@ enum VacuumIntoDestinationSeed {
8375
ExistingDb,
8476
}
8577

86-
fn sqlite_vacuum_literal(path: &Path, target: VacuumIntoTargetLiteral) -> String {
78+
fn build_vacuum_target(path: &Path, target: VacuumIntoTarget) -> String {
8779
let path = path
8880
.to_str()
8981
.expect("test database paths must be valid UTF-8");
9082
match target {
91-
VacuumIntoTargetLiteral::Plain => path.to_string(),
92-
VacuumIntoTargetLiteral::Uri { query: "" } => format!("file:{path}"),
93-
VacuumIntoTargetLiteral::Uri { query } => format!("file:{path}?{query}"),
83+
VacuumIntoTarget::Plain => path.to_string(),
84+
VacuumIntoTarget::Uri { query: "" } => format!("file:{path}"),
85+
VacuumIntoTarget::Uri { query } => format!("file:{path}?{query}"),
9486
}
9587
}
9688

@@ -1677,13 +1669,28 @@ fn test_connection_from_uri_memory_mode_opens_in_memory_db(
16771669
Ok(())
16781670
}
16791671

1672+
#[turso_macros::test]
1673+
fn test_connection_from_uri_memory_rw_mode_opens_in_memory_db(
1674+
_tmp_db: TempDatabase,
1675+
) -> anyhow::Result<()> {
1676+
let (_io, conn) = Connection::from_uri("file::memory:?mode=rw", DatabaseOpts::new())?;
1677+
1678+
conn.execute("CREATE TABLE t(value TEXT)")?;
1679+
conn.execute("INSERT INTO t VALUES ('ok')")?;
1680+
1681+
let rows: Vec<(String,)> = conn.exec_rows("SELECT value FROM t");
1682+
assert_eq!(rows, vec![("ok".to_string(),)]);
1683+
1684+
Ok(())
1685+
}
1686+
16801687
#[turso_macros::test]
16811688
fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
16821689
_tmp_db: TempDatabase,
16831690
) -> anyhow::Result<()> {
16841691
struct Case {
16851692
name: &'static str,
1686-
target: VacuumIntoTargetLiteral,
1693+
target: VacuumIntoTarget,
16871694
seed: VacuumIntoDestinationSeed,
16881695
expected_ok: bool,
16891696
writes_file: bool,
@@ -1692,98 +1699,98 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
16921699
let cases = [
16931700
Case {
16941701
name: "plain-existing-empty",
1695-
target: VacuumIntoTargetLiteral::Plain,
1702+
target: VacuumIntoTarget::Plain,
16961703
seed: VacuumIntoDestinationSeed::EmptyFile,
16971704
expected_ok: false,
16981705
writes_file: true,
16991706
},
17001707
Case {
17011708
name: "uri-default-existing-empty",
1702-
target: VacuumIntoTargetLiteral::Uri { query: "" },
1709+
target: VacuumIntoTarget::Uri { query: "" },
17031710
seed: VacuumIntoDestinationSeed::EmptyFile,
17041711
expected_ok: true,
17051712
writes_file: true,
17061713
},
17071714
Case {
17081715
name: "uri-default-existing-db",
1709-
target: VacuumIntoTargetLiteral::Uri { query: "" },
1716+
target: VacuumIntoTarget::Uri { query: "" },
17101717
seed: VacuumIntoDestinationSeed::ExistingDb,
17111718
expected_ok: false,
17121719
writes_file: true,
17131720
},
17141721
Case {
17151722
name: "uri-default-existing-file",
1716-
target: VacuumIntoTargetLiteral::Uri { query: "" },
1723+
target: VacuumIntoTarget::Uri { query: "" },
17171724
seed: VacuumIntoDestinationSeed::ArbitraryFile,
17181725
expected_ok: false,
17191726
writes_file: true,
17201727
},
17211728
Case {
17221729
name: "uri-rw-existing-empty",
1723-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rw" },
1730+
target: VacuumIntoTarget::Uri { query: "mode=rw" },
17241731
seed: VacuumIntoDestinationSeed::EmptyFile,
17251732
expected_ok: true,
17261733
writes_file: true,
17271734
},
17281735
Case {
17291736
name: "uri-rw-existing-db",
1730-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rw" },
1737+
target: VacuumIntoTarget::Uri { query: "mode=rw" },
17311738
seed: VacuumIntoDestinationSeed::ExistingDb,
17321739
expected_ok: false,
17331740
writes_file: true,
17341741
},
17351742
Case {
17361743
name: "uri-rw-existing-file",
1737-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rw" },
1744+
target: VacuumIntoTarget::Uri { query: "mode=rw" },
17381745
seed: VacuumIntoDestinationSeed::ArbitraryFile,
17391746
expected_ok: false,
17401747
writes_file: true,
17411748
},
17421749
Case {
17431750
name: "uri-rw-missing",
1744-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rw" },
1751+
target: VacuumIntoTarget::Uri { query: "mode=rw" },
17451752
seed: VacuumIntoDestinationSeed::Missing,
17461753
expected_ok: false,
17471754
writes_file: true,
17481755
},
17491756
Case {
17501757
name: "uri-rwc-existing-empty",
1751-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rwc" },
1758+
target: VacuumIntoTarget::Uri { query: "mode=rwc" },
17521759
seed: VacuumIntoDestinationSeed::EmptyFile,
17531760
expected_ok: true,
17541761
writes_file: true,
17551762
},
17561763
Case {
17571764
name: "uri-rwc-existing-db",
1758-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rwc" },
1765+
target: VacuumIntoTarget::Uri { query: "mode=rwc" },
17591766
seed: VacuumIntoDestinationSeed::ExistingDb,
17601767
expected_ok: false,
17611768
writes_file: true,
17621769
},
17631770
Case {
17641771
name: "uri-rwc-existing-file",
1765-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rwc" },
1772+
target: VacuumIntoTarget::Uri { query: "mode=rwc" },
17661773
seed: VacuumIntoDestinationSeed::ArbitraryFile,
17671774
expected_ok: false,
17681775
writes_file: true,
17691776
},
17701777
Case {
17711778
name: "uri-rwc-missing",
1772-
target: VacuumIntoTargetLiteral::Uri { query: "mode=rwc" },
1779+
target: VacuumIntoTarget::Uri { query: "mode=rwc" },
17731780
seed: VacuumIntoDestinationSeed::Missing,
17741781
expected_ok: true,
17751782
writes_file: true,
17761783
},
17771784
Case {
17781785
name: "uri-ro-existing-empty",
1779-
target: VacuumIntoTargetLiteral::Uri { query: "mode=ro" },
1786+
target: VacuumIntoTarget::Uri { query: "mode=ro" },
17801787
seed: VacuumIntoDestinationSeed::EmptyFile,
17811788
expected_ok: false,
17821789
writes_file: true,
17831790
},
17841791
Case {
17851792
name: "uri-memory-missing",
1786-
target: VacuumIntoTargetLiteral::Uri {
1793+
target: VacuumIntoTarget::Uri {
17871794
query: "mode=memory",
17881795
},
17891796
seed: VacuumIntoDestinationSeed::Missing,
@@ -1792,7 +1799,7 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
17921799
},
17931800
Case {
17941801
name: "uri-memory-existing-file",
1795-
target: VacuumIntoTargetLiteral::Uri {
1802+
target: VacuumIntoTarget::Uri {
17961803
query: "mode=memory",
17971804
},
17981805
seed: VacuumIntoDestinationSeed::ArbitraryFile,
@@ -1814,22 +1821,32 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
18141821
.exists()
18151822
.then(|| std::fs::read(&turso_dest))
18161823
.transpose()?;
1817-
let turso_literal = sqlite_vacuum_literal(&turso_dest, case.target);
1824+
let vacuum_target = build_vacuum_target(&turso_dest, case.target);
18181825
let turso_ok = {
18191826
let turso_source_db = TempDatabase::new_with_existent(&turso_source);
18201827
let conn = turso_source_db.connect_limbo();
1821-
vacuum_into_literal(&conn, &turso_literal).is_ok()
1828+
conn.execute(format!(
1829+
"VACUUM INTO '{}'",
1830+
escape_sqlite_string_literal(&vacuum_target)
1831+
))
1832+
.is_ok()
18221833
};
18231834

18241835
assert_eq!(
18251836
turso_ok, case.expected_ok,
18261837
"{}: VACUUM INTO result should match the sqlite3 3.51 runtime for {}",
1827-
case.name, turso_literal
1838+
case.name, vacuum_target
18281839
);
18291840

18301841
if turso_ok && case.writes_file {
1831-
let source_hash = hash_database_path_with_encryption(&turso_source, None)?;
1832-
let dest_hash = hash_database_path_with_encryption(&turso_dest, None)?;
1842+
let source_hash = turso_dbhash::hash_database(
1843+
turso_source.to_str().unwrap(),
1844+
&turso_dbhash::DbHashOptions::default(),
1845+
)?;
1846+
let dest_hash = turso_dbhash::hash_database(
1847+
turso_dest.to_str().unwrap(),
1848+
&turso_dbhash::DbHashOptions::default(),
1849+
)?;
18331850
assert_eq!(
18341851
source_hash.hash, dest_hash.hash,
18351852
"{}: successful URI VACUUM should preserve logical content",
@@ -1878,7 +1895,7 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
18781895
}
18791896

18801897
#[turso_macros::test]
1881-
fn test_vacuum_into_uri_encrypts_plain_destination(tmp_db: TempDatabase) -> anyhow::Result<()> {
1898+
fn test_vacuum_into_uri_encrypts_plaintext_source(tmp_db: TempDatabase) -> anyhow::Result<()> {
18821899
let conn = tmp_db.connect_limbo();
18831900
conn.execute("CREATE TABLE secrets (id INTEGER PRIMARY KEY, value TEXT)")?;
18841901
conn.execute("INSERT INTO secrets VALUES (1, 'plain')")?;
@@ -1898,7 +1915,10 @@ fn test_vacuum_into_uri_encrypts_plain_destination(tmp_db: TempDatabase) -> anyh
18981915
);
18991916
let plain_uri = format!("file:{dest_path_str}");
19001917

1901-
vacuum_into_literal(&conn, &dest_uri)?;
1918+
conn.execute(format!(
1919+
"VACUUM INTO '{}'",
1920+
escape_sqlite_string_literal(&dest_uri)
1921+
))?;
19021922

19031923
assert!(
19041924
!can_read_query_from_uri(&plain_uri, "SELECT COUNT(*) FROM secrets"),
@@ -1948,15 +1968,21 @@ fn test_vacuum_into_uri_reencrypts_encrypted_source(_tmp_db: TempDatabase) -> an
19481968
"file:{dest_path_str}?cipher={}&hexkey={}",
19491969
dest_encryption_opts.cipher, dest_encryption_opts.hexkey
19501970
);
1951-
let source_uri = format!(
1971+
let dest_uri_with_source_encryption_params = format!(
19521972
"file:{dest_path_str}?cipher={}&hexkey={}",
19531973
source_encryption_opts.cipher, source_encryption_opts.hexkey
19541974
);
19551975

1956-
vacuum_into_literal(&conn, &dest_uri)?;
1976+
conn.execute(format!(
1977+
"VACUUM INTO '{}'",
1978+
escape_sqlite_string_literal(&dest_uri)
1979+
))?;
19571980

19581981
assert!(
1959-
!can_read_query_from_uri(&source_uri, "SELECT COUNT(*) FROM secrets"),
1982+
!can_read_query_from_uri(
1983+
&dest_uri_with_source_encryption_params,
1984+
"SELECT COUNT(*) FROM secrets"
1985+
),
19601986
"Destination should not be readable with the source encryption params"
19611987
);
19621988
assert!(
@@ -1996,14 +2022,20 @@ fn test_vacuum_into_uri_decrypts_encrypted_source(_tmp_db: TempDatabase) -> anyh
19962022
let dest_path = dest_dir.path().join("vacuum-plain.db");
19972023
let dest_uri = format!("file:{}", dest_path.to_str().unwrap());
19982024

1999-
vacuum_into_literal(&conn, &dest_uri)?;
2025+
conn.execute(format!(
2026+
"VACUUM INTO '{}'",
2027+
escape_sqlite_string_literal(&dest_uri)
2028+
))?;
20002029

20012030
assert!(
20022031
can_read_query_from_uri(&dest_uri, "SELECT COUNT(*) FROM secrets"),
20032032
"Plain VACUUM destination should be readable without encryption params"
20042033
);
20052034

2006-
let dest_hash = hash_database_path_with_encryption(&dest_path, None)?;
2035+
let dest_hash = turso_dbhash::hash_database(
2036+
dest_path.to_str().unwrap(),
2037+
&turso_dbhash::DbHashOptions::default(),
2038+
)?;
20072039
assert_eq!(
20082040
source_hash.hash, dest_hash.hash,
20092041
"Plain VACUUM destination should preserve logical content"

0 commit comments

Comments
 (0)