2024-09-26 08:52:16 +02:00
using System ;
2021-09-17 04:31:48 +02:00
using System.Threading.Tasks ;
2023-09-11 02:59:17 +02:00
using BTCPayServer.Client ;
2024-11-05 03:49:30 +01:00
using BTCPayServer.Client.Models ;
2021-09-17 04:31:48 +02:00
using BTCPayServer.Controllers ;
2021-12-16 17:37:19 +01:00
using BTCPayServer.Data ;
2023-05-23 02:18:57 +02:00
using BTCPayServer.Hosting ;
2021-09-17 04:31:48 +02:00
using BTCPayServer.Models.AppViewModels ;
2023-09-11 02:59:17 +02:00
using BTCPayServer.Plugins.Crowdfund.Models ;
2023-03-17 03:56:32 +01:00
using BTCPayServer.Plugins.PointOfSale ;
2022-07-18 20:51:53 +02:00
using BTCPayServer.Plugins.PointOfSale.Controllers ;
using BTCPayServer.Plugins.PointOfSale.Models ;
2023-05-23 02:18:57 +02:00
using BTCPayServer.Services.Apps ;
2021-09-17 04:31:48 +02:00
using Microsoft.AspNetCore.Mvc ;
using Xunit ;
using Xunit.Abstractions ;
using static BTCPayServer . Tests . UnitTest1 ;
2024-11-05 03:49:30 +01:00
using PosViewType = BTCPayServer . Plugins . PointOfSale . PosViewType ;
2021-09-17 04:31:48 +02:00
namespace BTCPayServer.Tests
{
2021-11-23 05:57:45 +01:00
[Collection(nameof(NonParallelizableCollectionDefinition))]
2021-11-22 09:16:08 +01:00
public class POSTests : UnitTestBase
2021-09-17 04:31:48 +02:00
{
2021-11-22 09:16:08 +01:00
public POSTests ( ITestOutputHelper helper ) : base ( helper )
2021-09-17 04:31:48 +02:00
{
}
2023-05-23 02:18:57 +02:00
[Fact]
[Trait("Fast", "Fast")]
public void CanParseOldYmlCorrectly ( )
{
var testOriginalDefaultYmlTemplate = @ "
green tea :
price : 1
title : Green Tea
description : Lovely , fresh and tender , Meng Ding Gan Lu ( ' sweet dew ' ) is grown in the lush Meng Ding Mountains of the southwestern province of Sichuan where it has been cultivated for over a thousand years .
image : ~ / img / pos - sample / green - tea . jpg
black tea :
price : 1
title : Black Tea
description : Tian Jian Tian Jian means ' heavenly tippy tea ' in Chinese , and it describes the finest grade of dark tea . Our Tian Jian dark tea is from Hunan province which is famous for making some of the best dark teas available .
image : ~ / img / pos - sample / black - tea . jpg
rooibos :
price : 1.2
title : Rooibos
description : Rooibos is a dramatic red tea made from a South African herb that contains polyphenols and flavonoids . Often called ' African redbush tea ' , Rooibos herbal tea delights the senses and delivers potential health benefits with each caffeine - free sip .
image : ~ / img / pos - sample / rooibos . jpg
pu erh :
price : 2
title : Pu Erh
description : This loose pur - erh tea is produced in Yunnan Province , China . The process in a relatively high humidity environment has mellowed the elemental character of the tea when compared to young Pu - erh .
image : ~ / img / pos - sample / pu - erh . jpg
herbal tea :
price : 1.8
title : Herbal Tea
description : Chamomile tea is made from the flower heads of the chamomile plant . The medicinal use of chamomile dates back to the ancient Egyptians , Romans and Greeks . Pay us what you want !
image : ~ / img / pos - sample / herbal - tea . jpg
custom : true
fruit tea :
price : 1.5
title : Fruit Tea
description : The Tibetan Himalayas , the land is majestic and beautiful — a spiritual place where , despite the perilous environment , many journey seeking enlightenment . Pay us what you want !
image : ~ / img / pos - sample / fruit - tea . jpg
inventory : 5
custom : true
";
var parsedDefault = MigrationStartupTask . ParsePOSYML ( testOriginalDefaultYmlTemplate ) ;
Assert . Equal ( 6 , parsedDefault . Length ) ;
Assert . Equal ( "Green Tea" , parsedDefault [ 0 ] . Title ) ;
Assert . Equal ( "green tea" , parsedDefault [ 0 ] . Id ) ;
Assert . Equal ( "Lovely, fresh and tender, Meng Ding Gan Lu ('sweet dew') is grown in the lush Meng Ding Mountains of the southwestern province of Sichuan where it has been cultivated for over a thousand years." , parsedDefault [ 0 ] . Description ) ;
Assert . Null ( parsedDefault [ 0 ] . BuyButtonText ) ;
Assert . Equal ( "~/img/pos-sample/green-tea.jpg" , parsedDefault [ 0 ] . Image ) ;
Assert . Equal ( 1 , parsedDefault [ 0 ] . Price ) ;
2024-11-05 03:49:30 +01:00
Assert . Equal ( AppItemPriceType . Fixed , parsedDefault [ 0 ] . PriceType ) ;
2023-05-23 02:18:57 +02:00
Assert . Null ( parsedDefault [ 0 ] . AdditionalData ) ;
Assert . Equal ( "Herbal Tea" , parsedDefault [ 4 ] . Title ) ;
Assert . Equal ( "herbal tea" , parsedDefault [ 4 ] . Id ) ;
Assert . Equal ( "Chamomile tea is made from the flower heads of the chamomile plant. The medicinal use of chamomile dates back to the ancient Egyptians, Romans and Greeks. Pay us what you want!" , parsedDefault [ 4 ] . Description ) ;
Assert . Null ( parsedDefault [ 4 ] . BuyButtonText ) ;
Assert . Equal ( "~/img/pos-sample/herbal-tea.jpg" , parsedDefault [ 4 ] . Image ) ;
Assert . Equal ( 1.8 m , parsedDefault [ 4 ] . Price ) ;
2024-11-05 03:49:30 +01:00
Assert . Equal ( AppItemPriceType . Minimum , parsedDefault [ 4 ] . PriceType ) ;
2023-05-23 02:18:57 +02:00
Assert . Null ( parsedDefault [ 4 ] . AdditionalData ) ;
}
2024-09-26 08:52:16 +02:00
[Fact]
[Trait("Fast", "Fast")]
public void CanParseAppTemplate ( )
{
var template = @ "[
{
"" description "" : "" Lovely , fresh and tender , Meng Ding Gan Lu ( ' sweet dew ' ) is grown in the lush Meng Ding Mountains of the southwestern province of Sichuan where it has been cultivated for over a thousand years . "" ,
"" id "" : "" green - tea "" ,
"" image "" : "" ~ / img / pos - sample / green - tea . jpg "" ,
"" priceType "" : "" Fixed "" ,
"" price "" : "" 1 "" ,
"" title "" : "" Green Tea "" ,
"" disabled "" : false
} ,
{
"" description "" : "" Tian Jian Tian Jian means ' heavenly tippy tea ' in Chinese , and it describes the finest grade of dark tea . Our Tian Jian dark tea is from Hunan province which is famous for making some of the best dark teas available . "" ,
"" id "" : "" black - tea "" ,
"" image "" : "" ~ / img / pos - sample / black - tea . jpg "" ,
"" priceType "" : "" Fixed "" ,
"" price "" : "" 1 "" ,
"" title "" : "" Black Tea "" ,
"" disabled "" : false
}
] ";
var items = AppService . Parse ( template ) ;
Assert . Equal ( 2 , items . Length ) ;
Assert . Equal ( "green-tea" , items [ 0 ] . Id ) ;
Assert . Equal ( "black-tea" , items [ 1 ] . Id ) ;
// Fails gracefully for missing ID
var missingId = template . Replace ( @"""id"": ""green-tea""," , "" ) ;
items = AppService . Parse ( missingId ) ;
Assert . Single ( items ) ;
Assert . Equal ( "black-tea" , items [ 0 ] . Id ) ;
// Throws for missing ID
Assert . Throws < ArgumentException > ( ( ) = > AppService . Parse ( missingId , true , true ) ) ;
// Fails gracefully for duplicate IDs
var duplicateId = template . Replace ( @"""id"": ""green-tea""," , @"""id"": ""black-tea""," ) ;
items = AppService . Parse ( duplicateId ) ;
Assert . Empty ( items ) ;
// Throws for duplicate IDs
Assert . Throws < ArgumentException > ( ( ) = > AppService . Parse ( duplicateId , true , true ) ) ;
}
2023-05-23 02:18:57 +02:00
2021-09-17 04:31:48 +02:00
[Fact(Timeout = LongRunningTestTimeout)]
2021-11-22 16:49:51 +01:00
[Trait("Integration", "Integration")]
2021-09-17 04:31:48 +02:00
public async Task CanUsePoSApp1 ( )
{
2022-01-14 09:50:29 +01:00
using var tester = CreateServerTester ( ) ;
await tester . StartAsync ( ) ;
var user = tester . NewAccount ( ) ;
await user . GrantAccessAsync ( ) ;
user . RegisterDerivationScheme ( "BTC" ) ;
var apps = user . GetController < UIAppsController > ( ) ;
2022-07-18 20:51:53 +02:00
var pos = user . GetController < UIPointOfSaleController > ( ) ;
2022-01-14 09:50:29 +01:00
var vm = Assert . IsType < CreateAppViewModel > ( Assert . IsType < ViewResult > ( apps . CreateApp ( user . StoreId ) ) . Model ) ;
2023-03-20 02:39:26 +01:00
var appType = PointOfSaleAppType . AppType ;
2022-01-14 09:50:29 +01:00
vm . AppName = "test" ;
2022-06-28 07:05:02 +02:00
vm . SelectedAppType = appType ;
2023-03-17 03:56:32 +01:00
var redirect = Assert . IsType < RedirectResult > ( apps . CreateApp ( user . StoreId , vm ) . Result ) ;
Assert . EndsWith ( "/settings/pos" , redirect . Url ) ;
2022-01-14 09:50:29 +01:00
var appList = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( user . StoreId ) . Result ) . Model ) ;
var app = appList . Apps [ 0 ] ;
2022-07-18 20:51:53 +02:00
var appData = new AppData { Id = app . Id , StoreDataId = app . StoreId , Name = app . AppName , AppType = appType } ;
apps . HttpContext . SetAppData ( appData ) ;
pos . HttpContext . SetAppData ( appData ) ;
var vmpos = await pos . UpdatePointOfSale ( app . Id ) . AssertViewModelAsync < UpdatePointOfSaleViewModel > ( ) ;
2022-01-14 09:50:29 +01:00
vmpos . Template = @ "
2021-09-17 04:31:48 +02:00
apple :
price : 5.0
title : good apple
disabled : true
orange :
price : 10.0
donation :
price : 1.02
custom : true
";
2023-09-11 02:59:17 +02:00
vmpos . Currency = "EUR" ;
2023-05-23 02:18:57 +02:00
vmpos . Template = AppService . SerializeTemplate ( MigrationStartupTask . ParsePOSYML ( vmpos . Template ) ) ;
2022-07-18 20:51:53 +02:00
Assert . IsType < RedirectToActionResult > ( pos . UpdatePointOfSale ( app . Id , vmpos ) . Result ) ;
await pos . UpdatePointOfSale ( app . Id ) . AssertViewModelAsync < UpdatePointOfSaleViewModel > ( ) ;
2022-07-22 15:41:14 +02:00
var publicApps = user . GetController < UIPointOfSaleController > ( ) ;
2022-06-20 06:34:25 +02:00
var vmview = await publicApps . ViewPointOfSale ( app . Id , PosViewType . Cart ) . AssertViewModelAsync < ViewPointOfSaleViewModel > ( ) ;
2021-09-17 04:31:48 +02:00
2023-09-11 02:59:17 +02:00
Assert . Equal ( "EUR" , vmview . CurrencyCode ) ;
2022-01-14 09:50:29 +01:00
// apple shouldn't be available since we it's set to "disabled: true" above
Assert . Equal ( 2 , vmview . Items . Length ) ;
Assert . Equal ( "orange" , vmview . Items [ 0 ] . Title ) ;
Assert . Equal ( "donation" , vmview . Items [ 1 ] . Title ) ;
// orange is available
Assert . IsType < RedirectToActionResult > ( publicApps
2023-06-16 16:02:14 +02:00
. ViewPointOfSale ( app . Id , PosViewType . Cart , 0 , choiceKey : "orange" ) . Result ) ;
2022-01-14 09:50:29 +01:00
// apple is not found
Assert . IsType < NotFoundResult > ( publicApps
2023-06-16 16:02:14 +02:00
. ViewPointOfSale ( app . Id , PosViewType . Cart , 0 , choiceKey : "apple" ) . Result ) ;
2023-09-11 02:59:17 +02:00
// List
appList = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( user . StoreId ) . Result ) . Model ) ;
app = appList . Apps [ 0 ] ;
apps = user . GetController < UIAppsController > ( ) ;
appData = new AppData { Id = app . Id , StoreDataId = app . StoreId , Name = app . AppName , AppType = appType , Settings = "{\"currency\":\"EUR\"}" } ;
apps . HttpContext . SetAppData ( appData ) ;
pos . HttpContext . SetAppData ( appData ) ;
Assert . Single ( appList . Apps ) ;
Assert . Equal ( "test" , app . AppName ) ;
Assert . True ( app . Role . ToPermissionSet ( appList . Apps [ 0 ] . StoreId ) . Contains ( Policies . CanModifyStoreSettings , app . StoreId ) ) ;
Assert . Equal ( user . StoreId , app . StoreId ) ;
Assert . False ( app . Archived ) ;
// Archive
redirect = Assert . IsType < RedirectResult > ( apps . ToggleArchive ( app . Id ) . Result ) ;
Assert . EndsWith ( "/settings/pos" , redirect . Url ) ;
appList = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( user . StoreId ) . Result ) . Model ) ;
Assert . Empty ( appList . Apps ) ;
appList = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( user . StoreId , archived : true ) . Result ) . Model ) ;
app = appList . Apps [ 0 ] ;
Assert . True ( app . Archived ) ;
Assert . IsType < NotFoundResult > ( await publicApps . ViewPointOfSale ( app . Id , PosViewType . Static ) ) ;
// Unarchive
redirect = Assert . IsType < RedirectResult > ( apps . ToggleArchive ( app . Id ) . Result ) ;
Assert . EndsWith ( "/settings/pos" , redirect . Url ) ;
appList = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( user . StoreId ) . Result ) . Model ) ;
app = appList . Apps [ 0 ] ;
Assert . False ( app . Archived ) ;
Assert . IsType < ViewResult > ( await publicApps . ViewPointOfSale ( app . Id , PosViewType . Static ) ) ;
// Delete
Assert . IsType < ViewResult > ( apps . DeleteApp ( app . Id ) ) ;
var redirectToAction = Assert . IsType < RedirectToActionResult > ( apps . DeleteAppPost ( app . Id ) . Result ) ;
Assert . Equal ( nameof ( UIStoresController . Dashboard ) , redirectToAction . ActionName ) ;
appList = await apps . ListApps ( user . StoreId ) . AssertViewModelAsync < ListAppsViewModel > ( ) ;
Assert . Empty ( appList . Apps ) ;
2021-09-17 04:31:48 +02:00
}
}
}