LNURL tests and small fixes

This commit is contained in:
Kukks
2021-10-26 13:55:13 +02:00
parent d1886b039e
commit fccbbb6fb7
5 changed files with 218 additions and 15 deletions

View File

@@ -132,15 +132,17 @@ namespace BTCPayServer.Tests
return usr; return usr;
} }
public (string storeName, string storeId) CreateNewStore() public (string storeName, string storeId) CreateNewStore(bool keepId = true)
{ {
Driver.WaitForElement(By.Id("Stores")).Click(); Driver.WaitForElement(By.Id("Stores")).Click();
Driver.WaitForElement(By.Id("CreateStore")).Click(); Driver.WaitForElement(By.Id("CreateStore")).Click();
var name = "Store" + RandomUtils.GetUInt64(); var name = "Store" + RandomUtils.GetUInt64();
Driver.WaitForElement(By.Id("Name")).SendKeys(name); Driver.WaitForElement(By.Id("Name")).SendKeys(name);
Driver.WaitForElement(By.Id("Create")).Click(); Driver.WaitForElement(By.Id("Create")).Click();
StoreId = Driver.WaitForElement(By.Id("Id")).GetAttribute("value"); var storeId = Driver.WaitForElement(By.Id("Id")).GetAttribute("value");
return (name, StoreId); if (keepId)
StoreId = storeId;
return (name, storeId);
} }
public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit) public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit)
@@ -213,7 +215,7 @@ namespace BTCPayServer.Tests
FindAlertMessage(); FindAlertMessage();
} }
public void AddLightningNode(string cryptoCode = "BTC", LightningConnectionType? connectionType = null) public void AddLightningNode(string cryptoCode = "BTC", LightningConnectionType? connectionType = null, Action beforeEnable = null, bool test = true)
{ {
Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click(); Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
@@ -236,13 +238,20 @@ namespace BTCPayServer.Tests
else else
{ {
Driver.FindElement(By.CssSelector("label[for=\"LightningNodeType-Custom\"]")).Click(); Driver.FindElement(By.CssSelector("label[for=\"LightningNodeType-Custom\"]")).Click();
Driver.FindElement(By.Id("ConnectionString")).Clear();
Driver.FindElement(By.Id("ConnectionString")).SendKeys(connectionString); Driver.FindElement(By.Id("ConnectionString")).SendKeys(connectionString);
if (test)
Driver.FindElement(By.Id("test")).Click(); {
Assert.Contains("Connection to the Lightning node successful.", FindAlertMessage().Text); Driver.FindElement(By.Id("test")).Click();
Assert.Contains("Connection to the Lightning node successful.", FindAlertMessage().Text);
}
} }
beforeEnable?.Invoke();
Driver.FindElement(By.Id("save")).Click(); Driver.FindElement(By.Id("save")).Click();
//soemtimes selenium slows down and misses a beat
if(FindAlertMessage().Text == "Connection to the Lightning node successful.")
Driver.FindElement(By.Id("save")).Click();
Assert.Contains($"{cryptoCode} Lightning node updated.", FindAlertMessage().Text); Assert.Contains($"{cryptoCode} Lightning node updated.", FindAlertMessage().Text);
var enabled = Driver.FindElement(By.Id($"{cryptoCode}LightningEnabled")); var enabled = Driver.FindElement(By.Id($"{cryptoCode}LightningEnabled"));
@@ -354,7 +363,8 @@ namespace BTCPayServer.Tests
decimal? amount = 100, decimal? amount = 100,
string currency = "USD", string currency = "USD",
string refundEmail = "", string refundEmail = "",
string defaultPaymentMethod = null string defaultPaymentMethod = null,
StatusMessageModel.StatusSeverity expectedSeverity = StatusMessageModel.StatusSeverity.Success
) )
{ {
GoToInvoices(); GoToInvoices();
@@ -370,9 +380,8 @@ namespace BTCPayServer.Tests
new SelectElement(Driver.FindElement(By.Name("DefaultPaymentMethod"))).SelectByValue(defaultPaymentMethod); new SelectElement(Driver.FindElement(By.Name("DefaultPaymentMethod"))).SelectByValue(defaultPaymentMethod);
Driver.FindElement(By.Id("Create")).Click(); Driver.FindElement(By.Id("Create")).Click();
var statusElement = FindAlertMessage(); var statusElement = FindAlertMessage(expectedSeverity);
var id = statusElement.Text.Split(" ")[1]; return expectedSeverity == StatusMessageModel.StatusSeverity.Success ? statusElement.Text.Split(" ")[1] : null;
return id;
} }
public async Task FundStoreWallet(WalletId walletId = null, int coins = 1, decimal denomination = 1m) public async Task FundStoreWallet(WalletId walletId = null, int coins = 1, decimal denomination = 1m)

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@@ -11,15 +12,18 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Lightning; using BTCPayServer.Lightning;
using BTCPayServer.Lightning.Charge; using BTCPayServer.Lightning.Charge;
using BTCPayServer.Lightning.CLightning;
using BTCPayServer.Lightning.LND; using BTCPayServer.Lightning.LND;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using BTCPayServer.Tests.Logging; using BTCPayServer.Tests.Logging;
using BTCPayServer.Views.Manage; using BTCPayServer.Views.Manage;
using BTCPayServer.Views.Server; using BTCPayServer.Views.Server;
using BTCPayServer.Views.Stores; using BTCPayServer.Views.Stores;
using BTCPayServer.Views.Wallets; using BTCPayServer.Views.Wallets;
using LNURL;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NBitcoin; using NBitcoin;
@@ -1220,6 +1224,195 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id($"{PayoutState.Completed}-view")).Click(); s.Driver.FindElement(By.Id($"{PayoutState.Completed}-view")).Click();
Assert.Contains(bolt, s.Driver.PageSource); Assert.Contains(bolt, s.Driver.PageSource);
} }
}
[Fact]
[Trait("Selenium", "Selenium")]
[Trait("Lightning", "Lightning")]
public async Task CanUseLNURL()
{
using var s = SeleniumTester.Create();
s.Server.ActivateLightning();
await s.StartAsync();
await s.Server.EnsureChannelsSetup();
await BTCPayServer.Lightning.Tests.ConnectChannels.ConnectAll(s.Server.ExplorerNode,
new[] { s.Server.MerchantLightningD },
new[] { s.Server.MerchantLnd.Client });
s.RegisterNewUser(true);
var store = s.CreateNewStore();
var network = s.Server.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork;
s.GoToStore(store.storeId);
s.AddLightningNode("BTC", LightningConnectionType.CLightning, () =>
{
//lnurl is false by default
Assert.False( s.Driver.FindElement(By.Id("LNURLEnabled")).Selected);
//lnurl settings are not expanded when lnurl is disabled
Assert.DoesNotContain("show", s.Driver.FindElement(By.Id("LNURLSettings")).GetAttribute("class"));
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
});
//topup Invoice test
var i = s.CreateInvoice(store.storeName, null, "BTC");
s.GoToInvoiceCheckout(i);
s.Driver.FindElement(By.Id("copy-tab")).Click();
var lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
var parsed = LNURL.LNURL.Parse(lnurl, out var tag);
var fetchedReuqest = Assert.IsType<LNURL.LNURLPayRequest>(await LNURL.LNURL.FetchInformation(parsed, new HttpClient()));
Assert.Equal(1m, fetchedReuqest.MinSendable.ToDecimal(LightMoneyUnit.Satoshi));
Assert.NotEqual(1m, fetchedReuqest.MaxSendable.ToDecimal(LightMoneyUnit.Satoshi));
var lnurlResponse = await fetchedReuqest.SendRequest(new LightMoney(0.000001m, LightMoneyUnit.BTC),
network, new HttpClient());
Assert.Equal(new LightMoney(0.000001m, LightMoneyUnit.BTC), lnurlResponse.GetPaymentRequest(network).MinimumAmount);
var lnurlResponse2 = await fetchedReuqest.SendRequest(new LightMoney(0.000002m, LightMoneyUnit.BTC),
network, new HttpClient());
Assert.Equal(new LightMoney(0.000002m, LightMoneyUnit.BTC), lnurlResponse2.GetPaymentRequest(network).MinimumAmount);
await Assert.ThrowsAnyAsync<LightningRPCException>(async () =>
{
//the initial bolt was cancelled
await s.Server.CustomerLightningD.Pay(lnurlResponse.Pr);
});
await s.Server.CustomerLightningD.Pay(lnurlResponse2.Pr);
await TestUtils.EventuallyAsync(async () =>
{
var inv = await s.Server.PayTester.InvoiceRepository.GetInvoice(i);
Assert.Equal(InvoiceStatusLegacy.Complete, inv.Status);
});
//standard invoice test
s.GoToHome();
i = s.CreateInvoice(store.storeName, 0.0000001m, "BTC");
s.GoToInvoiceCheckout(i);
s.Driver.FindElement(By.ClassName("payment__currencies")).Click();
//bolt 11 is also available for standard invoices
Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".vex.vex-theme-btcpay .vex-content .vexmenu li.vexmenuitem")).Count);
s.Driver.FindElement(By.CssSelector(".vex.vex-theme-btcpay .vex-content .vexmenu li.vexmenuitem")).Click();
s.Driver.FindElement(By.Id("copy-tab")).Click();
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
parsed = LNURL.LNURL.Parse(lnurl, out tag);
fetchedReuqest = Assert.IsType<LNURL.LNURLPayRequest>(await LNURL.LNURL.FetchInformation(parsed, new HttpClient()));
Assert.Equal(0.0000001m, fetchedReuqest.MaxSendable.ToDecimal(LightMoneyUnit.BTC));
Assert.Equal(0.0000001m, fetchedReuqest.MinSendable.ToDecimal(LightMoneyUnit.BTC));
await Assert.ThrowsAsync<HttpRequestException>(async () =>
{
await fetchedReuqest.SendRequest(new LightMoney(0.0000002m, LightMoneyUnit.BTC),
network, new HttpClient());
});
await Assert.ThrowsAsync<HttpRequestException>(async () =>
{
await fetchedReuqest.SendRequest(new LightMoney(0.00000005m, LightMoneyUnit.BTC),
network, new HttpClient());
});
lnurlResponse = await fetchedReuqest.SendRequest(new LightMoney(0.0000001m, LightMoneyUnit.BTC),
network, new HttpClient());
lnurlResponse2 = await fetchedReuqest.SendRequest(new LightMoney(0.0000001m, LightMoneyUnit.BTC),
network, new HttpClient());
//invoice amounts do no change so the paymnet request is not regenerated
Assert.Equal(lnurlResponse.Pr,lnurlResponse2.Pr);
await s.Server.CustomerLightningD.Pay(lnurlResponse.Pr);
Assert.Equal(new LightMoney(0.0000001m, LightMoneyUnit.BTC), lnurlResponse2.GetPaymentRequest(network).MinimumAmount);
s.GoToStore(s.StoreId);
s.AddLightningNode("BTC", LightningConnectionType.CLightning, () =>
{
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), false);
}, false);
i = s.CreateInvoice(store.storeName, 0.000001m, "BTC" );
s.GoToInvoiceCheckout(i);
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
s.GoToHome();
i = s.CreateInvoice(store.storeName, null, "BTC");
s.GoToInvoiceCheckout(i);
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
s.GoToStore(s.StoreId);
s.AddLightningNode("BTC", LightningConnectionType.CLightning, () =>
{
s.Driver.SetCheckbox(By.Id("LNURLBech32Mode"), false);
s.Driver.SetCheckbox(By.Id("DisableBolt11PaymentMethod"), true);
}, false);
s.CreateInvoice(store.storeName, 0.0000001m, "BTC","",null, StatusMessageModel.StatusSeverity.Error);
i = s.CreateInvoice(store.storeName, null, "BTC");
s.GoToInvoiceCheckout(i);
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
s.Driver.FindElement(By.Id("copy-tab")).Click();
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
Assert.StartsWith("lnurlp", lnurl);
LNURL.LNURL.Parse(lnurl, out tag);
s.GoToHome();
var newStore = s.CreateNewStore(false);
s.AddLightningNode("BTC", LightningConnectionType.LndREST, () =>
{
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
s.Driver.SetCheckbox(By.Id("DisableBolt11PaymentMethod"), true);
}, false);
var invForPP = s.CreateInvoice(newStore.storeName, 0.0000001m, "BTC");
s.GoToInvoiceCheckout(invForPP);
s.Driver.FindElement(By.Id("copy-tab")).Click();
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
parsed = LNURL.LNURL.Parse(lnurl, out tag);
//check that pull payment has lightning option
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
Assert.Equal(new PaymentMethodId("BTC", PaymentTypes.LightningLike),PaymentMethodId.Parse(Assert.Single( s.Driver.FindElement(By.Id("PaymentMethods")).FindElements(By.TagName("option"))).GetAttribute("value")));
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
s.Driver.FindElement(By.Id("Amount")).Clear();
s.Driver.FindElement(By.Id("Amount")).SendKeys("0.0000001");;
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.FindElement(By.LinkText("View")).Click();
s.Driver.FindElement(By.Id("Destination")).SendKeys(lnurl);
var pullPaymentId = s.Driver.Url.Split('/').Last();
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("0.0000001" + Keys.Enter);
s.FindAlertMessage();
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
var payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
payouts[0].Click();
s.Driver.FindElement(By.Id("BTC_LightningLike-view")).Click();
Assert.NotEmpty(s.Driver.FindElements(By.ClassName("payout")));
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-selectAllCheckbox")).Click();
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-actions")).Click();
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-approve-pay")).Click();
Assert.Contains(lnurl, s.Driver.PageSource);
s.Driver.FindElement(By.Id("pay-invoices-form")).Submit();
await TestUtils.EventuallyAsync(async () =>
{
var inv = await s.Server.PayTester.InvoiceRepository.GetInvoice(invForPP);
Assert.Equal(InvoiceStatusLegacy.Complete, inv.Status);
await using var ctx = s.Server.PayTester.GetService<ApplicationDbContextFactory>().CreateContext();
var payoutsData = await ctx.Payouts.Where(p => p.PullPaymentDataId == pullPaymentId).ToListAsync();
Assert.True(payoutsData.All(p => p.State == PayoutState.Completed));
});
} }
private static void CanBrowseContent(SeleniumTester s) private static void CanBrowseContent(SeleniumTester s)

