btcpayserver/BTCPayServer.Tests/CrowdfundTests.cs

282 lines
16 KiB
C#
Raw Normal View History

2019-01-04 16:42:35 +01:00
using System;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
2019-01-04 16:42:35 +01:00
using BTCPayServer.Controllers;
using BTCPayServer.Data;
2019-01-04 16:42:35 +01:00
using BTCPayServer.Models.AppViewModels;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Tests.Logging;
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
using NBitpayClient;
using Xunit;
using Xunit.Abstractions;
using static BTCPayServer.Tests.UnitTest1;
2019-01-04 16:42:35 +01:00
namespace BTCPayServer.Tests
{
2021-11-22 09:16:08 +01:00
public class CrowdfundTests : UnitTestBase
2019-01-04 16:42:35 +01:00
{
2021-11-22 09:16:08 +01:00
public CrowdfundTests(ITestOutputHelper helper) : base(helper)
2019-01-04 16:42:35 +01:00
{
}
[Fact(Timeout = LongRunningTestTimeout)]
2019-01-04 16:42:35 +01:00
[Trait("Integration", "Integration")]
2019-10-07 09:04:25 +02:00
public async Task CanCreateAndDeleteCrowdfundApp()
2019-01-04 16:42:35 +01:00
{
2021-11-22 09:16:08 +01:00
using (var tester = CreateServerTester())
2019-01-04 16:42:35 +01:00
{
2019-10-07 09:04:25 +02:00
await tester.StartAsync();
2019-01-04 16:42:35 +01:00
var user = tester.NewAccount();
await user.GrantAccessAsync();
2019-01-04 16:42:35 +01:00
var user2 = tester.NewAccount();
await user2.GrantAccessAsync();
2019-01-04 16:42:35 +01:00
var apps = user.GetController<AppsController>();
var apps2 = user2.GetController<AppsController>();
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
2019-01-04 16:42:35 +01:00
Assert.NotNull(vm.SelectedAppType);
Assert.Null(vm.AppName);
vm.AppName = "test";
2019-01-04 16:42:35 +01:00
vm.SelectedAppType = AppType.Crowdfund.ToString();
var redirectToAction = Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
2019-01-05 19:47:39 +01:00
Assert.Equal(nameof(apps.UpdateCrowdfund), redirectToAction.ActionName);
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
var app = appList.Apps[0];
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
2019-01-04 16:42:35 +01:00
var appList2 =
Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps2.ListApps(user2.StoreId).Result).Model);
2019-01-04 16:42:35 +01:00
Assert.Single(appList.Apps);
Assert.Empty(appList2.Apps);
Assert.Equal("test", appList.Apps[0].AppName);
Assert.Equal(apps.CreatedAppId, appList.Apps[0].Id);
Assert.True(appList.Apps[0].IsOwner);
Assert.Equal(user.StoreId, appList.Apps[0].StoreId);
Assert.IsType<NotFoundResult>(apps2.DeleteApp(appList.Apps[0].Id));
Assert.IsType<ViewResult>(apps.DeleteApp(appList.Apps[0].Id));
2019-01-04 16:42:35 +01:00
redirectToAction = Assert.IsType<RedirectToActionResult>(apps.DeleteAppPost(appList.Apps[0].Id).Result);
Assert.Equal(nameof(apps.ListApps), redirectToAction.ActionName);
appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
2019-01-04 16:42:35 +01:00
Assert.Empty(appList.Apps);
}
}
2020-06-28 10:55:27 +02:00
[Fact(Timeout = LongRunningTestTimeout)]
2019-01-04 16:42:35 +01:00
[Trait("Integration", "Integration")]
public async Task CanContributeOnlyWhenAllowed()
{
2021-11-22 09:16:08 +01:00
using (var tester = CreateServerTester())
2019-01-04 16:42:35 +01:00
{
2019-10-07 09:04:25 +02:00
await tester.StartAsync();
2019-01-04 16:42:35 +01:00
var user = tester.NewAccount();
await user.GrantAccessAsync();
2019-01-04 16:42:35 +01:00
user.RegisterDerivationScheme("BTC");
var apps = user.GetController<AppsController>();
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
vm.AppName = "test";
2019-01-04 16:42:35 +01:00
vm.SelectedAppType = AppType.Crowdfund.ToString();
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
var app = appList.Apps[0];
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
//Scenario 1: Not Enabled - Not Allowed
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
.IsType<ViewResult>(apps.UpdateCrowdfund(app.Id)).Model);
2019-01-05 19:47:39 +01:00
crowdfundViewModel.TargetCurrency = "BTC";
2019-01-04 16:42:35 +01:00
crowdfundViewModel.Enabled = false;
crowdfundViewModel.EndDate = null;
2020-06-28 10:55:27 +02:00
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
var anonAppPubsController = tester.PayTester.GetController<AppsPublicController>();
var publicApps = user.GetController<AppsPublicController>();
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
2019-01-04 16:42:35 +01:00
{
Amount = new decimal(0.01)
2019-03-05 09:29:52 +01:00
}, default));
2020-06-28 10:55:27 +02:00
Assert.IsType<NotFoundResult>(await anonAppPubsController.ViewCrowdfund(app.Id, string.Empty));
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
//Scenario 2: Not Enabled But Admin - Allowed
Assert.IsType<OkObjectResult>(await publicApps.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
2019-01-04 16:42:35 +01:00
{
RedirectToCheckout = false,
Amount = new decimal(0.01)
2019-03-05 09:29:52 +01:00
}, default));
Assert.IsType<ViewResult>(await publicApps.ViewCrowdfund(app.Id, string.Empty));
Assert.IsType<NotFoundResult>(await anonAppPubsController.ViewCrowdfund(app.Id, string.Empty));
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
//Scenario 3: Enabled But Start Date > Now - Not Allowed
2020-06-28 10:55:27 +02:00
crowdfundViewModel.StartDate = DateTime.Today.AddDays(2);
2019-01-04 16:42:35 +01:00
crowdfundViewModel.Enabled = true;
2020-06-28 10:55:27 +02:00
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
2019-01-04 16:42:35 +01:00
{
Amount = new decimal(0.01)
2019-03-05 09:29:52 +01:00
}, default));
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
//Scenario 4: Enabled But End Date < Now - Not Allowed
2020-06-28 10:55:27 +02:00
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
crowdfundViewModel.EndDate = DateTime.Today.AddDays(-1);
2019-01-04 16:42:35 +01:00
crowdfundViewModel.Enabled = true;
2020-06-28 10:55:27 +02:00
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
2019-01-04 16:42:35 +01:00
{
Amount = new decimal(0.01)
2019-03-05 09:29:52 +01:00
}, default));
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
//Scenario 5: Enabled and within correct timeframe, however target is enforced and Amount is Over - Not Allowed
2020-06-28 10:55:27 +02:00
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
crowdfundViewModel.EndDate = DateTime.Today.AddDays(2);
2019-01-04 16:42:35 +01:00
crowdfundViewModel.Enabled = true;
crowdfundViewModel.TargetAmount = 1;
crowdfundViewModel.TargetCurrency = "BTC";
crowdfundViewModel.EnforceTargetAmount = true;
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
2019-01-04 16:42:35 +01:00
{
Amount = new decimal(1.01)
2019-03-05 09:29:52 +01:00
}, default));
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
//Scenario 6: Allowed
Assert.IsType<OkObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
2019-01-04 16:42:35 +01:00
{
Amount = new decimal(0.05)
2019-03-05 09:29:52 +01:00
}, default));
2019-01-04 16:42:35 +01:00
}
}
[Fact(Timeout = LongRunningTestTimeout)]
2019-01-04 16:42:35 +01:00
[Trait("Integration", "Integration")]
2019-10-07 09:04:25 +02:00
public async Task CanComputeCrowdfundModel()
2019-01-04 16:42:35 +01:00
{
2021-11-22 09:16:08 +01:00
using (var tester = CreateServerTester())
2019-01-04 16:42:35 +01:00
{
2019-10-07 09:04:25 +02:00
await tester.StartAsync();
2019-01-04 16:42:35 +01:00
var user = tester.NewAccount();
await user.GrantAccessAsync();
2019-01-04 16:42:35 +01:00
user.RegisterDerivationScheme("BTC");
await user.SetNetworkFeeMode(NetworkFeeMode.Never);
2019-01-04 16:42:35 +01:00
var apps = user.GetController<AppsController>();
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
vm.AppName = "test";
2019-01-04 16:42:35 +01:00
vm.SelectedAppType = AppType.Crowdfund.ToString();
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
var app = appList.Apps[0];
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation("We create an invoice with a hardcap");
2019-01-04 16:42:35 +01:00
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
.IsType<ViewResult>(apps.UpdateCrowdfund(app.Id)).Model);
2019-01-04 16:42:35 +01:00
crowdfundViewModel.Enabled = true;
crowdfundViewModel.EndDate = null;
crowdfundViewModel.TargetAmount = 100;
crowdfundViewModel.TargetCurrency = "BTC";
crowdfundViewModel.UseAllStoreInvoices = true;
crowdfundViewModel.EnforceTargetAmount = true;
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
2020-06-28 10:55:27 +02:00
2019-01-04 16:42:35 +01:00
var anonAppPubsController = tester.PayTester.GetController<AppsPublicController>();
var publicApps = user.GetController<AppsPublicController>();
var model = Assert.IsType<ViewCrowdfundViewModel>(Assert
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, String.Empty).Result).Model);
2020-06-28 10:55:27 +02:00
Assert.Equal(crowdfundViewModel.TargetAmount, model.TargetAmount);
Assert.Equal(crowdfundViewModel.EndDate, model.EndDate);
Assert.Equal(crowdfundViewModel.StartDate, model.StartDate);
Assert.Equal(crowdfundViewModel.TargetCurrency, model.TargetCurrency);
Assert.Equal(0m, model.Info.CurrentAmount);
2019-01-05 19:47:39 +01:00
Assert.Equal(0m, model.Info.CurrentPendingAmount);
Assert.Equal(0m, model.Info.ProgressPercentage);
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation("Unpaid invoices should show as pending contribution because it is hardcap");
TestLogs.LogInformation("Because UseAllStoreInvoices is true, we can manually create an invoice and it should show as contribution");
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
2019-01-04 16:42:35 +01:00
{
Buyer = new Buyer() { email = "test@fwf.com" },
Price = 1m,
Currency = "BTC",
PosData = "posData",
ItemDesc = "Some description",
2019-01-05 19:47:39 +01:00
TransactionSpeed = "high",
2019-01-04 16:42:35 +01:00
FullNotifications = true
}, Facade.Merchant);
2019-01-04 16:42:35 +01:00
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, string.Empty).Result).Model);
2020-06-28 10:55:27 +02:00
Assert.Equal(0m, model.Info.CurrentAmount);
2019-01-05 19:47:39 +01:00
Assert.Equal(1m, model.Info.CurrentPendingAmount);
Assert.Equal(0m, model.Info.ProgressPercentage);
2019-01-05 19:47:39 +01:00
Assert.Equal(1m, model.Info.PendingProgressPercentage);
2019-01-04 16:42:35 +01:00
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation("Let's check current amount change once payment is confirmed");
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
tester.ExplorerNode.SendToAddress(invoiceAddress, invoice.BtcDue);
tester.ExplorerNode.Generate(1); // By default invoice confirmed at 1 block
TestUtils.Eventually(() =>
{
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, String.Empty).Result).Model);
Assert.Equal(1m, model.Info.CurrentAmount);
Assert.Equal(0m, model.Info.CurrentPendingAmount);
});
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation("Because UseAllStoreInvoices is true, let's make sure the invoice is tagged");
2019-02-19 04:59:12 +01:00
var invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
Assert.True(invoiceEntity.Version >= InvoiceEntity.InternalTagSupport_Version);
Assert.Contains(AppService.GetAppInternalTag(app.Id), invoiceEntity.InternalTags);
2019-02-19 04:59:12 +01:00
crowdfundViewModel.UseAllStoreInvoices = false;
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
2019-02-19 04:59:12 +01:00
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation("Because UseAllStoreInvoices is false, let's make sure the invoice is not tagged");
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
2019-02-19 04:59:12 +01:00
{
Buyer = new Buyer { email = "test@fwf.com" },
2019-02-19 04:59:12 +01:00
Price = 1m,
Currency = "BTC",
PosData = "posData",
ItemDesc = "Some description",
TransactionSpeed = "high",
FullNotifications = true
}, Facade.Merchant);
invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
Assert.DoesNotContain(AppService.GetAppInternalTag(app.Id), invoiceEntity.InternalTags);
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation("After turning setting a softcap, let's check that only actual payments are counted");
crowdfundViewModel.EnforceTargetAmount = false;
crowdfundViewModel.UseAllStoreInvoices = true;
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
{
Buyer = new Buyer { email = "test@fwf.com" },
Price = 1m,
Currency = "BTC",
PosData = "posData",
ItemDesc = "Some description",
TransactionSpeed = "high",
FullNotifications = true
}, Facade.Merchant);
Assert.Equal(0m, model.Info.CurrentPendingAmount);
invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.5m));
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.2m));
TestUtils.Eventually(() =>
{
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, string.Empty).Result).Model);
Assert.Equal(0.7m, model.Info.CurrentPendingAmount);
});
2019-01-04 16:42:35 +01:00
}
}
}
}