diff --git a/Cargo.lock b/Cargo.lock index 57b1badb..2379fa85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,9 +66,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "argon2" @@ -174,9 +174,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" dependencies = [ "allocator-api2", ] @@ -195,9 +195,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "shlex", ] @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -379,6 +379,7 @@ dependencies = [ "ed25519-compact", "enum_dispatch", "hmac", + "include_dir", "itertools", "lazy_static", "nanoid", @@ -400,7 +401,7 @@ dependencies = [ "strum", "tempfile", "test-case", - "thiserror 2.0.12", + "thiserror 2.0.14", "tracing", "tracing-subscriber", "ts-rs", @@ -878,9 +879,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "group" @@ -905,9 +906,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", ] @@ -918,7 +919,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -1072,20 +1073,39 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "serde", ] @@ -1181,9 +1201,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libsqlite3-sys" @@ -1331,9 +1351,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owo-colors" -version = "4.2.1" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" [[package]] name = "p256" @@ -1552,9 +1572,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" dependencies = [ "unicode-ident", ] @@ -1596,9 +1616,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -1758,7 +1778,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "rand 0.9.1", + "rand 0.9.2", "syn 1.0.109", ] @@ -1824,15 +1844,16 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fc9767ae49274bafd3e55be9d30405a033b7a59548327d87fd4971fbb58e264" dependencies = [ + "include_dir", "log", "rusqlite", ] [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -1851,23 +1872,17 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] -[[package]] -name = "rustversion" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" - [[package]] name = "ryu" version = "1.0.20" @@ -1911,9 +1926,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ "bitflags 2.9.1", "core-foundation", @@ -1960,9 +1975,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -2030,9 +2045,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "sled" @@ -2122,23 +2137,22 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", "syn 2.0.104", ] @@ -2378,11 +2392,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.14", ] [[package]] @@ -2398,9 +2412,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", @@ -2531,7 +2545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ef1b7a6d914a34127ed8e1fa927eb7088903787bcded4fa3eef8f85ee1568be" dependencies = [ "dprint-plugin-typescript", - "thiserror 2.0.12", + "thiserror 2.0.14", "ts-rs-macros", ] @@ -2786,6 +2800,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-result" version = "0.1.2" @@ -2819,7 +2839,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -2840,10 +2860,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -2952,9 +2973,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -3084,9 +3105,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", diff --git a/Cargo.toml b/Cargo.toml index 67575152..7a8318ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,8 +95,9 @@ zeroize = { version = "1.8.1", features = ["derive"] } itertools = "0.14.0" rmp-serde = "1.3.0" security-framework-sys = { version = "2.14.0", optional = true } -rusqlite_migration = "2.3.0" +rusqlite_migration = { version = "2.3.0", features = ["from-directory"] } rusqlite = { version = "0.37.0", features = ["bundled"] } +include_dir = "0.7.4" [dev-dependencies] color-eyre = "0.6.3" diff --git a/cSpell.json b/cSpell.json index 1ff622c2..b629d652 100644 --- a/cSpell.json +++ b/cSpell.json @@ -29,6 +29,7 @@ "nmshd", "pkcs", "pwsh", + "qufiwefefwoyn", "redb", "repr", "reqwest", diff --git a/src/common/crypto/algorithms/encryption.rs b/src/common/crypto/algorithms/encryption.rs index f8264202..101b4bb2 100644 --- a/src/common/crypto/algorithms/encryption.rs +++ b/src/common/crypto/algorithms/encryption.rs @@ -111,6 +111,7 @@ impl Cipher { } } + #[allow(dead_code)] // is used in tests pub(crate) fn iv_len(&self) -> usize { match self { Self::AesCbc128 diff --git a/src/storage/storage_backend/mod.rs b/src/storage/storage_backend/mod.rs index a6a38051..70c703d2 100644 --- a/src/storage/storage_backend/mod.rs +++ b/src/storage/storage_backend/mod.rs @@ -111,7 +111,7 @@ mod test { // This results in the global db map being ineffective for unit tests. use crate::tests::TestStore; - use rstest::rstest; + use rstest::{fixture, rstest}; use std::sync::LazyLock; use tempfile::{tempdir_in, TempDir}; @@ -123,7 +123,10 @@ mod test { const TARGET_FOLDER: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/target"); - static TEST_TMP_DIR: LazyLock = LazyLock::new(|| tempdir_in(TARGET_FOLDER).unwrap()); + #[fixture] + fn temp_folder() -> TempDir { + tempdir_in(TARGET_FOLDER).unwrap() + } fn random_scoped_key() -> ScopedKey { ScopedKey { @@ -134,24 +137,34 @@ mod test { } } - fn create_kv_storage_backend() -> StorageBackendExplicit { + fn create_kv_storage_backend() -> (StorageBackendExplicit, Option) { let config = TEST_KV_STORE.impl_config(); - StorageBackendExplicit::new(&config.additional_config).unwrap() + ( + StorageBackendExplicit::new(&config.additional_config).unwrap(), + None, + ) } - fn create_file_storage_backend() -> StorageBackendExplicit { - let mut db_dir = TEST_TMP_DIR.path().to_path_buf(); + fn create_file_storage_backend() -> (StorageBackendExplicit, Option) { + let temp_folder = temp_folder(); + + let mut db_dir = temp_folder.path().to_path_buf(); db_dir.push(&nanoid!()); let additional_configs = vec![AdditionalConfig::FileStoreConfig { db_dir: db_dir.to_string_lossy().to_string(), }]; - StorageBackendExplicit::new(&additional_configs).unwrap() + ( + StorageBackendExplicit::new(&additional_configs).unwrap(), + Some(temp_folder), + ) } #[rstest] fn test_create_valid_storage_backend( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { let _ = create(); } @@ -164,16 +177,18 @@ mod test { assert!(matches!( error, StorageManagerInitializationError::MissingProviderImplConfigOption { .. } - )) + )); } #[rstest] fn test_invalid_creation_conflicting_config() { + let temp_folder = temp_folder(); + let mut provider_impl = TEST_KV_STORE.impl_config(); provider_impl .additional_config .push(AdditionalConfig::FileStoreConfig { - db_dir: TEST_TMP_DIR + db_dir: temp_folder .path() .join("test_invalid_creation_conflicting_config") .to_string_lossy() @@ -185,27 +200,34 @@ mod test { assert!(matches!( error, StorageManagerInitializationError::ConflictingProviderImplConfig { .. } - )) + )); } #[rstest] fn test_insert( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { - let storage = create(); + let (storage, _temp_dir) = create(); let scoped_key = random_scoped_key(); storage.store(scoped_key, b"TEST_DATA").unwrap(); + + drop(storage); + _temp_dir.map(|d| d.close()); } #[rstest] fn test_insert_and_get( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { - let storage = create(); + let (storage, _temp_dir) = create(); let scoped_key = random_scoped_key(); @@ -215,14 +237,19 @@ mod test { let loaded_data = storage.get(scoped_key).unwrap(); assert_eq!(data, loaded_data); + + drop(storage); + _temp_dir.map(|d| d.close()); } #[rstest] fn test_overwrite( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { - let storage = create(); + let (storage, _temp_dir) = create(); let scoped_key = random_scoped_key(); @@ -235,14 +262,19 @@ mod test { storage.store(scoped_key.clone(), &data2).unwrap(); let loaded_data_2 = storage.get(scoped_key).unwrap(); assert_eq!(data2, loaded_data_2); + + drop(storage); + _temp_dir.map(|d| d.close()); } #[rstest] fn test_fail_key_not_exists( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { - let storage = create(); + let (storage, _temp_dir) = create(); let scoped_key = random_scoped_key(); @@ -252,15 +284,20 @@ mod test { error, StorageBackendError::Sqlite(SqliteBackendError::NoKeyError) | StorageBackendError::KvStore(KvStorageBackendError::Get) - )) + )); + + drop(storage); + _temp_dir.map(|d| d.close()); } #[rstest] fn test_inserted_key_in_keys( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { - let storage = create(); + let (storage, _temp_dir) = create(); let scoped_key = random_scoped_key(); @@ -271,15 +308,20 @@ mod test { assert!(keys .into_iter() .filter_map(|r| r.ok()) - .contains(&scoped_key)) + .contains(&scoped_key)); + + drop(storage); + _temp_dir.map(|d| d.close()); } #[rstest] fn test_keys_all_ok( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { - let storage = create(); + let (storage, _temp_dir) = create(); let scoped_key = random_scoped_key(); @@ -287,15 +329,20 @@ mod test { let keys = storage.keys(); - assert!(keys.into_iter().all(|result| result.is_ok())) + assert!(keys.into_iter().all(|result| result.is_ok())); + + drop(storage); + _temp_dir.map(|d| d.close()); } #[rstest] fn test_delete( - #[values(create_file_storage_backend, create_kv_storage_backend)] - create: impl Fn() -> StorageBackendExplicit, + #[values(create_file_storage_backend, create_kv_storage_backend)] create: impl Fn() -> ( + StorageBackendExplicit, + Option, + ), ) { - let storage = create(); + let (storage, _temp_dir) = create(); let scoped_key = random_scoped_key(); @@ -314,5 +361,8 @@ mod test { StorageBackendError::Sqlite(SqliteBackendError::NoKeyError) | StorageBackendError::KvStore(KvStorageBackendError::Get) )); + + drop(storage); + _temp_dir.map(|d| d.close()); } } diff --git a/src/storage/storage_backend/sqlite_store/migrations/01-initial/up.sql b/src/storage/storage_backend/sqlite_store/migrations/01-initial/up.sql new file mode 100644 index 00000000..383aec46 --- /dev/null +++ b/src/storage/storage_backend/sqlite_store/migrations/01-initial/up.sql @@ -0,0 +1,7 @@ +CREATE TABLE keys ( + id TEXT PRIMARY KEY, + provider TEXT, + encryption_key_id TEXT, + signature_key_id TEXT, + data_blob BLOB +); diff --git a/src/storage/storage_backend/sqlite_store.rs b/src/storage/storage_backend/sqlite_store/mod.rs similarity index 62% rename from src/storage/storage_backend/sqlite_store.rs rename to src/storage/storage_backend/sqlite_store/mod.rs index 0699171f..ba82b9d5 100644 --- a/src/storage/storage_backend/sqlite_store.rs +++ b/src/storage/storage_backend/sqlite_store/mod.rs @@ -2,12 +2,13 @@ use core::fmt; use std::{ fs::create_dir_all, path::{Path, PathBuf}, - sync::{Arc, Mutex}, + sync::{Arc, LazyLock, Mutex}, }; +use include_dir::{include_dir, Dir}; use itertools::Itertools; use rusqlite::{named_params, Connection}; -use rusqlite_migration::{Migrations, M}; +use rusqlite_migration::Migrations; use thiserror::Error; use crate::storage::{ @@ -48,10 +49,11 @@ pub(in crate::storage) struct SqliteBackend { connection: Arc>, } -const MIGRATIONS_SLICE: &[M<'_>] = &[ - M::up("CREATE TABLE keys (id TEXT PRIMARY KEY, provider TEXT, encryption_key_id TEXT, signature_key_id TEXT, data_blob BLOB);"), -]; -const MIGRATIONS: Migrations<'_> = Migrations::from_slice(MIGRATIONS_SLICE); +static MIGRATIONS: LazyLock> = LazyLock::new(|| { + static MIGRATION_DIR: Dir = + include_dir!("$CARGO_MANIFEST_DIR/src/storage/storage_backend/sqlite_store/migrations"); + Migrations::from_directory(&MIGRATION_DIR).expect("Failed to parse migrations.") +}); impl SqliteBackend { pub(super) fn new(path: impl AsRef) -> Result { @@ -107,44 +109,65 @@ impl fmt::Debug for SqliteBackend { impl StorageBackend for SqliteBackend { fn store(&self, key: ScopedKey, data: &[u8]) -> Result<(), StorageBackendError> { - self.connection + let conn = self + .connection .lock() - .map_err(|_| SqliteBackendError::Acquire)? - .execute( - "INSERT INTO keys (id, provider, encryption_key_id, signature_key_id, data_blob) - VALUES (:id, :provider, :encryption_key_id, :signature_key_id, :data_blob) - ON CONFLICT(id) DO UPDATE - SET data_blob=excluded.data_blob;", - named_params! { - ":id": key.key_id, - ":provider": key.provider_scope, - ":encryption_key_id": key.encryption_scope, - ":signature_key_id": key.signature_scope, - ":data_blob": data, - }, - ) + .map_err(|_| SqliteBackendError::Acquire)?; + + // Extensions like `qufiwefefwoyn.inline-sql-syntax` for vscode are able to highlight sql. + const QUERY: &'static str = r"--sql + INSERT INTO keys (id, provider, encryption_key_id, signature_key_id, data_blob) + VALUES (:id, :provider, :encryption_key_id, :signature_key_id, :data_blob) + ON CONFLICT(id) DO + UPDATE + SET data_blob = excluded.data_blob; + "; + + let mut statement = conn + .prepare_cached(QUERY) + .map_err(|err| SqliteBackendError::SqlError(err))?; + + statement + .execute(named_params! { + ":id": key.key_id, + ":provider": key.provider_scope, + ":encryption_key_id": key.encryption_scope, + ":signature_key_id": key.signature_scope, + ":data_blob": data, + }) .map_err(|e| StorageBackendError::Sqlite(e.into()))?; + Ok(()) } fn get(&self, key: ScopedKey) -> Result, StorageBackendError> { - let query = - "SELECT data_blob FROM keys WHERE id = :id AND provider = :provider AND encryption_key_id = :encryption_key_id AND signature_key_id = :signature_key_id;"; - - let result = self + let conn = self .connection .lock() - .map_err(|_| SqliteBackendError::Acquire)? - .query_one( - query, - named_params! { - ":id": key.key_id, - ":provider": key.provider_scope, - ":encryption_key_id": key.encryption_scope, - ":signature_key_id": key.signature_scope, - }, - |row| row.get(0), - ); + .map_err(|_| SqliteBackendError::Acquire)?; + + const QUERY: &'static str = r"--sql + SELECT data_blob + FROM keys + WHERE id = :id + AND provider = :provider + AND encryption_key_id = :encryption_key_id + AND signature_key_id = :signature_key_id; + "; + + let mut statement = conn + .prepare_cached(QUERY) + .map_err(|err| SqliteBackendError::SqlError(err))?; + + let result = statement.query_one( + named_params! { + ":id": key.key_id, + ":provider": key.provider_scope, + ":encryption_key_id": key.encryption_scope, + ":signature_key_id": key.signature_scope, + }, + |row| row.get(0), + ); match result { Ok(v) => Ok(v), @@ -156,21 +179,30 @@ impl StorageBackend for SqliteBackend { } fn delete(&self, key: ScopedKey) -> Result<(), StorageBackendError> { - let query = - "DELETE FROM keys WHERE id = :id AND provider = :provider AND encryption_key_id = :encryption_key_id AND signature_key_id = :signature_key_id;"; - - self.connection + let conn = self + .connection .lock() - .map_err(|_| SqliteBackendError::Acquire)? - .execute( - query, - named_params! { - ":id": key.key_id, - ":provider": key.provider_scope, - ":encryption_key_id": key.encryption_scope, - ":signature_key_id": key.signature_scope, - }, - ) + .map_err(|_| SqliteBackendError::Acquire)?; + + const QUERY: &'static str = r"--sql + DELETE FROM keys + WHERE id = :id + AND provider = :provider + AND encryption_key_id = :encryption_key_id + AND signature_key_id = :signature_key_id; + "; + + let mut statement = conn + .prepare_cached(QUERY) + .map_err(|err| SqliteBackendError::SqlError(err))?; + + statement + .execute(named_params! { + ":id": key.key_id, + ":provider": key.provider_scope, + ":encryption_key_id": key.encryption_scope, + ":signature_key_id": key.signature_scope, + }) .map_err(|e| StorageBackendError::Sqlite(SqliteBackendError::SqlError(e)))?; Ok(()) } @@ -181,11 +213,19 @@ impl StorageBackend for SqliteBackend { Err(_) => return vec![Err(SqliteBackendError::Acquire.into())], }; - let query = "SELECT id, provider, encryption_key_id, signature_key_id FROM keys;"; - let statement = conn.prepare(query); + const QUERY: &'static str = r"--sql + SELECT id, + provider, + encryption_key_id, + signature_key_id + FROM keys; + "; + + let statement = conn.prepare_cached(QUERY); + match statement { Err(e) => { - return vec![Err(StorageBackendError::Sqlite( + vec![Err(StorageBackendError::Sqlite( SqliteBackendError::SqlError(e), ))] } @@ -222,25 +262,28 @@ fn flatten_res(res: Result, E>) -> Result { #[cfg(test)] mod test { - use std::sync::LazyLock; - use nanoid::nanoid; + use rstest::{fixture, rstest}; use tempfile::{tempdir_in, TempDir}; use super::*; const TARGET_FOLDER: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/target"); - static TEST_TMP_DIR: LazyLock = LazyLock::new(|| tempdir_in(TARGET_FOLDER).unwrap()); - #[test] - fn test_file_store_creation() { - let _storage = SqliteBackend::new(TEST_TMP_DIR.path().join("test_file_store_creation")) + #[fixture] + fn temp_folder() -> TempDir { + tempdir_in(TARGET_FOLDER).unwrap() + } + + #[rstest] + fn test_file_store_creation(temp_folder: TempDir) { + let _storage = SqliteBackend::new(temp_folder.path().join("test_file_store_creation")) .expect("Failed to create a file store"); } - #[test] - fn test_multi_file_store_creation_same_file() { - let db_dir = TEST_TMP_DIR + #[rstest] + fn test_multi_file_store_creation_same_file(temp_folder: TempDir) { + let db_dir = temp_folder .path() .join("test_multi_file_store_creation_same_file"); @@ -262,5 +305,10 @@ mod test { let fetched_data = store2.get(key).unwrap(); assert_eq!(&fetched_data, data); + + drop(store1); + drop(store2); + + // std::fs::remove_dir_all(TEST_TMP_DIR.path()).unwrap(); } } diff --git a/ts-types/package-lock.json b/ts-types/package-lock.json index 360499a4..99e38a43 100644 --- a/ts-types/package-lock.json +++ b/ts-types/package-lock.json @@ -182,6 +182,22 @@ "node": ">=14.21.3" } }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.0.tgz", + "integrity": "sha512-5v3YXc5ZMfL6OJqXPrX9csb4l7NlQA2doO1yynUjpUChT9hg4JcuBVP0RbsEJ/3SL/sxWEyFjT2W69ZhtoBWqg==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.6.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + } + }, "node_modules/@samchon/openapi": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@samchon/openapi/-/openapi-3.3.0.tgz", @@ -189,10 +205,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.17.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", - "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", - "dev": true, + "version": "22.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz", + "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -315,9 +330,9 @@ } }, "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", "license": "MIT" }, "node_modules/cli-cursor": { @@ -460,20 +475,6 @@ "node": ">=4" } }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -546,12 +547,12 @@ } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -594,16 +595,16 @@ } }, "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", "license": "MIT", "dependencies": { + "@inquirer/external-editor": "^1.0.0", "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", - "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", @@ -1010,15 +1011,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, - "node_modules/tmp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", - "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, "node_modules/ts-patch": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/ts-patch/-/ts-patch-3.3.0.tgz", @@ -1094,7 +1086,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/util-deprecate": { diff --git a/ts-types/package.json b/ts-types/package.json index c2a93eef..e595f8d2 100644 --- a/ts-types/package.json +++ b/ts-types/package.json @@ -29,8 +29,5 @@ }, "dependencies": { "typia": "^8.0.3" - }, - "overrides": { - "tmp": "^0.2.4" } }