2024-08-28 11:52:08 +02:00
using System.Linq ;
using System.Threading.Tasks ;
using BTCPayServer.Payments ;
using Dapper ;
using Microsoft.EntityFrameworkCore ;
2024-09-24 15:07:02 +02:00
using NBitcoin ;
2024-08-28 11:52:08 +02:00
using NBitcoin.Altcoins ;
2024-09-24 15:07:02 +02:00
using NBitpayClient ;
2024-08-28 11:52:08 +02:00
using Newtonsoft.Json.Linq ;
using Xunit ;
using Xunit.Abstractions ;
namespace BTCPayServer.Tests
{
[Trait("Integration", "Integration")]
public class DatabaseTests : UnitTestBase
{
public DatabaseTests ( ITestOutputHelper helper ) : base ( helper )
{
}
2024-09-24 15:07:02 +02:00
[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" ) ;
}
}
2024-09-19 15:15:02 +02:00
[Fact]
public async Task CanMigrateInvoiceAddresses ( )
{
var tester = CreateDBTester ( ) ;
await tester . MigrateUntil ( "20240919085726_refactorinvoiceaddress" ) ;
using var ctx = tester . CreateContext ( ) ;
var conn = ctx . Database . GetDbConnection ( ) ;
await conn . ExecuteAsync ( "INSERT INTO \"Invoices\" (\"Id\", \"Created\") VALUES ('i', NOW())" ) ;
await conn . ExecuteAsync (
"INSERT INTO \"AddressInvoices\" VALUES ('aaa#BTC', 'i'),('bbb','i'),('ccc#BTC_LNU', 'i'),('ddd#XMR_MoneroLike', 'i'),('eee#ZEC_ZcashLike', 'i')" ) ;
await tester . ContinueMigration ( ) ;
foreach ( var v in new [ ] { ( "aaa" , "BTC-CHAIN" ) , ( "bbb" , "BTC-CHAIN" ) , ( "ddd" , "XMR-CHAIN" ) , ( "eee" , "ZEC-CHAIN" ) } )
{
var ok = await conn . ExecuteScalarAsync < bool > ( "SELECT 't'::BOOLEAN FROM \"AddressInvoices\" WHERE \"Address\"=@a AND \"PaymentMethodId\"=@b" , new { a = v . Item1 , b = v . Item2 } ) ;
Assert . True ( ok ) ;
}
var notok = await conn . ExecuteScalarAsync < bool > ( "SELECT 't'::BOOLEAN FROM \"AddressInvoices\" WHERE \"Address\"='ccc'" ) ;
Assert . False ( notok ) ;
}
2024-08-28 11:52:08 +02:00
[Fact]
public async Task CanMigratePayoutsAndPullPayments ( )
{
var tester = CreateDBTester ( ) ;
await tester . MigrateUntil ( "20240827034505_migratepayouts" ) ;
using var ctx = tester . CreateContext ( ) ;
var conn = ctx . Database . GetDbConnection ( ) ;
await conn . ExecuteAsync ( "INSERT INTO \"Stores\"(\"Id\", \"SpeedPolicy\") VALUES (@store, 0)" , new { store = "store1" } ) ;
var param = new
{
Id = "pp1" ,
StoreId = "store1" ,
Blob = "{\"Name\": \"CoinLottery\", \"View\": {\"Email\": null, \"Title\": \"\", \"Description\": \"\", \"EmbeddedCSS\": null, \"CustomCSSLink\": null}, \"Limit\": \"10.00\", \"Period\": null, \"Currency\": \"GBP\", \"Description\": \"\", \"Divisibility\": 0, \"MinimumClaim\": \"0\", \"AutoApproveClaims\": false, \"SupportedPaymentMethods\": [\"BTC\", \"BTC_LightningLike\"]}"
} ;
await conn . ExecuteAsync ( "INSERT INTO \"PullPayments\"(\"Id\", \"StoreId\", \"Blob\", \"StartDate\", \"Archived\") VALUES (@Id, @StoreId, @Blob::JSONB, NOW(), 'f')" , param ) ;
var parameters = new [ ]
{
new
{
Id = "p1" ,
StoreId = "store1" ,
PullPaymentDataId = "pp1" ,
PaymentMethodId = "BTC" ,
Blob = "{\"Amount\": \"10.0\", \"Revision\": 0, \"Destination\": \"address\", \"CryptoAmount\": \"0.00012225\", \"MinimumConfirmation\": 1}"
} ,
new
{
Id = "p2" ,
StoreId = "store1" ,
PullPaymentDataId = "pp1" ,
PaymentMethodId = "BTC_LightningLike" ,
Blob = "{\"Amount\": \"10.0\", \"Revision\": 0, \"Destination\": \"address\", \"CryptoAmount\": null, \"MinimumConfirmation\": 1}"
} ,
new
{
Id = "p3" ,
StoreId = "store1" ,
PullPaymentDataId = null as string ,
PaymentMethodId = "BTC_LightningLike" ,
Blob = "{\"Amount\": \"10.0\", \"Revision\": 0, \"Destination\": \"address\", \"CryptoAmount\": null, \"MinimumConfirmation\": 1}"
2024-09-02 11:37:39 +02:00
} ,
new
{
Id = "p4" ,
StoreId = "store1" ,
PullPaymentDataId = null as string ,
PaymentMethodId = "BTC_LightningLike" ,
Blob = "{\"Amount\": \"-10.0\", \"Revision\": 0, \"Destination\": \"address\", \"CryptoAmount\": null, \"MinimumConfirmation\": 1}"
2024-08-28 11:52:08 +02:00
}
} ;
await conn . ExecuteAsync ( "INSERT INTO \"Payouts\"(\"Id\", \"StoreDataId\", \"PullPaymentDataId\", \"PaymentMethodId\", \"Blob\", \"State\", \"Date\") VALUES (@Id, @StoreId, @PullPaymentDataId, @PaymentMethodId, @Blob::JSONB, 'state', NOW())" , parameters ) ;
await tester . ContinueMigration ( ) ;
var migrated = await conn . ExecuteScalarAsync < bool > ( "SELECT 't'::BOOLEAN FROM \"PullPayments\" WHERE \"Id\"='pp1' AND \"Limit\"=10.0 AND \"Currency\"='GBP' AND \"Blob\"->>'SupportedPayoutMethods'='[\"BTC-CHAIN\", \"BTC-LN\"]'" ) ;
Assert . True ( migrated ) ;
migrated = await conn . ExecuteScalarAsync < bool > ( "SELECT 't'::BOOLEAN FROM \"Payouts\" WHERE \"Id\"='p1' AND \"Amount\"= 0.00012225 AND \"OriginalAmount\"=10.0 AND \"OriginalCurrency\"='GBP' AND \"PayoutMethodId\"='BTC-CHAIN'" ) ;
Assert . True ( migrated ) ;
migrated = await conn . ExecuteScalarAsync < bool > ( "SELECT 't'::BOOLEAN FROM \"Payouts\" WHERE \"Id\"='p2' AND \"Amount\" IS NULL AND \"OriginalAmount\"=10.0 AND \"OriginalCurrency\"='GBP' AND \"PayoutMethodId\"='BTC-LN'" ) ;
Assert . True ( migrated ) ;
migrated = await conn . ExecuteScalarAsync < bool > ( "SELECT 't'::BOOLEAN FROM \"Payouts\" WHERE \"Id\"='p3' AND \"Amount\" IS NULL AND \"OriginalAmount\"=10.0 AND \"OriginalCurrency\"='BTC'" ) ;
Assert . True ( migrated ) ;
2024-09-02 11:37:39 +02:00
migrated = await conn . ExecuteScalarAsync < bool > ( "SELECT 't'::BOOLEAN FROM \"Payouts\" WHERE \"Id\"='p4' AND \"Amount\" IS NULL AND \"OriginalAmount\"=-10.0 AND \"OriginalCurrency\"='BTC' AND \"PayoutMethodId\"='TOPUP'" ) ;
Assert . True ( migrated ) ;
2024-08-28 11:52:08 +02:00
}
}
}