fedimint_recurringd_tests/
fedimint-recurringd-tests.rs1use std::ops::ControlFlow;
2
3use devimint::tests::log_binary_versions;
4use devimint::util::{FedimintCli, almost_equal, poll};
5use devimint::version_constants::VERSION_0_12_0_ALPHA;
6use devimint::{DevFed, cmd};
7use tracing::info;
8
9#[tokio::main]
10async fn main() -> anyhow::Result<()> {
11 devimint::run_devfed_test()
12 .call(|dev_fed, process_mgr| async move {
13 log_binary_versions().await?;
14
15 let DevFed {
16 fed,
17 gw_lnd,
18 gw_ldk_second,
19 recurringd,
20 ..
21 } = dev_fed.to_dev_fed(&process_mgr).await?;
22
23 {
25 let dummy_invite = "fed114znk7uk7ppugdjuytr8venqf2tkywd65cqvg3u93um64tu5cw4yr0n3fvn7qmwvm4g48cpndgnm4gqq4waen5te0xyerwt3s9cczuvf6xyurzde597s7crdvsk2vmyarjw9gwyqjdzj";
26 let url = format!("{}lnv1/federations", recurringd.api_url);
27 let client = reqwest::Client::new();
28 let response_no_auth = client
29 .put(&url)
30 .header("Content-Type", "application/json")
31 .json(&serde_json::json!({ "invite": dummy_invite }))
32 .send()
33 .await?;
34 assert_eq!(
35 response_no_auth.status(),
36 reqwest::StatusCode::UNAUTHORIZED
37 );
38
39 let response_with_wrong_auth = client
40 .put(&url)
41 .header("Authorization", "Bearer recurringd-test-token-wrong")
42 .header("Content-Type", "application/json")
43 .json(&serde_json::json!({ "invite": dummy_invite }))
44 .send()
45 .await?;
46 assert_eq!(
47 response_with_wrong_auth.status(),
48 reqwest::StatusCode::UNAUTHORIZED
49 );
50 }
51
52 fed.pegin_gateways(10_000_000, vec![&gw_lnd]).await?;
55
56 let client = fed.new_joined_client("recurringd-test-client").await?;
57
58 let lnurl = cmd!(
59 client,
60 "module",
61 "ln",
62 "lnurl",
63 "register",
64 recurringd.api_url()
65 )
66 .out_json()
67 .await?["lnurl"]
68 .as_str()
69 .unwrap()
70 .to_owned();
71
72 let lnurl_list = cmd!(client, "module", "ln", "lnurl", "list")
73 .out_json()
74 .await?["codes"]
75 .as_object()
76 .unwrap()
77 .clone();
78
79 assert_eq!(lnurl_list.len(), 1);
80
81 let listed_lnurl = lnurl_list["0"].clone();
82 assert_eq!(listed_lnurl["lnurl"].as_str().unwrap(), &lnurl);
83 assert_eq!(listed_lnurl["last_derivation_index"].as_i64().unwrap(), 0);
84
85 let url = fedimint_lnurl::parse_lnurl(&lnurl).expect("valid lnurl");
86 let pay_response = fedimint_lnurl::request(&url).await.expect("pay request");
87 let invoice_response = fedimint_lnurl::get_invoice(&pay_response, 1_000_000)
88 .await
89 .expect("invoice request");
90 gw_ldk_second.client().pay_invoice(invoice_response.pr.clone()).await?;
91
92 let invoice_op_id = poll("lnurl_receive", || async {
93 cmd!(client, "dev", "wait", "2")
94 .run()
95 .await
96 .map_err(ControlFlow::Break)?;
97
98 let invoice_list = cmd!(client, "module", "ln", "lnurl", "invoices", "0")
99 .out_json()
100 .await
101 .map_err(ControlFlow::Break)?["invoices"]
102 .as_object()
103 .unwrap()
104 .clone();
105
106 if invoice_list.is_empty() {
107 return Err(ControlFlow::Continue(anyhow::anyhow!(
108 "No invoice recognized yet"
109 )));
110 }
111
112 Ok(invoice_list["1"]["operation_id"]
113 .as_str()
114 .unwrap()
115 .to_owned())
116 })
117 .await?;
118
119 let await_invoice_result = cmd!(
120 client,
121 "module",
122 "ln",
123 "lnurl",
124 "await-invoice-paid",
125 invoice_op_id
126 )
127 .out_json()
128 .await?;
129
130 assert_eq!(
131 await_invoice_result["amount_msat"].as_i64().unwrap(),
132 1_000_000
133 );
134 assert_eq!(
135 await_invoice_result["invoice"].as_str().unwrap(),
136 &invoice_response.pr.to_string()
137 );
138
139 let client_balance = client.balance().await?;
140 almost_equal(client_balance, 1_000_000, 5_000).unwrap();
141 info!("Client balance: {client_balance}");
142
143 if FedimintCli::version_or_default().await >= *VERSION_0_12_0_ALPHA {
151 let drain_receiver = fed
152 .new_joined_client("recurringd-test-drain-receiver")
153 .await?;
154 let drain_lnurl = cmd!(
155 drain_receiver,
156 "module",
157 "ln",
158 "lnurl",
159 "register",
160 recurringd.api_url()
161 )
162 .out_json()
163 .await?["lnurl"]
164 .as_str()
165 .unwrap()
166 .to_owned();
167
168 let balance_before_drain = client.balance().await?;
169 let drain_outcome = cmd!(
170 client,
171 "module",
172 "ln",
173 "pay",
174 &drain_lnurl,
175 "--all",
176 "--gateway-id",
177 &gw_lnd.gateway_id,
178 )
179 .out_json()
180 .await?;
181 assert!(
184 drain_outcome.get("Success").is_some(),
185 "draining the balance with `--all` should succeed, got: {drain_outcome}"
186 );
187
188 let balance_after_drain = client.balance().await?;
189 info!(
190 "Client balance after `--all` drain: {balance_after_drain} (was {balance_before_drain})"
191 );
192 assert!(
195 balance_after_drain < 20_000,
196 "`pay --all` should have drained (nearly) the whole balance, but {balance_after_drain} of {balance_before_drain} msats remain"
197 );
198 } else {
199 info!("Skipping `pay --all` drain test, fedimint-cli is too old");
200 }
201
202 Ok(())
203 })
204 .await
205}