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!(response_no_auth.status().is_client_error());
35
36 let response_with_wrong_auth = client
37 .put(&url)
38 .header("Authorization", "Bearer wrong-token")
39 .header("Content-Type", "application/json")
40 .json(&serde_json::json!({ "invite": dummy_invite }))
41 .send()
42 .await?;
43 assert!(response_with_wrong_auth.status().is_client_error());
44 }
45
46 fed.pegin_gateways(10_000_000, vec![&gw_lnd]).await?;
49
50 let client = fed.new_joined_client("recurringd-test-client").await?;
51
52 let lnurl = cmd!(
53 client,
54 "module",
55 "ln",
56 "lnurl",
57 "register",
58 recurringd.api_url()
59 )
60 .out_json()
61 .await?["lnurl"]
62 .as_str()
63 .unwrap()
64 .to_owned();
65
66 let lnurl_list = cmd!(client, "module", "ln", "lnurl", "list")
67 .out_json()
68 .await?["codes"]
69 .as_object()
70 .unwrap()
71 .clone();
72
73 assert_eq!(lnurl_list.len(), 1);
74
75 let listed_lnurl = lnurl_list["0"].clone();
76 assert_eq!(listed_lnurl["lnurl"].as_str().unwrap(), &lnurl);
77 assert_eq!(listed_lnurl["last_derivation_index"].as_i64().unwrap(), 0);
78
79 let url = fedimint_lnurl::parse_lnurl(&lnurl).expect("valid lnurl");
80 let pay_response = fedimint_lnurl::request(&url).await.expect("pay request");
81 let invoice_response = fedimint_lnurl::get_invoice(&pay_response, 1_000_000)
82 .await
83 .expect("invoice request");
84 gw_ldk_second.client().pay_invoice(invoice_response.pr.clone()).await?;
85
86 let invoice_op_id = poll("lnurl_receive", || async {
87 cmd!(client, "dev", "wait", "2")
88 .run()
89 .await
90 .map_err(ControlFlow::Break)?;
91
92 let invoice_list = cmd!(client, "module", "ln", "lnurl", "invoices", "0")
93 .out_json()
94 .await
95 .map_err(ControlFlow::Break)?["invoices"]
96 .as_object()
97 .unwrap()
98 .clone();
99
100 if invoice_list.is_empty() {
101 return Err(ControlFlow::Continue(anyhow::anyhow!(
102 "No invoice recognized yet"
103 )));
104 }
105
106 Ok(invoice_list["1"]["operation_id"]
107 .as_str()
108 .unwrap()
109 .to_owned())
110 })
111 .await?;
112
113 let await_invoice_result = cmd!(
114 client,
115 "module",
116 "ln",
117 "lnurl",
118 "await-invoice-paid",
119 invoice_op_id
120 )
121 .out_json()
122 .await?;
123
124 assert_eq!(
125 await_invoice_result["amount_msat"].as_i64().unwrap(),
126 1_000_000
127 );
128 assert_eq!(
129 await_invoice_result["invoice"].as_str().unwrap(),
130 &invoice_response.pr.to_string()
131 );
132
133 let client_balance = client.balance().await?;
134 almost_equal(client_balance, 1_000_000, 5_000).unwrap();
135 info!("Client balance: {client_balance}");
136
137 if FedimintCli::version_or_default().await >= *VERSION_0_12_0_ALPHA {
145 let drain_receiver = fed
146 .new_joined_client("recurringd-test-drain-receiver")
147 .await?;
148 let drain_lnurl = cmd!(
149 drain_receiver,
150 "module",
151 "ln",
152 "lnurl",
153 "register",
154 recurringd.api_url()
155 )
156 .out_json()
157 .await?["lnurl"]
158 .as_str()
159 .unwrap()
160 .to_owned();
161
162 let balance_before_drain = client.balance().await?;
163 let drain_outcome = cmd!(
164 client,
165 "module",
166 "ln",
167 "pay",
168 &drain_lnurl,
169 "--all",
170 "--gateway-id",
171 &gw_lnd.gateway_id,
172 )
173 .out_json()
174 .await?;
175 assert!(
178 drain_outcome.get("Success").is_some(),
179 "draining the balance with `--all` should succeed, got: {drain_outcome}"
180 );
181
182 let balance_after_drain = client.balance().await?;
183 info!(
184 "Client balance after `--all` drain: {balance_after_drain} (was {balance_before_drain})"
185 );
186 assert!(
189 balance_after_drain < 20_000,
190 "`pay --all` should have drained (nearly) the whole balance, but {balance_after_drain} of {balance_before_drain} msats remain"
191 );
192 } else {
193 info!("Skipping `pay --all` drain test, fedimint-cli is too old");
194 }
195
196 Ok(())
197 })
198 .await
199}