Files
platforms/backend/api/internal/logic/common/wallet_balance_test.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)
}
}