1#![warn(clippy::unused_async)]
6
7pub mod database;
8pub mod error;
9pub mod multi_mint_wallet;
10#[cfg(feature = "postgres")]
11pub mod postgres;
12pub mod sqlite;
13pub mod token;
14pub mod types;
15pub mod wallet;
16
17pub use database::*;
18pub use error::*;
19pub use multi_mint_wallet::*;
20pub use types::*;
21pub use wallet::*;
22
23uniffi::setup_scaffolding!();
24
25#[cfg(test)]
26mod tests {
27 use super::*;
28
29 #[test]
30 fn test_amount_conversion() {
31 let amount = Amount::new(1000);
32 assert_eq!(amount.value, 1000);
33 assert!(!amount.is_zero());
34
35 let zero = Amount::zero();
36 assert!(zero.is_zero());
37 }
38
39 #[test]
40 fn test_currency_unit_conversion() {
41 use cdk::nuts::CurrencyUnit as CdkCurrencyUnit;
42
43 let unit = CurrencyUnit::Sat;
44 let cdk_unit: CdkCurrencyUnit = unit.into();
45 let back: CurrencyUnit = cdk_unit.into();
46 assert_eq!(back, CurrencyUnit::Sat);
47 }
48
49 #[test]
50 fn test_mint_url_creation() {
51 let url = MintUrl::new("https://mint.example.com".to_string());
52 assert!(url.is_ok());
53
54 let invalid_url = MintUrl::new("not-a-url".to_string());
55 assert!(invalid_url.is_err());
56 }
57
58 #[test]
59 fn test_send_options_default() {
60 let options = SendOptions::default();
61 assert!(options.memo.is_none());
62 assert!(options.conditions.is_none());
63 assert!(matches!(options.amount_split_target, SplitTarget::None));
64 assert!(matches!(options.send_kind, SendKind::OnlineExact));
65 assert!(!options.include_fee);
66 assert!(options.max_proofs.is_none());
67 assert!(options.metadata.is_empty());
68 }
69
70 #[test]
71 fn test_receive_options_default() {
72 let options = ReceiveOptions::default();
73 assert!(matches!(options.amount_split_target, SplitTarget::None));
74 assert!(options.p2pk_signing_keys.is_empty());
75 assert!(options.preimages.is_empty());
76 assert!(options.metadata.is_empty());
77 }
78
79 #[test]
80 fn test_send_memo() {
81 let memo_text = "Test memo".to_string();
82 let memo = SendMemo {
83 memo: memo_text.clone(),
84 include_memo: true,
85 };
86
87 assert_eq!(memo.memo, memo_text);
88 assert!(memo.include_memo);
89 }
90
91 #[test]
92 fn test_split_target_variants() {
93 let split_none = SplitTarget::None;
94 assert!(matches!(split_none, SplitTarget::None));
95
96 let amount = Amount::new(1000);
97 let split_value = SplitTarget::Value { amount };
98 assert!(matches!(split_value, SplitTarget::Value { .. }));
99
100 let amounts = vec![Amount::new(100), Amount::new(200)];
101 let split_values = SplitTarget::Values { amounts };
102 assert!(matches!(split_values, SplitTarget::Values { .. }));
103 }
104
105 #[test]
106 fn test_send_kind_variants() {
107 let online_exact = SendKind::OnlineExact;
108 assert!(matches!(online_exact, SendKind::OnlineExact));
109
110 let tolerance = Amount::new(50);
111 let online_tolerance = SendKind::OnlineTolerance { tolerance };
112 assert!(matches!(online_tolerance, SendKind::OnlineTolerance { .. }));
113
114 let offline_exact = SendKind::OfflineExact;
115 assert!(matches!(offline_exact, SendKind::OfflineExact));
116
117 let offline_tolerance = SendKind::OfflineTolerance { tolerance };
118 assert!(matches!(
119 offline_tolerance,
120 SendKind::OfflineTolerance { .. }
121 ));
122 }
123
124 #[test]
125 fn test_secret_key_from_hex() {
126 let valid_hex = "a".repeat(64);
128 let secret_key = SecretKey::from_hex(valid_hex.clone());
129 assert!(secret_key.is_ok());
130 assert_eq!(secret_key.unwrap().hex, valid_hex);
131
132 let invalid_length = "a".repeat(32); let secret_key = SecretKey::from_hex(invalid_length);
135 assert!(secret_key.is_err());
136
137 let invalid_chars = "g".repeat(64); let secret_key = SecretKey::from_hex(invalid_chars);
140 assert!(secret_key.is_err());
141 }
142
143 #[test]
144 fn test_secret_key_random() {
145 let key1 = SecretKey::random();
146 let key2 = SecretKey::random();
147
148 assert_ne!(key1.hex, key2.hex);
150
151 assert_eq!(key1.hex.len(), 64);
153 assert_eq!(key2.hex.len(), 64);
154 assert!(key1.hex.chars().all(|c| c.is_ascii_hexdigit()));
155 assert!(key2.hex.chars().all(|c| c.is_ascii_hexdigit()));
156 }
157
158 #[test]
159 fn test_send_options_with_all_fields() {
160 use std::collections::HashMap;
161
162 let memo = SendMemo {
163 memo: "Test memo".to_string(),
164 include_memo: true,
165 };
166
167 let mut metadata = HashMap::new();
168 metadata.insert("key1".to_string(), "value1".to_string());
169
170 let conditions = SpendingConditions::P2PK {
171 pubkey: "02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc"
172 .to_string(),
173 conditions: None,
174 };
175
176 let options = SendOptions {
177 memo: Some(memo),
178 conditions: Some(conditions),
179 amount_split_target: SplitTarget::Value {
180 amount: Amount::new(1000),
181 },
182 send_kind: SendKind::OnlineTolerance {
183 tolerance: Amount::new(50),
184 },
185 include_fee: true,
186 max_proofs: Some(10),
187 metadata,
188 };
189
190 assert!(options.memo.is_some());
191 assert!(options.conditions.is_some());
192 assert!(matches!(
193 options.amount_split_target,
194 SplitTarget::Value { .. }
195 ));
196 assert!(matches!(
197 options.send_kind,
198 SendKind::OnlineTolerance { .. }
199 ));
200 assert!(options.include_fee);
201 assert_eq!(options.max_proofs, Some(10));
202 assert!(!options.metadata.is_empty());
203 }
204
205 #[test]
206 fn test_receive_options_with_all_fields() {
207 use std::collections::HashMap;
208
209 let secret_key = SecretKey::random();
210 let mut metadata = HashMap::new();
211 metadata.insert("key1".to_string(), "value1".to_string());
212
213 let options = ReceiveOptions {
214 amount_split_target: SplitTarget::Values {
215 amounts: vec![Amount::new(100), Amount::new(200)],
216 },
217 p2pk_signing_keys: vec![secret_key],
218 preimages: vec!["preimage1".to_string(), "preimage2".to_string()],
219 metadata,
220 };
221
222 assert!(matches!(
223 options.amount_split_target,
224 SplitTarget::Values { .. }
225 ));
226 assert_eq!(options.p2pk_signing_keys.len(), 1);
227 assert_eq!(options.preimages.len(), 2);
228 assert!(!options.metadata.is_empty());
229 }
230
231 #[test]
232 fn test_wallet_config() {
233 let config = WalletConfig {
234 target_proof_count: None,
235 };
236 assert!(config.target_proof_count.is_none());
237
238 let config_with_values = WalletConfig {
239 target_proof_count: Some(5),
240 };
241 assert_eq!(config_with_values.target_proof_count, Some(5));
242 }
243
244 #[test]
245 fn test_mnemonic_generation() {
246 let mnemonic = generate_mnemonic().unwrap();
248 assert!(!mnemonic.is_empty());
249 assert_eq!(mnemonic.split_whitespace().count(), 12);
250
251 use bip39::Mnemonic;
253 let parsed = Mnemonic::parse(&mnemonic);
254 assert!(parsed.is_ok());
255 }
256
257 #[test]
258 fn test_mnemonic_validation() {
259 let mnemonic = generate_mnemonic().unwrap();
261 use bip39::Mnemonic;
262 let parsed = Mnemonic::parse(&mnemonic);
263 assert!(parsed.is_ok());
264
265 let invalid_mnemonic = "invalid mnemonic phrase that should not work";
267 let parsed_invalid = Mnemonic::parse(invalid_mnemonic);
268 assert!(parsed_invalid.is_err());
269
270 let mnemonic_12 = generate_mnemonic().unwrap();
272 assert_eq!(mnemonic_12.split_whitespace().count(), 12);
273 }
274
275 #[test]
276 fn test_mnemonic_to_entropy() {
277 let mnemonic = generate_mnemonic().unwrap();
279 let entropy = mnemonic_to_entropy(mnemonic.clone()).unwrap();
280
281 assert_eq!(entropy.len(), 16);
283
284 use bip39::Mnemonic;
286 let recreated_mnemonic = Mnemonic::from_entropy(&entropy).unwrap();
287 assert_eq!(recreated_mnemonic.to_string(), mnemonic);
288
289 let invalid_result = mnemonic_to_entropy("invalid mnemonic".to_string());
291 assert!(invalid_result.is_err());
292 }
293}