Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
gruve-p committed Jul 29, 2024
2 parents ea965a4 + 7878a43 commit e5aac2e
Show file tree
Hide file tree
Showing 150 changed files with 2,154 additions and 370 deletions.
1 change: 1 addition & 0 deletions BTCPayServer.Abstractions/Configuration/DataDirectories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class DataDirectories
public string TempStorageDir { get; set; }
public string StorageDir { get; set; }
public string TempDir { get; set; }
public string LangsDir { get; set; }

public string ToDatadirFullPath(string path)
{
Expand Down
3 changes: 2 additions & 1 deletion BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;

Expand Down Expand Up @@ -58,7 +59,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
return null;
return TimeSpan.Zero;
}
if (reader.TokenType == JsonToken.String && TimeSpan.TryParse(reader.Value?.ToString(), out var res))
if (reader.TokenType == JsonToken.String && TimeSpan.TryParse(reader.Value?.ToString(), CultureInfo.InvariantCulture, out var res))
return res;
if (reader.TokenType != JsonToken.Integer)
throw new JsonObjectException("Invalid timespan, expected integer", reader);
Expand Down
4 changes: 4 additions & 0 deletions BTCPayServer.Client/Models/AppBaseData.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace BTCPayServer.Client.Models;

Expand All @@ -13,6 +15,8 @@ public class AppBaseData
public bool? Archived { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset Created { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
}

public interface IAppRequest
Expand Down
2 changes: 0 additions & 2 deletions BTCPayServer.Client/Models/PointOfSaleAppData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public abstract class PointOfSaleBaseData : AppBaseData
public bool? RedirectAutomatically { get; set; }
public int[]? CustomTipPercentages { get; set; }
public string? FormId { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
}

public class PointOfSaleAppData : PointOfSaleBaseData
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20231219031609_translationsmigration")]
public partial class translationsmigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql("""
CREATE TABLE lang_dictionaries (
dict_id TEXT PRIMARY KEY,
fallback TEXT DEFAULT NULL,
source TEXT DEFAULT NULL,
metadata JSONB DEFAULT NULL,
FOREIGN KEY (fallback) REFERENCES lang_dictionaries(dict_id) ON UPDATE CASCADE ON DELETE SET NULL
);
INSERT INTO lang_dictionaries(dict_id, source) VALUES ('English', 'Default');
CREATE TABLE lang_translations (
dict_id TEXT NOT NULL,
sentence TEXT NOT NULL,
translation TEXT NOT NULL,
PRIMARY KEY (dict_id, sentence),
FOREIGN KEY (dict_id) REFERENCES lang_dictionaries(dict_id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE VIEW translations AS
WITH RECURSIVE translations_with_paths AS (
SELECT d.dict_id, t.sentence, t.translation, ARRAY[d.dict_id] AS path FROM lang_translations t
INNER JOIN lang_dictionaries d USING (dict_id)
UNION ALL
SELECT d.dict_id, t.sentence, t.translation, d.dict_id || t.path FROM translations_with_paths t
INNER JOIN lang_dictionaries d ON d.fallback=t.dict_id
),
ranked_translations AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dict_id, sentence ORDER BY array_length(path, 1)) AS rn
FROM translations_with_paths
)
SELECT dict_id, sentence, translation, path FROM ranked_translations WHERE rn=1;
COMMENT ON VIEW translations IS 'Compute the translation for all sentences for all dictionaries, taking into account fallbacks';
""");
}
}

protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
if (ticker != null)
{
var pair = GetCurrencyPair(symbol);
if (pair is not null)
if (pair is not null && ticker.Bid <= ticker.Ask)
result.Add(new PairRate(pair, new BidAsk(ticker.Bid, ticker.Ask)));
}
}
Expand Down
26 changes: 13 additions & 13 deletions BTCPayServer.Tests/ApiKeysTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async Task CanCreateApiKeys()
s.GoToLogin();
s.LogIn(user.RegisterDetails.Email, user.RegisterDetails.Password);
s.GoToProfile(ManageNavPages.APIKeys);
s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.ClickPagePrimary();

//not an admin, so this permission should not show
Assert.DoesNotContain("btcpay.server.canmodifyserversettings", s.Driver.PageSource);
Expand All @@ -56,34 +56,34 @@ public async Task CanCreateApiKeys()
s.GoToLogin();
s.LogIn(user.RegisterDetails.Email, user.RegisterDetails.Password);
s.GoToProfile(ManageNavPages.APIKeys);
s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.ClickPagePrimary();
Assert.Contains("btcpay.server.canmodifyserversettings", s.Driver.PageSource);

//server management should show now
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true);
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
s.Driver.SetCheckbox(By.Id("btcpay.user.canviewprofile"), true);
s.Driver.FindElement(By.Id("Generate")).Click();
s.ClickPagePrimary();
var superApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;

//this api key has access to everything
await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings, Policies.CanModifyStoreSettings, Policies.CanViewProfile);

s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.ClickPagePrimary();
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true);
s.Driver.FindElement(By.Id("Generate")).Click();
s.ClickPagePrimary();
var serverOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user,
Policies.CanModifyServerSettings);

s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.ClickPagePrimary();
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
s.Driver.FindElement(By.Id("Generate")).Click();
s.ClickPagePrimary();
var allStoreOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user,
Policies.CanModifyStoreSettings);

