Skip to main content

hashtree_cli/
cashu_helper.rs

1pub 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(&current_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(&current_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}