213 lines
6.7 KiB
Go
213 lines
6.7 KiB
Go
package common
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
|
|
)
|
|
|
|
func TestSpendWalletBalanceClosesWithdrawableGap(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
Balance: 10_000,
|
|
WithdrawalBalance: 10_000,
|
|
}
|
|
|
|
if err := SpendWalletBalance(&wallet, 8_000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if wallet.Balance != 2_000 || wallet.WithdrawalBalance != 2_000 {
|
|
t.Fatalf("balances after expense = (%d, %d), want (2000, 2000)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
if err := ReserveWalletWithdrawal(&wallet, 10_000); err != ErrWalletBalance {
|
|
t.Fatalf("withdrawal after expense error = %v, want %v", err, ErrWalletBalance)
|
|
}
|
|
}
|
|
|
|
func TestSpendWalletBalanceConsumesNonWithdrawableBalanceFirst(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
Balance: 15_000,
|
|
WithdrawalBalance: 10_000,
|
|
}
|
|
|
|
if err := SpendWalletBalance(&wallet, 4_000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if wallet.Balance != 11_000 || wallet.WithdrawalBalance != 10_000 {
|
|
t.Fatalf("balances after first expense = (%d, %d), want (11000, 10000)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
if err := SpendWalletBalance(&wallet, 2_000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if wallet.Balance != 9_000 || wallet.WithdrawalBalance != 9_000 {
|
|
t.Fatalf("balances after second expense = (%d, %d), want (9000, 9000)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
}
|
|
|
|
func TestWithdrawalReserveAndRejectAreExactInverse(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
Balance: 10_000,
|
|
WithdrawalBalance: 10_000,
|
|
}
|
|
|
|
if err := ReserveWalletWithdrawal(&wallet, 10_000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if wallet.Balance != 0 || wallet.WithdrawalBalance != 0 {
|
|
t.Fatalf("reserved balances = (%d, %d), want (0, 0)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
if err := ReleaseWalletWithdrawal(&wallet, 10_000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if wallet.Balance != 10_000 || wallet.WithdrawalBalance != 10_000 {
|
|
t.Fatalf("released balances = (%d, %d), want (10000, 10000)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
}
|
|
|
|
func TestLegacyWithdrawalReleaseCannotExceedRemainingBalance(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
Balance: 2_000,
|
|
WithdrawalBalance: 2_000,
|
|
}
|
|
|
|
if err := ReleaseLegacyWithdrawalBalance(&wallet, 8_000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if wallet.Balance != 2_000 || wallet.WithdrawalBalance != 2_000 {
|
|
t.Fatalf("legacy released balances = (%d, %d), want (2000, 2000)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
}
|
|
|
|
func TestFrozenWalletRejectsNewDebits(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusFrozen),
|
|
Balance: 10_000,
|
|
WithdrawalBalance: 10_000,
|
|
}
|
|
|
|
if err := SpendWalletBalance(&wallet, 1); err != ErrWalletUnavailable {
|
|
t.Fatalf("expense error = %v, want %v", err, ErrWalletUnavailable)
|
|
}
|
|
if err := ReserveWalletWithdrawal(&wallet, 1); err != ErrWalletUnavailable {
|
|
t.Fatalf("withdrawal error = %v, want %v", err, ErrWalletUnavailable)
|
|
}
|
|
}
|
|
|
|
func TestWithdrawalReleaseRejectsOverflow(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
Balance: math.MaxInt64,
|
|
WithdrawalBalance: math.MaxInt64,
|
|
}
|
|
|
|
if err := ReleaseWalletWithdrawal(&wallet, 1); err != ErrWalletOverflow {
|
|
t.Fatalf("release error = %v, want %v", err, ErrWalletOverflow)
|
|
}
|
|
}
|
|
|
|
func TestNewWithdrawalRejectionRestoresBothBalances(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
OwnerType: "delivery",
|
|
Balance: 2_000,
|
|
WithdrawalBalance: 2_000,
|
|
}
|
|
application := models.WalletApplyCash{Amount: 8_000, BalanceReserved: true}
|
|
|
|
released, err := ReleaseWithdrawalApplication(&wallet, application)
|
|
if err != nil || !released {
|
|
t.Fatalf("release = (%v, %v), want (true, nil)", released, err)
|
|
}
|
|
if wallet.Balance != 10_000 || wallet.WithdrawalBalance != 10_000 {
|
|
t.Fatalf("released balances = (%d, %d), want (10000, 10000)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
}
|
|
|
|
func TestLegacyOrganizationWithdrawalRejectionDoesNotMintBalance(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
OwnerType: "delivery",
|
|
Balance: 10_000,
|
|
WithdrawalBalance: 10_000,
|
|
}
|
|
application := models.WalletApplyCash{Amount: 8_000}
|
|
|
|
released, err := ReleaseWithdrawalApplication(&wallet, application)
|
|
if err != nil || released {
|
|
t.Fatalf("release = (%v, %v), want (false, nil)", released, err)
|
|
}
|
|
if wallet.Balance != 10_000 || wallet.WithdrawalBalance != 10_000 {
|
|
t.Fatalf("legacy organization balances changed to (%d, %d)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
}
|
|
|
|
func TestLegacyWithdrawalCompletionDebitsMissingBalances(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
ownerType string
|
|
withdrawalBalance int64
|
|
wantBalance int64
|
|
wantWithdrawal int64
|
|
}{
|
|
{
|
|
name: "client already reserved withdrawable balance",
|
|
ownerType: "user",
|
|
withdrawalBalance: 2_000,
|
|
wantBalance: 2_000,
|
|
wantWithdrawal: 2_000,
|
|
},
|
|
{
|
|
name: "organization reserved neither balance",
|
|
ownerType: "delivery",
|
|
withdrawalBalance: 10_000,
|
|
wantBalance: 2_000,
|
|
wantWithdrawal: 2_000,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
OwnerType: test.ownerType,
|
|
Balance: 10_000,
|
|
WithdrawalBalance: test.withdrawalBalance,
|
|
}
|
|
changed, err := SettleLegacyWithdrawal(&wallet, models.WalletApplyCash{Amount: 8_000})
|
|
if err != nil || !changed {
|
|
t.Fatalf("settle = (%v, %v), want (true, nil)", changed, err)
|
|
}
|
|
if wallet.Balance != test.wantBalance || wallet.WithdrawalBalance != test.wantWithdrawal {
|
|
t.Fatalf(
|
|
"settled balances = (%d, %d), want (%d, %d)",
|
|
wallet.Balance,
|
|
wallet.WithdrawalBalance,
|
|
test.wantBalance,
|
|
test.wantWithdrawal,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReservedWithdrawalCompletionDoesNotDebitAgain(t *testing.T) {
|
|
wallet := models.WalletBasic{
|
|
Entity: NewEntity(StatusEnable),
|
|
OwnerType: "user",
|
|
Balance: 2_000,
|
|
WithdrawalBalance: 2_000,
|
|
}
|
|
application := models.WalletApplyCash{Amount: 8_000, BalanceReserved: true}
|
|
|
|
changed, err := SettleLegacyWithdrawal(&wallet, application)
|
|
if err != nil || changed {
|
|
t.Fatalf("settle = (%v, %v), want (false, nil)", changed, err)
|
|
}
|
|
if wallet.Balance != 2_000 || wallet.WithdrawalBalance != 2_000 {
|
|
t.Fatalf("reserved completion changed balances to (%d, %d)", wallet.Balance, wallet.WithdrawalBalance)
|
|
}
|
|
}
|