From c97c9d4ece72ce8b42a1aff101bae4dc8b108ebb Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Tue, 24 Sep 2024 22:07:02 +0900 Subject: [PATCH] Add SQL test for GetMonitoredInvoices --- BTCPayServer.Tests/DatabaseTester.cs | 25 +++-- BTCPayServer.Tests/DatabaseTests.cs | 106 +++++++++++++++++++++ BTCPayServer/Data/InvoiceDataExtensions.cs | 2 +- BTCPayServer/Data/PaymentDataExtensions.cs | 6 +- 4 files changed, 126 insertions(+), 13 deletions(-) diff --git a/BTCPayServer.Tests/DatabaseTester.cs b/BTCPayServer.Tests/DatabaseTester.cs index 19a3b98f9..9b373d3a2 100644 --- a/BTCPayServer.Tests/DatabaseTester.cs +++ b/BTCPayServer.Tests/DatabaseTester.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using BTCPayServer.Abstractions.Models; using BTCPayServer.Data; +using BTCPayServer.Services.Invoices; using BTCPayServer.Tests.Logging; using Dapper; using Microsoft.EntityFrameworkCore; @@ -42,6 +43,13 @@ namespace BTCPayServer.Tests }), _loggerFactory); } + public InvoiceRepository GetInvoiceRepository() + { + var logs = new BTCPayServer.Logging.Logs(); + logs.Configure(_loggerFactory); + return new InvoiceRepository(CreateContextFactory(), new EventAggregator(logs)); + } + public ApplicationDbContext CreateContext() => CreateContextFactory().CreateContext(); public async Task MigrateAsync() @@ -59,18 +67,21 @@ namespace BTCPayServer.Tests await conn.ExecuteAsync($"CREATE DATABASE \"{dbname}\";"); } - public async Task MigrateUntil(string migration) + public async Task MigrateUntil(string migration = null) { using var ctx = CreateContext(); var db = ctx.Database.GetDbConnection(); await EnsureCreatedAsync(); var migrations = ctx.Database.GetMigrations().ToArray(); - var untilMigrationIdx = Array.IndexOf(migrations, migration); - if (untilMigrationIdx == -1) - throw new InvalidOperationException($"Migration {migration} not found"); - notAppliedMigrations = migrations[untilMigrationIdx..]; - await db.ExecuteAsync("CREATE TABLE IF NOT EXISTS \"__EFMigrationsHistory\" (\"MigrationId\" TEXT, \"ProductVersion\" TEXT)"); - await db.ExecuteAsync("INSERT INTO \"__EFMigrationsHistory\" VALUES (@migration, '8.0.0')", notAppliedMigrations.Select(m => new { migration = m }).ToArray()); + if (migration is not null) + { + var untilMigrationIdx = Array.IndexOf(migrations, migration); + if (untilMigrationIdx == -1) + throw new InvalidOperationException($"Migration {migration} not found"); + notAppliedMigrations = migrations[untilMigrationIdx..]; + await db.ExecuteAsync("CREATE TABLE IF NOT EXISTS \"__EFMigrationsHistory\" (\"MigrationId\" TEXT, \"ProductVersion\" TEXT)"); + await db.ExecuteAsync("INSERT INTO \"__EFMigrationsHistory\" VALUES (@migration, '8.0.0')", notAppliedMigrations.Select(m => new { migration = m }).ToArray()); + } await ctx.Database.MigrateAsync(); } diff --git a/BTCPayServer.Tests/DatabaseTests.cs b/BTCPayServer.Tests/DatabaseTests.cs index f213fd827..60ee7cc58 100644 --- a/BTCPayServer.Tests/DatabaseTests.cs +++ b/BTCPayServer.Tests/DatabaseTests.cs @@ -3,7 +3,9 @@ using System.Threading.Tasks; using BTCPayServer.Payments; using Dapper; using Microsoft.EntityFrameworkCore; +using NBitcoin; using NBitcoin.Altcoins; +using NBitpayClient; using Newtonsoft.Json.Linq; using Xunit; using Xunit.Abstractions; @@ -18,6 +20,110 @@ namespace BTCPayServer.Tests { } + [Fact] + public async Task CanQueryMonitoredInvoices() + { + var tester = CreateDBTester(); + await tester.MigrateUntil(); + var invoiceRepository = tester.GetInvoiceRepository(); + using var ctx = tester.CreateContext(); + var conn = ctx.Database.GetDbConnection(); + + async Task AddPrompt(string invoiceId, string paymentMethodId, bool activated = true) + { + JObject prompt = new JObject(); + if (!activated) + prompt["inactive"] = true; + prompt["currency"] = "USD"; + var query = """ + UPDATE "Invoices" SET "Blob2" = jsonb_set('{"prompts": {}}'::JSONB || COALESCE("Blob2",'{}'), ARRAY['prompts','@paymentMethodId'], '@prompt'::JSONB) + WHERE "Id" = '@invoiceId' + """; + query = query.Replace("@paymentMethodId", paymentMethodId); + query = query.Replace("@prompt", prompt.ToString()); + query = query.Replace("@invoiceId", invoiceId); + Assert.Equal(1, await conn.ExecuteAsync(query)); + } + + await conn.ExecuteAsync(""" + INSERT INTO "Invoices" ("Id", "Created", "Status","Currency") VALUES + ('BTCOnly', NOW(), 'New', 'USD'), + ('LTCOnly', NOW(), 'New', 'USD'), + ('LTCAndBTC', NOW(), 'New', 'USD'), + ('LTCAndBTCLazy', NOW(), 'New', 'USD') + """); + foreach (var invoiceId in new string[] { "LTCOnly", "LTCAndBTCLazy", "LTCAndBTC" }) + { + await AddPrompt(invoiceId, "LTC-CHAIN", true); + } + foreach (var invoiceId in new string[] { "BTCOnly", "LTCAndBTC" }) + { + await AddPrompt(invoiceId, "BTC-CHAIN", true); + } + await AddPrompt("LTCAndBTCLazy", "BTC-CHAIN", false); + + var btc = PaymentMethodId.Parse("BTC-CHAIN"); + var ltc = PaymentMethodId.Parse("LTC-CHAIN"); + var invoices = await invoiceRepository.GetMonitoredInvoices(btc); + Assert.Equal(2, invoices.Length); + foreach (var invoiceId in new[] { "BTCOnly", "LTCAndBTC" }) + { + Assert.Contains(invoices, i => i.Id == invoiceId); + } + invoices = await invoiceRepository.GetMonitoredInvoices(btc, true); + Assert.Equal(3, invoices.Length); + foreach (var invoiceId in new[] { "BTCOnly", "LTCAndBTC", "LTCAndBTCLazy" }) + { + Assert.Contains(invoices, i => i.Id == invoiceId); + } + + invoices = await invoiceRepository.GetMonitoredInvoices(ltc); + Assert.Equal(3, invoices.Length); + foreach (var invoiceId in new[] { "LTCAndBTC", "LTCAndBTC", "LTCAndBTCLazy" }) + { + Assert.Contains(invoices, i => i.Id == invoiceId); + } + + await conn.ExecuteAsync(""" + INSERT INTO "Payments" ("Id", "InvoiceDataId", "PaymentMethodId", "Status", "Blob2", "Created", "Amount", "Currency") VALUES + ('1','LTCAndBTC', 'LTC-CHAIN', 'Processing', '{}'::JSONB, NOW(), 123, 'USD'), + ('2','LTCAndBTC', 'BTC-CHAIN', 'Processing', '{}'::JSONB, NOW(), 123, 'USD'), + ('3','LTCAndBTC', 'BTC-CHAIN', 'Processing', '{}'::JSONB, NOW(), 123, 'USD'), + ('4','LTCAndBTC', 'BTC-CHAIN', 'Settled', '{}'::JSONB, NOW(), 123, 'USD'); + + INSERT INTO "AddressInvoices" ("InvoiceDataId", "Address", "PaymentMethodId") VALUES + ('LTCAndBTC', 'BTC1', 'BTC-CHAIN'), + ('LTCAndBTC', 'BTC2', 'BTC-CHAIN'), + ('LTCAndBTC', 'LTC1', 'LTC-CHAIN'); + """); + + var invoice = Assert.Single(await invoiceRepository.GetMonitoredInvoices(ltc), i => i.Id == "LTCAndBTC"); + var payment = Assert.Single(invoice.GetPayments(false)); + Assert.Equal("1", payment.Id); + + foreach (var includeNonActivated in new[] { true, false }) + { + invoices = await invoiceRepository.GetMonitoredInvoices(btc, includeNonActivated); + invoice = Assert.Single(invoices, i => i.Id == "LTCAndBTC"); + var payments = invoice.GetPayments(false); + Assert.Equal(3, payments.Count); + + foreach (var paymentId in new[] { "2", "3", "4" }) + { + Assert.Contains(payments, p => p.Id == paymentId); + } + Assert.Equal(2, invoice.Addresses.Count); + foreach (var addr in new[] { "BTC1", "BTC2" }) + { + Assert.Contains(invoice.Addresses, p => p.Address == addr); + } + if (!includeNonActivated) + Assert.DoesNotContain(invoices, i => i.Id == "LTCAndBTCLazy"); + else + Assert.Contains(invoices, i => i.Id == "LTCAndBTCLazy"); + } + } + [Fact] public async Task CanMigrateInvoiceAddresses() { diff --git a/BTCPayServer/Data/InvoiceDataExtensions.cs b/BTCPayServer/Data/InvoiceDataExtensions.cs index 5fb5d9ecd..7475a146e 100644 --- a/BTCPayServer/Data/InvoiceDataExtensions.cs +++ b/BTCPayServer/Data/InvoiceDataExtensions.cs @@ -32,7 +32,7 @@ namespace BTCPayServer.Data #nullable enable public static PayoutMethodId? GetClosestPayoutMethodId(this InvoiceData invoice, IEnumerable pmids) { - var paymentMethodIds = invoice.Payments.Select(o => o.GetPaymentMethodId()).ToArray(); + var paymentMethodIds = invoice.Payments.Select(o => PaymentMethodId.Parse(o.PaymentMethodId)).ToArray(); if (paymentMethodIds.Length == 0) paymentMethodIds = invoice.GetBlob().GetPaymentPrompts().Select(p => p.PaymentMethodId).ToArray(); return PaymentMethodId.GetSimilarities(pmids, paymentMethodIds) diff --git a/BTCPayServer/Data/PaymentDataExtensions.cs b/BTCPayServer/Data/PaymentDataExtensions.cs index cf001cff5..11ca1033d 100644 --- a/BTCPayServer/Data/PaymentDataExtensions.cs +++ b/BTCPayServer/Data/PaymentDataExtensions.cs @@ -38,16 +38,12 @@ namespace BTCPayServer.Data paymentData.Blob2 = JToken.FromObject(blob, InvoiceDataExtensions.DefaultSerializer).ToString(Newtonsoft.Json.Formatting.None); return paymentData; } - public static PaymentMethodId GetPaymentMethodId(this PaymentData paymentData) - { - return PaymentMethodId.Parse(paymentData.PaymentMethodId); - } public static PaymentEntity GetBlob(this PaymentData paymentData) { var entity = JToken.Parse(paymentData.Blob2).ToObject(InvoiceDataExtensions.DefaultSerializer) ?? throw new FormatException($"Invalid {nameof(PaymentEntity)}"); entity.Status = paymentData.Status!.Value; entity.Currency = paymentData.Currency; - entity.PaymentMethodId = GetPaymentMethodId(paymentData); + entity.PaymentMethodId = PaymentMethodId.Parse(paymentData.PaymentMethodId); entity.Value = paymentData.Amount!.Value; entity.Id = paymentData.Id; entity.ReceivedTime = paymentData.Created!.Value;