From 8d3b156738f5d957bed2207084f0db2ce289642c Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Tue, 23 Apr 2024 18:18:52 +0300 Subject: [PATCH] [test] add tests for `eclair` funding source (#2459) * fix: test data * test: add `status` tests * refactor: try-catch * test: create invoice tests * test: add first `payinvoice` test * test: add pay_invoice error test * feat: allow more test options * test: add pending tests * fix: make check * test: add, pending no fee * fix: make consistent with other API calls * test: more assertions * test: add pending * test: first payment status test * test: pending status * refactor: remove duplicate code * refactor: rename field * chore: code format * chore: uniform --- lnbits/wallets/eclair.py | 144 ++-- lnbits/wallets/lndrest.py | 2 +- .../wallets/fixtures/json/fixtures_rest.json | 668 +++++++++++++++++- tests/wallets/fixtures/models.py | 8 +- tests/wallets/test_rest_wallets.py | 4 +- 5 files changed, 740 insertions(+), 86 deletions(-) diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 3f612c6c3..d1d75e319 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -55,20 +55,27 @@ class EclairWallet(Wallet): logger.warning(f"Error closing wallet connection: {e}") async def status(self) -> StatusResponse: - r = await self.client.post("/globalbalance", timeout=5) try: + r = await self.client.post("/globalbalance", timeout=5) + r.raise_for_status() + data = r.json() - except Exception: - return StatusResponse( - f"Failed to connect to {self.url}, got: '{r.text[:200]}...'", 0 - ) - if r.is_error: - return StatusResponse(data.get("error") or "undefined error", 0) - if len(data) == 0: - return StatusResponse("no data", 0) + if len(data) == 0: + return StatusResponse("no data", 0) - return StatusResponse(None, int(data.get("total") * 100_000_000_000)) + if "error" in data: + return StatusResponse(f"""Server error: '{data["error"]}'""", 0) + + if r.is_error or "total" not in data: + return StatusResponse(f"Server error: '{r.text}'", 0) + + return StatusResponse(None, int(data.get("total") * 100_000_000_000)) + except json.JSONDecodeError: + return StatusResponse("Server error: 'invalid json response'", 0) + except Exception as exc: + logger.warning(exc) + return StatusResponse(f"Unable to connect to {self.url}.", 0) async def create_invoice( self, @@ -92,73 +99,78 @@ class EclairWallet(Wallet): else: data["description"] = memo - r = await self.client.post("/createinvoice", data=data, timeout=40) + try: + r = await self.client.post("/createinvoice", data=data, timeout=40) + r.raise_for_status() + data = r.json() - if r.is_error: - try: - data = r.json() - error_message = data["error"] - except Exception: - error_message = r.text + if len(data) == 0: + return InvoiceResponse(False, None, None, "no data") - return InvoiceResponse(False, None, None, error_message) + if "error" in data: + return InvoiceResponse( + False, None, None, f"""Server error: '{data["error"]}'""" + ) - data = r.json() - return InvoiceResponse(True, data["paymentHash"], data["serialized"], None) + if r.is_error: + return InvoiceResponse(False, None, None, f"Server error: '{r.text}'") + + return InvoiceResponse(True, data["paymentHash"], data["serialized"], None) + except json.JSONDecodeError: + return InvoiceResponse( + False, None, None, "Server error: 'invalid json response'" + ) + except KeyError as exc: + logger.warning(exc) + return InvoiceResponse( + False, None, None, "Server error: 'missing required fields'" + ) + except Exception as exc: + logger.warning(exc) + return InvoiceResponse( + False, None, None, f"Unable to connect to {self.url}." + ) async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: - r = await self.client.post( - "/payinvoice", - data={"invoice": bolt11, "blocking": True}, - timeout=None, - ) + try: + r = await self.client.post( + "/payinvoice", + data={"invoice": bolt11, "blocking": True}, + timeout=None, + ) + r.raise_for_status() + data = r.json() - if "error" in r.json(): - try: - data = r.json() - error_message = data["error"] - except Exception: - error_message = r.text - return PaymentResponse(False, None, None, None, error_message) + if "error" in data: + return PaymentResponse(False, None, None, None, data["error"]) + if r.is_error: + return PaymentResponse(False, None, None, None, r.text) - data = r.json() + if data["type"] == "payment-failed": + return PaymentResponse(False, None, None, None, "payment failed") - if data["type"] == "payment-failed": - return PaymentResponse(False, None, None, None, "payment failed") + checking_id = data["paymentHash"] + preimage = data["paymentPreimage"] - checking_id = data["paymentHash"] - preimage = data["paymentPreimage"] - - # We do all this again to get the fee: - - r = await self.client.post( - "/getsentinfo", - data={"paymentHash": checking_id}, - timeout=40, - ) - - if "error" in r.json(): - try: - data = r.json() - error_message = data["error"] - except Exception: - error_message = r.text - return PaymentResponse(None, checking_id, None, preimage, error_message) - - statuses = { - "sent": True, - "failed": False, - "pending": None, - } - - data = r.json()[-1] - fee_msat = 0 - if data["status"]["type"] == "sent": - fee_msat = -data["status"]["feesPaid"] - preimage = data["status"]["paymentPreimage"] + except json.JSONDecodeError: + return PaymentResponse( + False, None, None, None, "Server error: 'invalid json response'" + ) + except KeyError: + return PaymentResponse( + False, None, None, None, "Server error: 'missing required fields'" + ) + except Exception as exc: + logger.info(f"Failed to pay invoice {bolt11}") + logger.warning(exc) + return PaymentResponse( + False, None, None, None, f"Unable to connect to {self.url}." + ) + payment_status: PaymentStatus = await self.get_payment_status(checking_id) + success = True if payment_status.success else None return PaymentResponse( - statuses[data["status"]["type"]], checking_id, fee_msat, preimage, None + success, checking_id, payment_status.fee_msat, preimage, None ) async def get_invoice_status(self, checking_id: str) -> PaymentStatus: diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py index 12ce2a954..184a2cd43 100644 --- a/lnbits/wallets/lndrest.py +++ b/lnbits/wallets/lndrest.py @@ -177,7 +177,7 @@ class LndRestWallet(Wallet): except Exception as exc: logger.warning(f"LndRestWallet pay_invoice POST error: {exc}.") return PaymentResponse( - None, None, None, None, f"Unable to connect to {self.endpoint}." + False, None, None, None, f"Unable to connect to {self.endpoint}." ) try: diff --git a/tests/wallets/fixtures/json/fixtures_rest.json b/tests/wallets/fixtures/json/fixtures_rest.json index cfe3b7802..54a2695e3 100644 --- a/tests/wallets/fixtures/json/fixtures_rest.json +++ b/tests/wallets/fixtures/json/fixtures_rest.json @@ -24,6 +24,14 @@ "alby_access_token": "mock-alby-access-token", "user_agent": "LNbits/Tests" } + }, + "eclair": { + "wallet_class": "EclairWallet", + "settings": { + "eclair_url": "http://127.0.0.1:8555", + "eclair_pass": "secret", + "user_agent": "LNbits/Tests" + } } }, "functions": { @@ -60,6 +68,16 @@ }, "method": "GET" } + }, + "eclair": { + "status_endpoint": { + "uri": "/globalbalance", + "headers": { + "Authorization": "Basic OnNlY3JldA==", + "User-Agent": "LNbits/Tests" + }, + "method": "POST" + } } }, "tests": [ @@ -101,6 +119,16 @@ } } ] + }, + "eclair": { + "status_endpoint": [ + { + "response_type": "json", + "response": { + "total": 0.00000055 + } + } + ] } } }, @@ -140,6 +168,16 @@ } } ] + }, + "eclair": { + "status_endpoint": [ + { + "response_type": "json", + "response": { + "error": "\"test-error\"" + } + } + ] } } }, @@ -174,6 +212,14 @@ "response": {} } ] + }, + "eclair": { + "status_endpoint": [ + { + "response_type": "json", + "response": {} + } + ] } } }, @@ -208,6 +254,14 @@ "response": "data-not-json" } ] + }, + "eclair": { + "status_endpoint": [ + { + "response_type": "data", + "response": "data-not-json" + } + ] } } }, @@ -251,6 +305,17 @@ } } ] + }, + "eclair": { + "status_endpoint": [ + { + "response_type": "response", + "response": { + "response": "Not Found", + "status": 404 + } + } + ] } } }, @@ -297,6 +362,16 @@ }, "method": "POST" } + }, + "eclair": { + "create_invoice_endpoint": { + "uri": "/createinvoice", + "headers": { + "Authorization": "Basic OnNlY3JldA==", + "User-Agent": "LNbits/Tests" + }, + "method": "POST" + } } }, "tests": [ @@ -363,6 +438,21 @@ } } ] + }, + "eclair": { + "create_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "amountMsat": 555000 + }, + "response_type": "json", + "response": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", + "serialized": "lnbc5550n1pnq9jg3sp52rvwstvjcypjsaenzdh0h30jazvzsf8aaye0julprtth9kysxtuspp5e5s3z7felv4t9zrcc6wpn7ehvjl5yzewanzl5crljdl3jgeffyhqdq2f38xy6t5wvxqzjccqpjrzjq0yzeq76ney45hmjlnlpvu0nakzy2g35hqh0dujq8ujdpr2e42pf2rrs6vqpgcsqqqqqqqqqqqqqqeqqyg9qxpqysgqwftcx89k5pp28435pgxfl2vx3ksemzxccppw2j9yjn0ngr6ed7wj8ztc0d5kmt2mvzdlcgrludhz7jncd5l5l9w820hc4clpwhtqj3gq62g66n" + } + } + ] } } }, @@ -414,6 +504,20 @@ }, "alby": { "create_invoice_endpoint": [] + }, + "eclair": { + "create_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "amountMsat": 555000 + }, + "response_type": "json", + "response": { + "error": "Test Error" + } + } + ] } } }, @@ -478,6 +582,20 @@ } } ] + }, + "eclair": { + "create_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "amountMsat": 555000 + }, + "response_type": "json", + "response": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + } + } + ] } } }, @@ -535,6 +653,18 @@ "response": "data-not-json" } ] + }, + "eclair": { + "create_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "amountMsat": 555000 + }, + "response_type": "data", + "response": "data-not-json" + } + ] } } }, @@ -601,6 +731,21 @@ } } ] + }, + "eclair": { + "create_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "amountMsat": 555000 + }, + "response_type": "response", + "response": { + "response": "Not Found", + "status": 404 + } + } + ] } } }, @@ -653,6 +798,24 @@ }, "method": "POST" } + }, + "eclair": { + "pay_invoice_endpoint": { + "uri": "/payinvoice", + "headers": { + "Authorization": "Basic OnNlY3JldA==", + "User-Agent": "LNbits/Tests" + }, + "method": "POST" + }, + "get_payment_status_endpoint": { + "uri": "/getsentinfo", + "headers": { + "Authorization": "Basic OnNlY3JldA==", + "User-Agent": "LNbits/Tests" + }, + "method": "POST" + } } }, "tests": [ @@ -663,11 +826,13 @@ "fee_limit_msat": 25000 }, "expect": { + "error_message": null, "success": true, + "pending": false, + "failed": false, "checking_id": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", "fee_msat": 50, - "preimage": "0000000000000000000000000000000000000000000000000000000000000000", - "error_message": null + "preimage": "0000000000000000000000000000000000000000000000000000000000000000" }, "mocks": { "corelightningrest": { @@ -725,9 +890,186 @@ } } ] + }, + "eclair": { + "pay_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "json", + "response": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", + "paymentPreimage": "0000000000000000000000000000000000000000000000000000000000000000", + "type": "mock" + } + } + ], + "get_payment_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [ + { + "status": { + "type": "sent", + "feesPaid": -50, + "paymentPreimage": "0000000000000000000000000000000000000000000000000000000000000000" + } + } + ] + } + ] } } }, + { + "description": "pending, no fee", + "call_params": { + "bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu", + "fee_limit_msat": 25000 + }, + "expect": { + "success": false, + "pending": true, + "failed": false, + "checking_id": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", + "fee_msat": null, + "preimage": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "mocks": { + "corelightningrest": {}, + "lndrest": {}, + "alby": {}, + "eclair": [ + { + "pay_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "json", + "response": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", + "paymentPreimage": "0000000000000000000000000000000000000000000000000000000000000000", + "type": "mock" + } + } + ], + "get_payment_status_endpoint": [ + { + "description": "get paid invoice: error", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": { + "error": "Test Error" + } + } + ] + }, + { + "pay_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "json", + "response": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", + "paymentPreimage": "0000000000000000000000000000000000000000000000000000000000000000", + "type": "mock" + } + } + ], + "get_payment_status_endpoint": [ + { + "description": "get paid invoice: no data", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": {} + } + ] + }, + { + "pay_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "json", + "response": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", + "paymentPreimage": "0000000000000000000000000000000000000000000000000000000000000000", + "type": "mock" + } + } + ], + "get_payment_status_endpoint": [ + { + "description": "get paid invoice: bad json", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "data", + "response": "data-not-json" + } + ] + }, + { + "pay_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "json", + "response": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96", + "paymentPreimage": "0000000000000000000000000000000000000000000000000000000000000000", + "type": "mock" + } + } + ], + "get_payment_status_endpoint": [ + { + "description": "get paid invoice: pending status", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [ + { + "status": { + "type": "pending" + } + } + ] + } + ] + } + ] + } + }, { "description": "error", "call_params": { @@ -736,6 +1078,8 @@ }, "expect": { "success": false, + "pending": false, + "failed": true, "checking_id": null, "fee_msat": null, "preimage": null, @@ -775,6 +1119,31 @@ }, "alby": { "pay_invoice_endpoint": [] + }, + "eclair": { + "pay_invoice_endpoint": [ + { + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "json", + "response": { + "error": "Test Error" + } + } + ], + "get_payment_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [] + } + ] } } }, @@ -785,11 +1154,13 @@ "fee_limit_msat": 25000 }, "expect": { + "error_message": "Server error: 'missing required fields'", "success": false, + "pending": false, + "failed": true, "checking_id": null, "fee_msat": null, - "preimage": null, - "error_message": "Server error: 'missing required fields'" + "preimage": null }, "mocks": { "corelightningrest": { @@ -830,6 +1201,30 @@ "response": {} } ] + }, + "eclair": { + "pay_invoice_endpoint": [ + { + "description": "no data from pay invoice", + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "json", + "response": {} + } + ], + "get_payment_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [] + } + ] } } }, @@ -840,11 +1235,13 @@ "fee_limit_msat": 25000 }, "expect": { + "error_message": "Server error: 'invalid json response'", "success": false, + "pending": false, + "failed": true, "checking_id": null, "fee_msat": null, - "preimage": null, - "error_message": "Server error: 'invalid json response'" + "preimage": null }, "mocks": { "corelightningrest": { @@ -885,6 +1282,30 @@ "response": "data-not-json" } ] + }, + "eclair": { + "pay_invoice_endpoint": [ + { + "description": "no data from pay invoice", + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "data", + "response": "data-not-json" + } + ], + "get_payment_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [] + } + ] } } }, @@ -895,11 +1316,13 @@ "fee_limit_msat": 25000 }, "expect": { + "error_message": "Unable to connect to http://127.0.0.1:8555.", "success": false, + "pending": false, + "failed": true, "checking_id": null, "fee_msat": null, - "preimage": null, - "error_message": "Unable to connect to http://127.0.0.1:8555." + "preimage": null }, "mocks": { "corelightningrest": { @@ -949,6 +1372,33 @@ } } ] + }, + "eclair": { + "pay_invoice_endpoint": [ + { + "description": "no data from pay invoice", + "request_type": "json", + "request_body": { + "blocking": true, + "invoice": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu" + }, + "response_type": "response", + "response": { + "response": "Not Found", + "status": 404 + } + } + ], + "get_payment_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [] + } + ] } } }, @@ -959,11 +1409,13 @@ "fee_limit_msat": 25000 }, "expect": { + "error_message": "Unable to connect to http://127.0.0.1:8555.", "success": false, + "pending": false, + "failed": true, "checking_id": null, "fee_msat": null, - "preimage": null, - "error_message": "Unable to connect to http://127.0.0.1:8555." + "preimage": null } } ] @@ -1004,6 +1456,16 @@ }, "method": "GET" } + }, + "eclair": { + "get_invoice_status_endpoint": { + "uri": "/getreceivedinfo", + "headers": { + "Authorization": "Basic OnNlY3JldA==", + "User-Agent": "LNbits/Tests" + }, + "method": "POST" + } } }, "tests": [ @@ -1061,6 +1523,22 @@ } } ] + }, + "eclair": { + "get_invoice_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": { + "status": { + "type": "received" + } + } + } + ] } } }, @@ -1097,6 +1575,22 @@ "alby": { "description": "alby.py doesn't handle the 'failed' status for `get_invoice_status`", "get_invoice_status_endpoint": [] + }, + "eclair": { + "get_invoice_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": { + "status": { + "type": "expired" + } + } + } + ] } } }, @@ -1143,7 +1637,7 @@ "lndrest": { "get_invoice_status_endpoint": [ { - "description": "error status", + "description": "no data", "response_type": "json", "response": {} }, @@ -1172,7 +1666,7 @@ "alby": { "get_invoice_status_endpoint": [ { - "description": "error status", + "description": "no data", "response_type": "json", "response": {} }, @@ -1197,6 +1691,53 @@ } } ] + }, + "eclair": { + "get_invoice_status_endpoint": [ + { + "description": "no data", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": {} + }, + { + "description": "pending status", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": { + "status": { + "type": "pending" + } + } + }, + { + "description": "bad json", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "data", + "response": "data-not-json" + }, + { + "description": "http 404", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "response", + "response": { + "response": "Not Found", + "status": 404 + } + } + ] } } }, @@ -1249,6 +1790,16 @@ }, "method": "GET" } + }, + "eclair": { + "get_payment_status_endpoint": { + "uri": "/getsentinfo", + "headers": { + "Authorization": "Basic OnNlY3JldA==", + "User-Agent": "LNbits/Tests" + }, + "method": "POST" + } } }, "tests": [ @@ -1258,6 +1809,7 @@ "checking_id": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" }, "expect": { + "fee_msat": 1000, "preimage": "0000000000000000000000000000000000000000000000000000000000000000", "success": true, "failed": false, @@ -1307,6 +1859,26 @@ } } ] + }, + "eclair": { + "get_payment_status_endpoint": [ + { + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [ + { + "status": { + "type": "sent", + "feesPaid": -1000, + "paymentPreimage": "0000000000000000000000000000000000000000000000000000000000000000" + } + } + ] + } + ] } } }, @@ -1361,6 +1933,25 @@ }, "alby": { "get_payment_status_endpoint": [] + }, + "eclair": { + "get_payment_status_endpoint": [ + { + "description": "failed status", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [ + { + "status": { + "type": "failed" + } + } + ] + } + ] } } }, @@ -1472,9 +2063,7 @@ "description": "CREATED", "response_type": "stream", "response": { - "result": { - "state": "CREATED" - } + "state": "CREATED" } }, { @@ -1496,6 +2085,55 @@ } } ] + }, + "eclair": { + "get_payment_status_endpoint": [ + { + "description": "no data", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [] + }, + { + "description": "pending status", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "json", + "response": [ + { + "status": { + "type": "pending" + } + } + ] + }, + { + "description": "bad json", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "data", + "response": "data-not-json" + }, + { + "description": "http 404", + "request_type": "json", + "request_body": { + "paymentHash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96" + }, + "response_type": "response", + "response": { + "response": "Not Found", + "status": 404 + } + } + ] } } }, diff --git a/tests/wallets/fixtures/models.py b/tests/wallets/fixtures/models.py index 073b08fdc..7149ee7cf 100644 --- a/tests/wallets/fixtures/models.py +++ b/tests/wallets/fixtures/models.py @@ -23,7 +23,7 @@ class TestMock(BaseModel): request_type: Optional[str] request_body: Optional[dict] response_type: str - response: Union[str, dict] + response: Union[str, dict, list] class Mock(FunctionMock, TestMock): @@ -100,7 +100,11 @@ class WalletTest(BaseModel): fs_mocks = fn["mocks"][fs_name] test_mocks = test["mocks"][fs_name] - mocks = self._build_mock_objects(list(fs_mocks), fs_mocks, test_mocks) + all_test_mocks = [test_mocks] if isinstance(test_mocks, dict) else test_mocks + + mocks = [] + for tm in all_test_mocks: + mocks += self._build_mock_objects(list(fs_mocks), fs_mocks, tm) return [self._tests_from_mock(m) for m in mocks] diff --git a/tests/wallets/test_rest_wallets.py b/tests/wallets/test_rest_wallets.py index dca8fc487..b91724bfc 100644 --- a/tests/wallets/test_rest_wallets.py +++ b/tests/wallets/test_rest_wallets.py @@ -45,7 +45,7 @@ async def test_rest_wallet(httpserver: HTTPServer, test_data: WalletTest): def _apply_mock(httpserver: HTTPServer, mock: Mock): - request_data: Dict[str, Union[str, dict]] = {} + request_data: Dict[str, Union[str, dict, list]] = {} request_type = getattr(mock.dict(), "request_type", None) # request_type = mock.request_type <--- this des not work for whatever reason!!! @@ -67,7 +67,7 @@ def _apply_mock(httpserver: HTTPServer, mock: Mock): **request_data, # type: ignore ) - server_response: Union[str, dict, Response] = mock.response + server_response: Union[str, dict, list, Response] = mock.response response_type = mock.response_type if response_type == "response": assert isinstance(server_response, dict), "server response must be JSON"