diff --git a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs index b9be03195..94d4e8836 100644 --- a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs +++ b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs @@ -475,11 +475,11 @@ namespace BTCPayServer.Tests tester.ActivateLTC(); await tester.StartAsync(); var user = tester.NewAccount(); - user.GrantAccess(); + await user.GrantAccessAsync(); user.RegisterDerivationScheme("BTC"); // First we try payment with a merchant having only BTC - var invoice = user.BitPay.CreateInvoice( - new Invoice() + var invoice = await user.BitPay.CreateInvoiceAsync( + new Invoice { Price = 5000.0m, Currency = "USD", @@ -490,10 +490,10 @@ namespace BTCPayServer.Tests }, Facade.Merchant); var cashCow = tester.ExplorerNode; - cashCow.Generate(2); // get some money in case + await cashCow.GenerateAsync(2); // get some money in case var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network); var firstPayment = Money.Coins(0.04m); - cashCow.SendToAddress(invoiceAddress, firstPayment); + await cashCow.SendToAddressAsync(invoiceAddress, firstPayment); TestUtils.Eventually(() => { invoice = user.BitPay.GetInvoice(invoice.Id); @@ -523,8 +523,8 @@ namespace BTCPayServer.Tests // Retry now with LTC enabled user.RegisterDerivationScheme("LTC"); - invoice = user.BitPay.CreateInvoice( - new Invoice() + invoice = await user.BitPay.CreateInvoiceAsync( + new Invoice { Price = 5000.0m, Currency = "USD", @@ -537,7 +537,7 @@ namespace BTCPayServer.Tests cashCow = tester.ExplorerNode; invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network); firstPayment = Money.Coins(0.04m); - cashCow.SendToAddress(invoiceAddress, firstPayment); + await cashCow.SendToAddressAsync(invoiceAddress, firstPayment); TestLogs.LogInformation("First payment sent to " + invoiceAddress); TestUtils.Eventually(() => { @@ -550,8 +550,8 @@ namespace BTCPayServer.Tests Assert.NotNull(ltcCryptoInfo); invoiceAddress = BitcoinAddress.Create(ltcCryptoInfo.Address, cashCow.Network); var secondPayment = Money.Coins(decimal.Parse(ltcCryptoInfo.Due, CultureInfo.InvariantCulture)); - cashCow.Generate(4); // LTC is not worth a lot, so just to make sure we have money... - cashCow.SendToAddress(invoiceAddress, secondPayment); + await cashCow.GenerateAsync(4); // LTC is not worth a lot, so just to make sure we have money... + await cashCow.SendToAddressAsync(invoiceAddress, secondPayment); TestLogs.LogInformation("Second payment sent to " + invoiceAddress); TestUtils.Eventually(() => { @@ -570,7 +570,6 @@ namespace BTCPayServer.Tests Assert.Equal(2, checkout.AvailableCryptos.Count); Assert.Equal("LTC", checkout.CryptoCode); - Assert.Equal(2, invoice.PaymentCodes.Count()); Assert.Equal(2, invoice.SupportedTransactionCurrencies.Count()); Assert.Equal(2, invoice.SupportedTransactionCurrencies.Count()); @@ -581,11 +580,10 @@ namespace BTCPayServer.Tests Assert.True(invoice.SupportedTransactionCurrencies["LTC"].Enabled); Assert.True(invoice.PaymentSubtotals.ContainsKey("LTC")); Assert.True(invoice.PaymentTotals.ContainsKey("LTC")); - - + // Check if we can disable LTC - invoice = user.BitPay.CreateInvoice( - new Invoice() + invoice = await user.BitPay.CreateInvoiceAsync( + new Invoice { Price = 5000.0m, Currency = "USD", diff --git a/BTCPayServer.Tests/ApiKeysTests.cs b/BTCPayServer.Tests/ApiKeysTests.cs index e4e6a61fe..7f9a7243a 100644 --- a/BTCPayServer.Tests/ApiKeysTests.cs +++ b/BTCPayServer.Tests/ApiKeysTests.cs @@ -11,6 +11,7 @@ using BTCPayServer.Views.Manage; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OpenQA.Selenium; +using OpenQA.Selenium.Support.Extensions; using Xunit; using Xunit.Abstractions; using StoreData = BTCPayServer.Data.StoreData; @@ -19,7 +20,7 @@ namespace BTCPayServer.Tests { public class ApiKeysTests : UnitTestBase { - public const int TestTimeout = TestUtils.TestTimeout; + public const int TestTimeout = 120_000; public const string TestApiPath = "api/test/apikey"; public ApiKeysTests(ITestOutputHelper helper) : base(helper) @@ -98,7 +99,7 @@ namespace BTCPayServer.Tests var option = dropdown.FindElement(By.TagName("option")); var storeId = option.GetAttribute("value"); option.Click(); - s.Driver.FindElement(By.Id("Generate")).Click(); + s.Driver.WaitForAndClick(By.Id("Generate")); var selectiveStoreApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; TestLogs.LogInformation("Checking CanModifyStoreSettings with StoreId permissions"); @@ -106,12 +107,13 @@ namespace BTCPayServer.Tests await TestApiAgainstAccessToken(selectiveStoreApiKey, tester, user, Permission.Create(Policies.CanModifyStoreSettings, storeId).ToString()); - s.Driver.FindElement(By.Id("AddApiKey")).Click(); - s.Driver.FindElement(By.Id("Generate")).Click(); + TestLogs.LogInformation("Adding API key for no permissions"); + s.Driver.WaitForAndClick(By.Id("AddApiKey")); + TestLogs.LogInformation("Generating API key for no permissions"); + s.Driver.WaitForAndClick(By.Id("Generate")); var noPermissionsApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; - TestLogs.LogInformation("Checking no permissions"); - + TestLogs.LogInformation($"Checking no permissions: {noPermissionsApiKey}"); await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user); await Assert.ThrowsAnyAsync(async () => @@ -135,26 +137,35 @@ namespace BTCPayServer.Tests var callbackUrl = s.ServerUri + "postredirect-callback-test"; var authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString(); - s.Driver.Navigate().GoToUrl(authUrl); + TestLogs.LogInformation($"Going to auth URL {authUrl}"); + s.GoToUrl(authUrl); Assert.Contains(appidentifier, s.Driver.PageSource); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant()); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant()); Assert.DoesNotContain("change-store-mode", s.Driver.PageSource); - s.Driver.FindElement(By.Id("consent-yes")).Click(); + + TestLogs.LogInformation("Going to callback URL"); + + s.Driver.WaitForAndClick(By.Id("consent-yes")); Assert.Equal(callbackUrl, s.Driver.Url); + TestLogs.LogInformation("On callback URL"); var apiKeyRepo = s.Server.PayTester.GetService(); var accessToken = GetAccessTokenFromCallbackResult(s.Driver); + TestLogs.LogInformation($"Access token: {accessToken}"); + await TestApiAgainstAccessToken(accessToken, tester, user, (await apiKeyRepo.GetKey(accessToken)).GetBlob().Permissions); authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, applicationDetails: (null, new Uri(callbackUrl))).ToString(); - s.Driver.Navigate().GoToUrl(authUrl); + TestLogs.LogInformation($"Going to auth URL 2 {authUrl}"); + s.GoToUrl(authUrl); + TestLogs.LogInformation("On auth URL 2"); Assert.DoesNotContain("kukksappname", s.Driver.PageSource); Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant()); @@ -164,11 +175,14 @@ namespace BTCPayServer.Tests s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), false); Assert.Contains("change-store-mode", s.Driver.PageSource); - s.Driver.FindElement(By.Id("consent-yes")).Click(); + + TestLogs.LogInformation("Going to callback URL 2"); + s.Driver.WaitForAndClick(By.Id("consent-yes")); Assert.Equal(callbackUrl, s.Driver.Url); + TestLogs.LogInformation("On callback URL 2"); accessToken = GetAccessTokenFromCallbackResult(s.Driver); - + TestLogs.LogInformation($"Access token: {accessToken}"); TestLogs.LogInformation("Checking authorized permissions"); await TestApiAgainstAccessToken(accessToken, tester, user, @@ -179,39 +193,43 @@ namespace BTCPayServer.Tests new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri(callbackUrl))).ToString(); //if it's the same, go to the confirm page - s.Driver.Navigate().GoToUrl(authUrl); - s.Driver.FindElement(By.Id("continue")).Click(); + TestLogs.LogInformation($"Going to auth URL 3 {authUrl}"); + s.GoToUrl(authUrl); + TestLogs.LogInformation("On auth URL 3"); + s.Driver.WaitForAndClick(By.Id("continue")); + TestLogs.LogInformation("Going to callback URL 3"); Assert.Equal(callbackUrl, s.Driver.Url); + TestLogs.LogInformation("On callback URL 3"); //same app but different redirect = nono authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri("https://international.local/callback"))).ToString(); - s.Driver.Navigate().GoToUrl(authUrl); + TestLogs.LogInformation($"Going to auth URL 4 {authUrl}"); + s.GoToUrl(authUrl); + TestLogs.LogInformation("On auth URL 4"); Assert.False(s.Driver.Url.StartsWith("https://international.com/callback")); // Make sure we can check all permissions when not an admin + TestLogs.LogInformation("Make sure we can check all permissions when not an admin"); await user.MakeAdmin(false); s.Logout(); s.GoToLogin(); s.LogIn(user.RegisterDetails.Email, user.RegisterDetails.Password); - s.GoToProfile(ManageNavPages.APIKeys); - s.Driver.FindElement(By.Id("AddApiKey")).Click(); - int checkedPermissionCount = 0; - foreach (var checkbox in s.Driver.FindElements(By.ClassName("form-check-input"))) - { - checkedPermissionCount++; - s.Driver.ScrollTo(checkbox); - checkbox.Click(); - } + TestLogs.LogInformation("Go to API Keys page"); + s.GoToUrl("/account/apikeys"); + TestLogs.LogInformation("On API Keys page"); + s.Driver.WaitForAndClick(By.Id("AddApiKey")); + int checkedPermissionCount = s.Driver.FindElements(By.ClassName("form-check-input")).Count; + TestLogs.LogInformation($"Adding API key: {checkedPermissionCount} permissions"); + s.Driver.ExecuteJavaScript("document.querySelectorAll('#Permissions .form-check-input').forEach(i => i.click())"); + TestLogs.LogInformation($"Clicked {checkedPermissionCount}"); - var generate = s.Driver.FindElement(By.Id("Generate")); - s.Driver.ScrollTo(generate); - generate.Click(); + TestLogs.LogInformation("Generating API key"); + s.Driver.WaitForAndClick(By.Id("Generate")); var allAPIKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; - TestLogs.LogInformation("Checking API key permissions"); - + TestLogs.LogInformation($"Checking API key permissions: {allAPIKey}"); var apikeydata = await TestApiAgainstAccessToken(allAPIKey, "api/v1/api-keys/current", tester.PayTester.HttpClient); Assert.Equal(checkedPermissionCount, apikeydata.Permissions.Length); } @@ -223,12 +241,14 @@ namespace BTCPayServer.Tests expectedPermissions ??= new Permission[0]; var apikeydata = await TestApiAgainstAccessToken(accessToken, $"api/v1/api-keys/current", tester.PayTester.HttpClient); var permissions = apikeydata.Permissions; + TestLogs.LogInformation($"TestApiAgainstAccessToken: Permissions {permissions.Length}"); Assert.Equal(expectedPermissions.Length, permissions.Length); foreach (var expectPermission in expectedPermissions) { Assert.True(permissions.Any(p => p == expectPermission), $"Missing expected permission {expectPermission}"); } + TestLogs.LogInformation("Testing CanViewProfile"); if (permissions.Contains(Permission.Create(Policies.CanViewProfile))) { var resultUser = await TestApiAgainstAccessToken(accessToken, $"{TestApiPath}/me/id", tester.PayTester.HttpClient); @@ -241,14 +261,17 @@ namespace BTCPayServer.Tests await TestApiAgainstAccessToken(accessToken, $"{TestApiPath}/me/id", tester.PayTester.HttpClient); }); } - //create a second user to see if any of its data gets messed upin our results. + //create a second user to see if any of its data gets messed up in our results. + TestLogs.LogInformation("Testing second user"); var secondUser = tester.NewAccount(); - secondUser.GrantAccess(); + await secondUser.GrantAccessAsync(); var canModifyAllStores = Permission.Create(Policies.CanModifyStoreSettings, null); var canModifyServer = Permission.Create(Policies.CanModifyServerSettings, null); var unrestricted = Permission.Create(Policies.Unrestricted, null); var selectiveStorePermissions = permissions.Where(p => p.Scope != null && p.Policy == Policies.CanModifyStoreSettings); + + TestLogs.LogInformation("Testing can edit store for first user"); if (permissions.Contains(canModifyAllStores) || selectiveStorePermissions.Any()) { var resultStores = @@ -308,7 +331,6 @@ namespace BTCPayServer.Tests } else if (!permissions.Contains(unrestricted)) { - await Assert.ThrowsAnyAsync(async () => { await TestApiAgainstAccessToken(accessToken, @@ -323,6 +345,7 @@ namespace BTCPayServer.Tests tester.PayTester.HttpClient); } + TestLogs.LogInformation("Testing can edit store for second user"); if (!permissions.Contains(unrestricted)) { await Assert.ThrowsAnyAsync(async () => @@ -336,7 +359,9 @@ namespace BTCPayServer.Tests await TestApiAgainstAccessToken(accessToken, $"{TestApiPath}/me/stores/{secondUser.StoreId}/can-edit", tester.PayTester.HttpClient); } + TestLogs.LogInformation("Testing can edit store for second user expectation met"); + TestLogs.LogInformation($"Testing CanModifyServer with {permissions.Contains(canModifyServer)}"); if (permissions.Contains(canModifyServer)) { Assert.True(await TestApiAgainstAccessToken(accessToken, @@ -352,17 +377,21 @@ namespace BTCPayServer.Tests tester.PayTester.HttpClient); }); } + TestLogs.LogInformation("Testing CanModifyServer expectation met"); } public async Task TestApiAgainstAccessToken(string apikey, string url, HttpClient client) { - var httpRequest = new HttpRequestMessage(HttpMethod.Get, - new Uri(client.BaseAddress, url)); + var uri = new Uri(client.BaseAddress, url); + var httpRequest = new HttpRequestMessage(HttpMethod.Get, uri); httpRequest.Headers.Authorization = new AuthenticationHeaderValue("token", apikey); + TestLogs.LogInformation($"Testing {uri}"); var result = await client.SendAsync(httpRequest); + TestLogs.LogInformation($"Testing {uri} status: {result.StatusCode}"); result.EnsureSuccessStatusCode(); var rawJson = await result.Content.ReadAsStringAsync(); + TestLogs.LogInformation($"Testing {uri} result: {rawJson}"); if (typeof(T).IsPrimitive || typeof(T) == typeof(string)) { return (T)Convert.ChangeType(rawJson, typeof(T)); diff --git a/BTCPayServer.Tests/SeleniumTester.cs b/BTCPayServer.Tests/SeleniumTester.cs index 2c40d8248..17eb1fad8 100644 --- a/BTCPayServer.Tests/SeleniumTester.cs +++ b/BTCPayServer.Tests/SeleniumTester.cs @@ -458,11 +458,11 @@ namespace BTCPayServer.Tests public void GoToProfile(ManageNavPages navPages = ManageNavPages.Index) { - Driver.FindElement(By.Id("Nav-Account")).Click(); - Driver.FindElement(By.Id("Nav-ManageAccount")).Click(); + Driver.WaitForAndClick(By.Id("Nav-Account")); + Driver.WaitForAndClick(By.Id("Nav-ManageAccount")); if (navPages != ManageNavPages.Index) { - Driver.FindElement(By.Id($"SectionNav-{navPages.ToString()}")).Click(); + Driver.WaitForAndClick(By.Id($"SectionNav-{navPages.ToString()}")); } } diff --git a/BTCPayServer.Tests/docker-compose.altcoins.yml b/BTCPayServer.Tests/docker-compose.altcoins.yml index 10574fa17..ed7521024 100644 --- a/BTCPayServer.Tests/docker-compose.altcoins.yml +++ b/BTCPayServer.Tests/docker-compose.altcoins.yml @@ -1,4 +1,4 @@ -version: "3" +version: "3" # Run `docker-compose up dev` for bootstrapping your development environment # Doing so will expose NBXplorer, Bitcoind RPC and postgres port to the host so that tests can Run, @@ -35,7 +35,7 @@ services: links: - dev - selenium - extra_hosts: + extra_hosts: - "tests:127.0.0.1" volumes: - "sshd_datadir:/root/.ssh" @@ -43,7 +43,7 @@ services: - "merchant_lightningd_datadir:/etc/merchant_lightningd_datadir" # The dev container is not actually used, it is just handy to run `docker-compose up dev` to start all services - dev: + dev: image: alpine:3.7 command: [ "/bin/sh", "-c", "trap : TERM INT; while :; do echo Ready to code and debug like a rockstar!!!; sleep 2073600; done & wait" ] links: @@ -69,7 +69,7 @@ services: volumes: - "sshd_datadir:/root/.ssh" - devlnd: + devlnd: image: btcpayserver/bitcoin:22.0 environment: BITCOIN_NETWORK: regtest @@ -93,7 +93,7 @@ services: restart: unless-stopped ports: - "32838:32838" - expose: + expose: - "32838" environment: NBXPLORER_NETWORK: regtest @@ -141,7 +141,7 @@ services: zmqpubrawtx=tcp://0.0.0.0:28333 deprecatedrpc=signrawtransaction fallbackfee=0.0002 - ports: + ports: - "43782:43782" - "39388:39388" expose: @@ -156,7 +156,7 @@ services: image: btcpayserver/lightning:v0.10.1-1-dev stop_signal: SIGKILL restart: unless-stopped - environment: + environment: EXPOSE_TCP: "true" LIGHTNINGD_CHAIN: "btc" LIGHTNINGD_NETWORK: "regtest" @@ -204,7 +204,7 @@ services: merchant_lightningd: image: btcpayserver/lightning:v0.10.1-1-dev stop_signal: SIGKILL - environment: + environment: EXPOSE_TCP: "true" LIGHTNINGD_CHAIN: "btc" LIGHTNINGD_NETWORK: "regtest" @@ -337,7 +337,7 @@ services: volumes: - "./monero_wallet:/wallet" depends_on: - - monerod + - monerod litecoind: restart: unless-stopped image: btcpayserver/litecoin:0.18.1 @@ -351,12 +351,12 @@ services: rpcbind=0.0.0.0:43782 port=39388 whitelist=0.0.0.0/0 - ports: + ports: - "43783:43782" expose: - "43782" # RPC - "39388" # P2P - + elementsd-liquid: restart: always container_name: btcpayserver_elementsd_liquid diff --git a/BTCPayServer/Views/UIManage/APIKeys.cshtml b/BTCPayServer/Views/UIManage/APIKeys.cshtml index 548b788fb..d8f2ccb5e 100644 --- a/BTCPayServer/Views/UIManage/APIKeys.cshtml +++ b/BTCPayServer/Views/UIManage/APIKeys.cshtml @@ -5,7 +5,7 @@ }