hashtree_cli/
cashu_helper.rs1pub use cashu_service::{
2 base_helper_args, helper_binary_name, helper_binary_path, run_helper_status, CashuHelperClient,
3 CashuMintBalance, CashuPaymentClient, CashuReceivedPayment, CashuSentPayment, CARGO_HELPER_ENV,
4 CASHU_HELPER_ENV,
5};
6
7#[cfg(test)]
8mod tests {
9 use super::*;
10 use serde_json::json;
11 use std::env;
12 #[cfg(unix)]
13 use std::os::unix::fs::PermissionsExt;
14 use std::path::Path;
15 use std::sync::{Mutex, OnceLock};
16
17 fn env_lock() -> &'static Mutex<()> {
18 static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
19 LOCK.get_or_init(|| Mutex::new(()))
20 }
21
22 #[test]
23 fn test_helper_binary_path_prefers_env_override() {
24 let _guard = env_lock().lock().unwrap_or_else(|err| err.into_inner());
25 let temp_dir = tempfile::tempdir().unwrap();
26 let override_path = temp_dir.path().join("custom-helper");
27 std::fs::write(&override_path, b"").unwrap();
28
29 env::set_var(CASHU_HELPER_ENV, &override_path);
30 env::remove_var(CARGO_HELPER_ENV);
31
32 let resolved = helper_binary_path(Path::new("/tmp/htree")).unwrap();
33 assert_eq!(resolved, override_path);
34
35 env::remove_var(CASHU_HELPER_ENV);
36 }
37
38 #[test]
39 fn test_helper_binary_path_falls_back_to_sibling_binary() {
40 let _guard = env_lock().lock().unwrap_or_else(|err| err.into_inner());
41 env::remove_var(CASHU_HELPER_ENV);
42 env::remove_var(CARGO_HELPER_ENV);
43
44 let temp_dir = tempfile::tempdir().unwrap();
45 let current_exe = temp_dir.path().join("htree");
46 std::fs::write(¤t_exe, b"").unwrap();
47 let sibling = temp_dir.path().join(helper_binary_name());
48 std::fs::write(&sibling, b"").unwrap();
49
50 let resolved = helper_binary_path(¤t_exe).unwrap();
51 assert_eq!(resolved, sibling);
52 }
53
54 #[cfg(unix)]
55 #[tokio::test]
56 #[allow(clippy::await_holding_lock)]
57 async fn test_cashu_helper_client_send_and_receive_json() {
58 let _guard = env_lock().lock().unwrap_or_else(|err| err.into_inner());
59 env::remove_var(CARGO_HELPER_ENV);
60
61 let temp_dir = tempfile::tempdir().unwrap();
62 let helper_path = temp_dir.path().join("htree-cashu-stub");
63 let script = format!(
64 "#!/bin/sh\nif [ \"$3\" = \"internal\" ] && [ \"$4\" = \"send\" ]; then\n printf '%s' '{}'\nelif [ \"$3\" = \"internal\" ] && [ \"$4\" = \"receive\" ]; then\n cat >/dev/null\n printf '%s' '{}'\nelse\n printf '%s' '{}'\nfi\n",
65 json!({
66 "mint_url": "https://mint.example",
67 "unit": "sat",
68 "amount_sat": 3,
69 "send_fee_sat": 1,
70 "operation_id": "op-123",
71 "token": "cashuBtoken"
72 }),
73 json!({
74 "mint_url": "https://mint.example",
75 "unit": "sat",
76 "amount_sat": 3
77 }),
78 json!({"ok": true}),
79 );
80 std::fs::write(&helper_path, script).unwrap();
81 let mut perms = std::fs::metadata(&helper_path).unwrap().permissions();
82 perms.set_mode(0o755);
83 std::fs::set_permissions(&helper_path, perms).unwrap();
84
85 env::set_var(CASHU_HELPER_ENV, &helper_path);
86 let client = CashuHelperClient::discover(temp_dir.path()).unwrap();
87
88 let sent = client
89 .send_payment("https://mint.example", 3)
90 .await
91 .unwrap();
92 assert_eq!(sent.amount_sat, 3);
93 assert_eq!(sent.send_fee_sat, 1);
94 assert_eq!(sent.operation_id, "op-123");
95
96 let received = client.receive_payment("cashuBtoken").await.unwrap();
97 assert_eq!(received.amount_sat, 3);
98 assert_eq!(received.mint_url, "https://mint.example");
99
100 client
101 .revoke_payment("https://mint.example", "op-123")
102 .await
103 .unwrap();
104
105 env::remove_var(CASHU_HELPER_ENV);
106 }
107
108 #[cfg(unix)]
109 #[tokio::test]
110 #[allow(clippy::await_holding_lock)]
111 async fn test_cashu_helper_client_queries_mint_balance_json() {
112 let _guard = env_lock().lock().unwrap_or_else(|err| err.into_inner());
113 env::remove_var(CARGO_HELPER_ENV);
114
115 let temp_dir = tempfile::tempdir().unwrap();
116 let helper_path = temp_dir.path().join("htree-cashu-stub");
117 let script = format!(
118 "#!/bin/sh\nif [ \"$3\" = \"internal\" ] && [ \"$4\" = \"balance\" ]; then\n printf '%s' '{}'\nelse\n printf '%s' '{}'\nfi\n",
119 json!({
120 "mint_url": "https://mint.example",
121 "unit": "sat",
122 "balance_sat": 21
123 }),
124 json!({"ok": true}),
125 );
126 std::fs::write(&helper_path, script).unwrap();
127 let mut perms = std::fs::metadata(&helper_path).unwrap().permissions();
128 perms.set_mode(0o755);
129 std::fs::set_permissions(&helper_path, perms).unwrap();
130
131 env::set_var(CASHU_HELPER_ENV, &helper_path);
132 let client = CashuHelperClient::discover(temp_dir.path()).unwrap();
133 let balance = client.mint_balance("https://mint.example").await.unwrap();
134 assert_eq!(balance.mint_url, "https://mint.example");
135 assert_eq!(balance.unit, "sat");
136 assert_eq!(balance.balance_sat, 21);
137
138 env::remove_var(CASHU_HELPER_ENV);
139 }
140}