fedimint_recurringd_tests/
fedimint-recurringd-tests.rs

1use std::ops::ControlFlow;
2
3use devimint::tests::log_binary_versions;
4use devimint::util::poll;
5use devimint::version_constants::VERSION_0_7_0_ALPHA;
6use devimint::{DevFed, cmd};
7use lightning_invoice::Bolt11Invoice;
8use tracing::info;
9
10#[tokio::main]
11async fn main() -> anyhow::Result<()> {
12    devimint::run_devfed_test()
13        .call(|dev_fed, process_mgr| async move {
14            log_binary_versions().await?;
15
16            let DevFed {
17                fed,
18                gw_lnd,
19                gw_ldk_second,
20                recurringd,
21                ..
22            } = dev_fed.to_dev_fed(&process_mgr).await?;
23
24            // Test admin auth is checked
25            {
26                let dummy_invite = "fed114znk7uk7ppugdjuytr8venqf2tkywd65cqvg3u93um64tu5cw4yr0n3fvn7qmwvm4g48cpndgnm4gqq4waen5te0xyerwt3s9cczuvf6xyurzde597s7crdvsk2vmyarjw9gwyqjdzj";
27                let url = format!("{}lnv1/federations", recurringd.api_url);
28                let client = reqwest::Client::new();
29                let response_no_auth = client
30                    .put(&url)
31                    .header("Content-Type", "application/json")
32                    .json(&serde_json::json!({ "invite": dummy_invite }))
33                    .send()
34                    .await?;
35                assert!(response_no_auth.status().is_client_error());
36
37                let response_with_wrong_auth = client
38                    .put(&url)
39                    .header("Authorization", "Bearer wrong-token")
40                    .header("Content-Type", "application/json")
41                    .json(&serde_json::json!({ "invite": dummy_invite }))
42                    .send()
43                    .await?;
44                assert!(response_with_wrong_auth.status().is_client_error());
45            }
46
47            let fedimint_cli_version = devimint::util::FedimintCli::version_or_default().await;
48
49            if fedimint_cli_version < *VERSION_0_7_0_ALPHA {
50                info!("Skipping recurringd test because fedimint-cli is lower than v0.7.0");
51                return Ok(());
52            }
53
54            // Give the LND Gateway a balance, it's the only GW serving LNv1 and recurringd
55            // is currently LNv1-only
56            fed.pegin_gateways(10_000_000, vec![&gw_lnd]).await?;
57
58            let client = fed.new_joined_client("recurringd-test-client").await?;
59
60            let lnurl = cmd!(
61                client,
62                "module",
63                "ln",
64                "lnurl",
65                "register",
66                recurringd.api_url()
67            )
68            .out_json()
69            .await?["lnurl"]
70                .as_str()
71                .unwrap()
72                .to_owned();
73
74            let lnurl_list = cmd!(client, "module", "ln", "lnurl", "list")
75                .out_json()
76                .await?["codes"]
77                .as_object()
78                .unwrap()
79                .clone();
80
81            assert_eq!(lnurl_list.len(), 1);
82
83            let listed_lnurl = lnurl_list["0"].clone();
84            assert_eq!(listed_lnurl["lnurl"].as_str().unwrap(), &lnurl);
85            assert_eq!(listed_lnurl["last_derivation_index"].as_i64().unwrap(), 0);
86
87            let invoice = cmd!("lnurlp", "--amount", "1000sat", lnurl)
88                .out_string()
89                .await?
90                .parse::<Bolt11Invoice>()
91                .unwrap();
92
93            gw_ldk_second.pay_invoice(invoice.clone()).await?;
94
95            let invoice_op_id = poll("lnurl_receive", || async {
96                cmd!(client, "dev", "wait", "2")
97                    .run()
98                    .await
99                    .map_err(ControlFlow::Break)?;
100
101                let invoice_list = cmd!(client, "module", "ln", "lnurl", "invoices", "0")
102                    .out_json()
103                    .await
104                    .map_err(ControlFlow::Break)?["invoices"]
105                    .as_object()
106                    .unwrap()
107                    .clone();
108
109                if invoice_list.is_empty() {
110                    return Err(ControlFlow::Continue(anyhow::anyhow!(
111                        "No invoice recognized yet"
112                    )));
113                }
114
115                Ok(invoice_list["1"]["operation_id"]
116                    .as_str()
117                    .unwrap()
118                    .to_owned())
119            })
120            .await?;
121
122            let await_invoice_result = cmd!(
123                client,
124                "module",
125                "ln",
126                "lnurl",
127                "await-invoice-paid",
128                invoice_op_id
129            )
130            .out_json()
131            .await?;
132
133            assert_eq!(
134                await_invoice_result["amount_msat"].as_i64().unwrap(),
135                1_000_000
136            );
137            assert_eq!(
138                await_invoice_result["invoice"].as_str().unwrap(),
139                &invoice.to_string()
140            );
141
142            let client_balance = client.balance().await?;
143            assert_eq!(client_balance, 1_000_000);
144            info!("Client balance: {client_balance}");
145
146            Ok(())
147        })
148        .await
149}