s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.ClickPagePrimary();
s.Driver.FindElement(By.CssSelector("button[value='btcpay.store.canmodifystoresettings:change-store-mode']")).Click();
//there should be a store already by default in the dropdown
var getPermissionValueIndex =
Expand All @@ -94,13 +94,13 @@ await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user,
var option = dropdown.FindElement(By.TagName("option"));
var storeId = option.GetAttribute("value");
option.Click();
s.Driver.WaitForAndClick(By.Id("Generate"));
s.ClickPagePrimary();
var selectiveStoreApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(selectiveStoreApiKey, tester, user,
Permission.Create(Policies.CanModifyStoreSettings, storeId).ToString());

s.Driver.WaitForAndClick(By.Id("AddApiKey"));
s.Driver.WaitForAndClick(By.Id("Generate"));
s.ClickPagePrimary(); // New API key
s.ClickPagePrimary(); // Generate
var noPermissionsApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user);
await Assert.ThrowsAnyAsync<HttpRequestException>(async () =>
Expand Down Expand Up @@ -223,12 +223,12 @@ await TestApiAgainstAccessToken(accessToken, tester, user,
s.GoToLogin();
s.LogIn(user.RegisterDetails.Email, user.RegisterDetails.Password);
s.GoToUrl("/account/apikeys");
s.Driver.WaitForAndClick(By.Id("AddApiKey"));
s.ClickPagePrimary();
int checkedPermissionCount = s.Driver.FindElements(By.ClassName("form-check-input")).Count;
s.Driver.ExecuteJavaScript("document.querySelectorAll('#Permissions .form-check-input').forEach(i => i.click())");

TestLogs.LogInformation("Generating API key");
s.Driver.WaitForAndClick(By.Id("Generate"));
s.ClickPagePrimary();
var allAPIKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;

TestLogs.LogInformation($"Checking API key permissions: {allAPIKey}");
Expand Down
2 changes: 2 additions & 0 deletions BTCPayServer.Tests/BTCPayServer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@

<ItemGroup>
<None Remove="TestData\OldInvoices.csv" />
<None Remove="TestData\Langs\Cypherpunk" />
</ItemGroup>

<ItemGroup>
<Content Include="TestData\OldInvoices.csv" />
<Content Include="TestData\Langs\Cypherpunk" />
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 8 additions & 2 deletions BTCPayServer.Tests/BTCPayServerTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public enum TestDatabases

public class BTCPayServerTester : IDisposable
{
private readonly string _Directory;

internal readonly string _Directory;
public ILoggerProvider LoggerProvider { get; }

ILog TestLogs;
Expand Down Expand Up @@ -95,6 +94,13 @@ public TestDatabases TestDatabase
get; set;
}

public async Task RestartStartupTask<T>()
{
var startupTask = GetService<IServiceProvider>().GetServices<Abstractions.Contracts.IStartupTask>()
.Single(task => task is T);
await startupTask.ExecuteAsync();
}

public bool MockRates { get; set; } = true;
public string SocksEndpoint { get; set; }

Expand Down
15 changes: 7 additions & 8 deletions BTCPayServer.Tests/CheckoutUITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ public async Task CanConfigureCheckout()
var supportUrl = "https://support.satoshisteaks.com/{InvoiceId}/";
s.GoToStore();
s.Driver.FindElement(By.Id("StoreWebsite")).SendKeys(storeUrl);
s.Driver.FindElement(By.Id("Save")).Click();
s.ClickPagePrimary();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);

s.GoToStore(StoreNavPages.CheckoutAppearance);
s.Driver.WaitForAndClick(By.Id("Presets"));
s.Driver.WaitForAndClick(By.Id("Presets_InStore"));
Assert.True(s.Driver.SetCheckbox(By.Id("ShowPayInWalletButton"), true));
s.Driver.FindElement(By.Id("SupportUrl")).SendKeys(supportUrl);
s.Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter);
s.ClickPagePrimary();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);

// Top up/zero amount invoices
Expand Down Expand Up @@ -105,7 +105,7 @@ public async Task CanConfigureCheckout()
s.GoToHome();
s.GoToLightningSettings();
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
s.Driver.FindElement(By.Id("save")).Click();
s.ClickPagePrimary();
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
s.GoToInvoiceCheckout(invoiceId);
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
Expand Down Expand Up @@ -251,7 +251,7 @@ await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Ne
s.GoToStore(StoreNavPages.CheckoutAppearance);
s.Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), true);
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), false);
s.Driver.FindElement(By.Id("Save")).Click();
s.ClickPagePrimary();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);

invoiceId = s.CreateInvoice();
Expand Down Expand Up @@ -285,7 +285,7 @@ await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Ne
s.GoToHome();
s.GoToStore(StoreNavPages.CheckoutAppearance);
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
s.Driver.FindElement(By.Id("Save")).Click();
s.ClickPagePrimary();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
s.GoToInvoiceCheckout(invoiceId);
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
Expand Down Expand Up @@ -368,7 +368,7 @@ await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Ne
Assert.Equal("5", displayExpirationTimer.GetAttribute("value"));
displayExpirationTimer.Clear();
displayExpirationTimer.SendKeys("10");
s.Driver.FindElement(By.Id("Save")).Click();
s.ClickPagePrimary();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);

s.GoToInvoiceCheckout(invoiceId);
Expand All @@ -392,8 +392,7 @@ await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Ne
s.GoToHome();
s.GoToLightningSettings();
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), false);
s.Driver.ScrollTo(By.Id("save"));
s.Driver.FindElement(By.Id("save")).Click();
s.ClickPagePrimary();
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);

// Test:
Expand Down
Loading

0 comments on commit e5aac2e

Please sign in to comment.