1use cdk_sql_common::mint::SQLMintAuthDatabase;
4use cdk_sql_common::SQLMintDatabase;
5
6use crate::common::SqliteConnectionManager;
7
8pub mod memory;
9
10pub type MintSqliteDatabase = SQLMintDatabase<SqliteConnectionManager>;
12
13pub type MintSqliteAuthDatabase = SQLMintAuthDatabase<SqliteConnectionManager>;
15
16#[cfg(test)]
17mod test {
18 use std::fs::remove_file;
19 use std::str::FromStr;
20 use std::sync::Arc;
21 use std::time::Duration;
22
23 use cdk_common::database::{self, MintAuthDatabase};
24 use cdk_common::secret::Secret;
25 use cdk_common::{mint_db_test, AuthProof, Id, SecretKey, State};
26 use cdk_sql_common::database::ConnectionWithTransaction;
27 use cdk_sql_common::pool::Pool;
28 use cdk_sql_common::stmt::query;
29
30 use super::*;
31 use crate::common::Config;
32
33 async fn provide_db(_test_name: String) -> MintSqliteDatabase {
34 memory::empty().await.unwrap()
35 }
36
37 mint_db_test!(provide_db);
38
39 #[tokio::test]
40 async fn kvstore_compare_and_swap() {
41 cdk_common::database::mint::test::kvstore_compare_and_swap(
42 provide_db("test_kvstore_compare_and_swap".to_owned()).await,
43 )
44 .await;
45 }
46
47 #[tokio::test]
48 async fn bug_opening_relative_path() {
49 let config: Config = "test.db".into();
50
51 let pool = Pool::<SqliteConnectionManager>::new(config);
52 let db = pool.get().await;
53 assert!(db.is_ok());
54 let _ = remove_file("test.db");
55 }
56
57 #[tokio::test]
58 async fn exhausted_in_memory_pool_times_out() {
59 let config: Config = ":memory:".into();
60 let pool = Pool::<SqliteConnectionManager>::new(config);
61
62 let _conn = pool.get().await.expect("valid connection");
63 let result = pool.get_timeout(Duration::from_millis(10)).await;
64
65 assert!(matches!(result, Err(cdk_sql_common::pool::Error::Timeout)));
66 }
67
68 #[tokio::test]
69 async fn cloned_config_creates_independent_in_memory_databases() {
70 let config: Config = ":memory:".into();
71 let _first = MintSqliteDatabase::new(config.clone())
72 .await
73 .expect("first database");
74 let _second = MintSqliteDatabase::new(config)
75 .await
76 .expect("independent second database");
77 }
78
79 #[test]
80 fn discarded_in_memory_connection_is_not_replaced() {
81 let config: Config = ":memory:".into();
82 let pool = Pool::<SqliteConnectionManager>::new(config);
83 let runtime = || {
84 tokio::runtime::Builder::new_current_thread()
85 .enable_all()
86 .build()
87 .expect("runtime")
88 };
89
90 let first = runtime();
93 first.block_on(async {
94 let conn = pool.get().await.expect("valid connection");
95 let tx = ConnectionWithTransaction::new(conn)
96 .await
97 .expect("transaction");
98 drop(tx);
99 });
100 drop(first);
101
102 runtime().block_on(async {
103 assert!(matches!(
104 pool.get().await,
105 Err(cdk_sql_common::pool::Error::Resource(_))
106 ));
107 });
108 }
109
110 async fn spend_auth_proof(
111 db: Arc<MintSqliteAuthDatabase>,
112 proof: AuthProof,
113 ) -> Result<(), database::Error> {
114 let mut tx = db.as_ref().begin_transaction().await?;
115 tx.add_proof(proof).await?;
116 tx.commit().await
117 }
118
119 #[tokio::test]
120 async fn concurrent_blind_auth_proof_spend_allows_one_request() {
121 let path = std::env::temp_dir().join(format!(
122 "cdk-blind-auth-replay-{}.sqlite",
123 uuid::Uuid::new_v4()
124 ));
125
126 #[cfg(not(feature = "sqlcipher"))]
127 let db = Arc::new(
128 MintSqliteAuthDatabase::new(&path)
129 .await
130 .expect("auth database"),
131 );
132 #[cfg(feature = "sqlcipher")]
133 let db = Arc::new(
134 MintSqliteAuthDatabase::new((path.clone(), "test".to_owned()))
135 .await
136 .expect("auth database"),
137 );
138
139 let proof = AuthProof {
140 keyset_id: Id::from_str("00916bbf7ef91a36").expect("valid keyset id"),
141 secret: Secret::generate(),
142 c: SecretKey::generate().public_key(),
143 dleq: None,
144 };
145 let y = proof.y().expect("proof y");
146
147 let (first, second) = tokio::join!(
148 spend_auth_proof(db.clone(), proof.clone()),
149 spend_auth_proof(db.clone(), proof)
150 );
151
152 assert!(matches!(
153 (&first, &second),
154 (Ok(()), Err(database::Error::Duplicate)) | (Err(database::Error::Duplicate), Ok(()))
155 ));
156 assert_eq!(
157 db.get_proofs_states(&[y]).await.expect("proof state"),
158 vec![Some(State::Spent)]
159 );
160
161 drop(db);
162 remove_file(path).expect("remove auth database");
163 }
164
165 #[tokio::test]
166 async fn open_legacy_and_migrate() {
167 let file = format!(
168 "{}/db.sqlite",
169 std::env::temp_dir().to_str().unwrap_or_default()
170 );
171
172 {
173 let _ = remove_file(&file);
174 #[cfg(not(feature = "sqlcipher"))]
175 let config: Config = file.as_str().into();
176 #[cfg(feature = "sqlcipher")]
177 let config: Config = (file.as_str(), "test").into();
178
179 let pool = Pool::<SqliteConnectionManager>::new(config);
180
181 let conn = pool.get().await.expect("valid connection");
182
183 query(include_str!("../../tests/legacy-sqlx.sql"))
184 .expect("query")
185 .execute(&*conn)
186 .await
187 .expect("create former db failed");
188 }
189
190 #[cfg(not(feature = "sqlcipher"))]
191 let conn = MintSqliteDatabase::new(file.as_str()).await;
192
193 #[cfg(feature = "sqlcipher")]
194 let conn = MintSqliteDatabase::new((file.as_str(), "test")).await;
195
196 assert!(conn.is_ok(), "Failed with {:?}", conn.unwrap_err());
197
198 let _ = remove_file(&file);
199 }
200}