View File

@@ -63,7 +63,7 @@
<PackageReference Include="Fido2" Version="2.0.1" /> <PackageReference Include="Fido2" Version="2.0.1" />
<PackageReference Include="Fido2.AspNet" Version="2.0.1" /> <PackageReference Include="Fido2.AspNet" Version="2.0.1" />
<PackageReference Include="HtmlSanitizer" Version="5.0.372" /> <PackageReference Include="HtmlSanitizer" Version="5.0.372" />
<PackageReference Include="LNURL" Version="0.0.8" /> <PackageReference Include="LNURL" Version="0.0.13" />
<PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.4.0" /> <PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.4.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="3.3.2"> <PackageReference Include="Microsoft.NetCore.Analyzers" Version="3.3.2">

View File

@@ -200,7 +200,8 @@ namespace BTCPayServer.Controllers
} }
else else
{ {
vm.LNURLEnabled = !lnSet; //disable by default for now
//vm.LNURLEnabled = !lnSet;
vm.DisableBolt11PaymentMethod = false; vm.DisableBolt11PaymentMethod = false;
} }
} }

View File

@@ -15,9 +15,9 @@ namespace BTCPayServer.Models.StoreViewModels
[Display(Name = "LNURL Classic Mode")] [Display(Name = "LNURL Classic Mode")]
public bool LNURLBech32Mode { get; set; } = true; public bool LNURLBech32Mode { get; set; } = true;
[Display(Name = "LNURL enabled for standard invoices")] [Display(Name = "LNURL enabled for standard invoices")]
public bool LNURLStandardInvoiceEnabled { get; set; } public bool LNURLStandardInvoiceEnabled { get; set; } = true;
[Display(Name = "Allow payee to pass a comment")] [Display(Name = "Allow payee to pass a comment")]
public bool LUD12Enabled { get; set; } public bool LUD12Enabled { get; set; }