mirror of
https://github.com/muun/recovery.git
synced 2025-07-20 22:11:16 -04:00
772 lines
21 KiB
Go
772 lines
21 KiB
Go
package lnurl
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/fiatjaf/go-lnurl"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
)
|
|
|
|
func TestWithdraw(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete",
|
|
MaxWithdrawable: 1_000_000,
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdrawRequest",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: StatusOK,
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
if amt != 1_000_000 {
|
|
t.Fatalf("unexpected invoice amount: %v", amt)
|
|
}
|
|
if desc != "Withdraw from Lapp" {
|
|
t.Fatalf("unexpected invoice description: %v", desc)
|
|
}
|
|
if host != "127.0.0.1" {
|
|
t.Fatalf("unexpected host: %v", host)
|
|
}
|
|
return "12345", nil
|
|
}
|
|
|
|
var err string
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
err = e.Message
|
|
}
|
|
})
|
|
if err != "" {
|
|
t.Fatalf("expected withdraw to succeed, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWithdrawWithCompatibilityTag(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete",
|
|
MaxWithdrawable: 1_000_000,
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdraw",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: StatusOK,
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
if amt != 1_000_000 {
|
|
t.Fatalf("unexpected invoice amount: %v", amt)
|
|
}
|
|
if desc != "Withdraw from Lapp" {
|
|
t.Fatalf("unexpected invoice description: %v", desc)
|
|
}
|
|
if host != "127.0.0.1" {
|
|
t.Fatalf("unexpected host: %v", host)
|
|
}
|
|
return "12345", nil
|
|
}
|
|
|
|
var err string
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
err = e.Message
|
|
}
|
|
})
|
|
if err != "" {
|
|
t.Fatalf("expected withdraw to succeed, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDecodeError(t *testing.T) {
|
|
qr := "lightning:abcde"
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrDecode {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusContacting {
|
|
t.Fatal("should not contact server")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestWrongTagError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/channelRequest", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
Tag: "channelRequest",
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/channelRequest", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrWrongTag {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatal("should not create invoice")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUnreachableError(t *testing.T) {
|
|
originalTimeout := httpClient.Timeout
|
|
httpClient.Timeout = 1 * time.Second
|
|
defer func() {
|
|
httpClient.Timeout = originalTimeout
|
|
}()
|
|
|
|
// LNURL QR pointing to a non-responding domain
|
|
qr := "LIGHTNING:LNURL1DP68GURN8GHJ7ARGD9EJUER0D4SKJM3WV3HK2UEWDEHHGTN90P5HXAPWV4UXZMTSD3JJUCM0D5LHXETRWFJHG0F3XGENGDGQ8EH52"
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrUnreachable {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatal("should not create invoice")
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
func TestServiceError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete",
|
|
MaxWithdrawable: 1_000_000,
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdrawRequest",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: StatusError,
|
|
Reason: "something something",
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
if amt != 1_000_000 {
|
|
t.Fatalf("unexpected invoice amount: %v", amt)
|
|
}
|
|
if desc != "Withdraw from Lapp" {
|
|
t.Fatalf("unexpected invoice description: %v", desc)
|
|
}
|
|
if host != "127.0.0.1" {
|
|
t.Fatalf("unexpected host: %v", host)
|
|
}
|
|
return "12345", nil
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrResponse {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusReceiving {
|
|
t.Fatal("should not reach receiving status")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestInvalidResponseError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("foobar"))
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrInvalidResponse {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatal("should not reach invoice creation")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUnsafeURLError(t *testing.T) {
|
|
qr, _ := encode("http://localhost/withdraw")
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, false, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrUnsafeURL {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestWrongTagInQR(t *testing.T) {
|
|
// LNURL QR with a `login` tag value in its query params
|
|
qr := "lightning:lnurl1dp68gurn8ghj7mrww4exctt5dahkccn00qhxget8wfjk2um0veax2un09e3k7mf0w5lhgct884kx7emfdcnxkvfa8qexxc35vymnxcf5xumkxvfsv4snxwph8qunzv3hxesnyv3jvv6nyv3e8yuxzvnpv4skvepnxg6rwv34xqck2c3sxcerzdpnv56r2dss2vt96"
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrWrongTag {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusContacting {
|
|
t.Fatal("should not contact server")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestOnionLinkNotSupported(t *testing.T) {
|
|
qr := "LNURL1DP68GUP69UHKVMM0VFSHYTN0DE5K7MSHXU8YD"
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrTorNotSupported {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusContacting {
|
|
t.Fatal("should not contact server")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestExpiredCheck(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: "ERROR",
|
|
Reason: "something something Expired blabla",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: StatusOK,
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrRequestExpired {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatal("should not create invoice")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestNoAvailableBalance(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete",
|
|
MaxWithdrawable: 0,
|
|
MinWithdrawable: 0,
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdrawRequest",
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrNoAvailableBalance {
|
|
t.Fatalf("unexpected error code: %d", e.Code)
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatalf("should not create invoice")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestNoRouteCheck(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete",
|
|
MaxWithdrawable: 1_000_000,
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdrawRequest",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: StatusError,
|
|
Reason: "Unable to pay LN Invoice: FAILURE_REASON_NO_ROUTE",
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
return "12345", nil
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 && e.Code != ErrNoRoute {
|
|
t.Fatalf("unexpected error code: %d", e.Code)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestExtraQueryParams(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete?foo=bar",
|
|
MaxWithdrawable: 1_000_000,
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdrawRequest",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Query().Get("foo") != "bar" {
|
|
t.Fatalf("Expected foo=bar in query params. Got URL: %v", r.URL.String())
|
|
}
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: StatusOK,
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
if amt != 1_000_000 {
|
|
t.Fatalf("unexpected invoice amount: %v", amt)
|
|
}
|
|
if desc != "Withdraw from Lapp" {
|
|
t.Fatalf("unexpected invoice description: %v", desc)
|
|
}
|
|
if host != "127.0.0.1" {
|
|
t.Fatalf("unexpected host: %v", host)
|
|
}
|
|
return "12345", nil
|
|
}
|
|
|
|
var err string
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
err = e.Message
|
|
}
|
|
})
|
|
if err != "" {
|
|
t.Fatalf("expected withdraw to succeed, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStringlyTypedNumberFields(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&struct {
|
|
Response
|
|
Tag string `json:"tag"`
|
|
K1 string `json:"k1"`
|
|
Callback string `json:"callback"`
|
|
MaxWithdrawable string `json:"maxWithdrawable"`
|
|
MinWithdrawable string `json:"minWithdrawable"`
|
|
DefaultDescription string `json:"defaultDescription"`
|
|
}{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete",
|
|
MaxWithdrawable: "1000000",
|
|
MinWithdrawable: "0",
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdrawRequest",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&Response{
|
|
Status: StatusOK,
|
|
})
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
if amt != 1_000_000 {
|
|
t.Fatalf("unexpected invoice amount: %v", amt)
|
|
}
|
|
if desc != "Withdraw from Lapp" {
|
|
t.Fatalf("unexpected invoice description: %v", desc)
|
|
}
|
|
if host != "127.0.0.1" {
|
|
t.Fatalf("unexpected host: %v", host)
|
|
}
|
|
return "12345", nil
|
|
}
|
|
|
|
var err string
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
err = e.Message
|
|
}
|
|
})
|
|
if err != "" {
|
|
t.Fatalf("expected withdraw to succeed, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestErrorContainsResponseBody(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(400)
|
|
w.Write([]byte("this is a custom error response"))
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
if e.Code != ErrInvalidResponse {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if !strings.Contains(e.Message, "this is a custom error response") {
|
|
t.Fatalf("expected error message to contain response, got `%s`", e.Message)
|
|
}
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatal("should not reach invoice creation")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestErrorContainsResponseBodyForFinishRequest(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(&WithdrawResponse{
|
|
K1: "foobar",
|
|
Callback: "http://" + r.Host + "/withdraw/complete",
|
|
MaxWithdrawable: 1_000_000,
|
|
DefaultDescription: "Withdraw from Lapp",
|
|
Tag: "withdrawRequest",
|
|
})
|
|
})
|
|
mux.HandleFunc("/withdraw/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(400)
|
|
w.Write([]byte("this is a custom error response"))
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
return "12345", nil
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
if e.Code != ErrInvalidResponse {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
if !strings.Contains(e.Message, "this is a custom error response") {
|
|
t.Fatalf("expected error message to contain response, got `%s`", e.Message)
|
|
}
|
|
}
|
|
if e.Code == StatusReceiving {
|
|
t.Fatal("should not reach receiving status")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestForbidden(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(403)
|
|
w.Write([]byte("Forbidden"))
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
if e.Code != ErrForbidden {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatal("should not reach invoice creation")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestZebedee403MapsToCountryNotSupported(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/withdraw/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(403)
|
|
w.Write([]byte("Forbidden"))
|
|
})
|
|
server := httptest.NewServer(mux)
|
|
defer server.Close()
|
|
|
|
// Super ugly hack to emulate that local endpoint is zebedee
|
|
zebedeeHost = "127.0.0.1"
|
|
t.Cleanup(func() {
|
|
zebedeeHost = zebedeeHostConst // after test reset to its original value
|
|
})
|
|
|
|
qr, _ := encode(fmt.Sprintf("%s/withdraw", server.URL))
|
|
|
|
createInvoiceFunc := func(amt lnwire.MilliSatoshi, desc string, host string) (string, error) {
|
|
panic("should not reach here")
|
|
}
|
|
|
|
Withdraw(qr, createInvoiceFunc, true, func(e *Event) {
|
|
if e.Code < 100 {
|
|
if e.Code != ErrCountryNotSupported {
|
|
t.Fatalf("unexpected error code: %v", e.Code)
|
|
}
|
|
}
|
|
if e.Code == StatusInvoiceCreated {
|
|
t.Fatal("should not reach invoice creation")
|
|
}
|
|
})
|
|
}
|
|
|
|
func encode(url string) (string, error) {
|
|
return lnurl.LNURLEncode(url)
|
|
}
|
|
|
|
func TestWithdrawResponse_Validate(t *testing.T) {
|
|
|
|
type fields struct {
|
|
Response Response
|
|
Tag string
|
|
K1 string
|
|
Callback string
|
|
MaxWithdrawable stringOrNumber
|
|
MinWithdrawable stringOrNumber
|
|
DefaultDescription string
|
|
}
|
|
errorResponse := func(reason string) fields {
|
|
return fields{
|
|
Response: Response{
|
|
Status: StatusError,
|
|
Reason: reason,
|
|
},
|
|
}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
want int
|
|
}{
|
|
{
|
|
"invalid tag",
|
|
fields{Tag: "blebidy"},
|
|
ErrWrongTag,
|
|
},
|
|
{
|
|
"negative withdraw",
|
|
fields{MaxWithdrawable: -1, Tag: "withdraw"},
|
|
ErrNoAvailableBalance,
|
|
},
|
|
{
|
|
"valid",
|
|
fields{
|
|
Response: Response{
|
|
Status: StatusOK,
|
|
},
|
|
Tag: "withdraw",
|
|
MaxWithdrawable: 10,
|
|
},
|
|
ErrNone,
|
|
},
|
|
{
|
|
"already being processed",
|
|
errorResponse("This Withdrawal Request is already being processed by another wallet"),
|
|
ErrAlreadyUsed,
|
|
},
|
|
{
|
|
"can only be processed only once",
|
|
errorResponse("This Withdrawal Request can only be processed once"),
|
|
ErrAlreadyUsed,
|
|
},
|
|
{
|
|
"withdraw is spent",
|
|
errorResponse("Withdraw is spent"),
|
|
ErrAlreadyUsed,
|
|
},
|
|
{
|
|
"withdraw link is empty",
|
|
errorResponse("Withdraw link is empty"),
|
|
ErrAlreadyUsed,
|
|
},
|
|
{
|
|
"has already been used",
|
|
errorResponse("This LNURL has already been used"),
|
|
ErrAlreadyUsed,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
wr := &WithdrawResponse{
|
|
Response: tt.fields.Response,
|
|
Tag: tt.fields.Tag,
|
|
K1: tt.fields.K1,
|
|
Callback: tt.fields.Callback,
|
|
MaxWithdrawable: tt.fields.MaxWithdrawable,
|
|
MinWithdrawable: tt.fields.MinWithdrawable,
|
|
DefaultDescription: tt.fields.DefaultDescription,
|
|
}
|
|
got, _ := wr.Validate()
|
|
if got != tt.want {
|
|
t.Errorf("Validate() got = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidate(t *testing.T) {
|
|
type args struct {
|
|
qr string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want bool
|
|
}{
|
|
{
|
|
name: "plain",
|
|
args: args{"LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "lightning scheme",
|
|
args: args{"lightning:LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "HTTP fallback scheme",
|
|
args: args{"https://example.com/?lightning=LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "muun scheme",
|
|
args: args{"muun:LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "muun scheme with double slashes",
|
|
args: args{"muun://LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "lightning scheme with double slashes",
|
|
args: args{"lightning://LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "muun + lightning schemes",
|
|
args: args{"muun:lightning:LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "muun + lightning schemes with double slashes",
|
|
args: args{"muun://lightning:LNURL1DP68GUP69UHKCMMRV9KXSMMNWSARWVPCXQHKCMN4WFKZ7AMFW35XGUNPWULHXETRWFJHG0F3XGENGDGK59DKV"},
|
|
want: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := Validate(tt.args.qr); got != tt.want {
|
|
t.Errorf("Validate() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|