shad-go/ledger/ledger_test.go

141 lines
2.9 KiB
Go
Raw Normal View History

2022-03-31 11:19:47 +00:00
package ledger_test
import (
"context"
"errors"
"fmt"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
2023-04-08 09:08:35 +00:00
"go.uber.org/goleak"
2022-04-17 15:28:33 +00:00
2023-10-03 17:25:41 +00:00
"gitlab.com/manytask/itmo-go/public/ledger"
"gitlab.com/manytask/itmo-go/public/pgfixture"
2022-03-31 11:19:47 +00:00
)
func TestLedger(t *testing.T) {
2023-04-17 10:01:22 +00:00
t.Cleanup(func() { goleak.VerifyNone(t) })
2023-04-08 09:08:35 +00:00
2022-03-31 11:19:47 +00:00
dsn := pgfixture.Start(t)
ctx := context.Background()
l0, err := ledger.New(ctx, dsn)
require.NoError(t, err)
2022-04-13 15:13:39 +00:00
defer func() { _ = l0.Close() }()
2022-03-31 11:19:47 +00:00
t.Run("SimpleCommands", func(t *testing.T) {
checkBalance := func(account ledger.ID, amount ledger.Money) {
b, err := l0.GetBalance(ctx, account)
require.NoError(t, err)
require.Equal(t, amount, b)
2022-03-31 11:19:47 +00:00
}
require.NoError(t, l0.CreateAccount(ctx, "a0"))
checkBalance("a0", 0)
require.Error(t, l0.CreateAccount(ctx, "a0"))
require.NoError(t, l0.Deposit(ctx, "a0", ledger.Money(100)))
checkBalance("a0", 100)
require.NoError(t, l0.Withdraw(ctx, "a0", ledger.Money(50)))
checkBalance("a0", 50)
require.ErrorIs(t, l0.Withdraw(ctx, "a0", ledger.Money(100)), ledger.ErrNoMoney)
require.NoError(t, l0.CreateAccount(ctx, "a1"))
require.NoError(t, l0.Transfer(ctx, "a0", "a1", ledger.Money(40)))
checkBalance("a0", 10)
checkBalance("a1", 40)
require.ErrorIs(t, l0.Transfer(ctx, "a0", "a1", ledger.Money(50)), ledger.ErrNoMoney)
})
t.Run("Transactions", func(t *testing.T) {
const nAccounts = 10
const initialBalance = 5
var accounts []ledger.ID
for i := 0; i < nAccounts; i++ {
id := ledger.ID(fmt.Sprint(i))
accounts = append(accounts, id)
require.NoError(t, l0.CreateAccount(ctx, id))
require.NoError(t, l0.Deposit(ctx, id, initialBalance))
}
var wg sync.WaitGroup
done := make(chan struct{})
spawn := func(action func() error) {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-done:
return
default:
if err := action(); err != nil {
if !errors.Is(err, ledger.ErrNoMoney) {
t.Errorf("operation failed: %v", err)
return
}
}
}
}
}()
}
for i := 0; i < nAccounts; i++ {
i := i
account := accounts[i]
next := accounts[(i+1)%len(accounts)]
prev := accounts[(i+len(accounts)-1)%len(accounts)]
spawn(func() error {
balance, err := l0.GetBalance(ctx, account)
if err != nil {
return err
}
if balance < 0 {
return fmt.Errorf("%q balance is negative", account)
}
return nil
})
spawn(func() error {
return l0.Transfer(ctx, account, next, 1)
})
spawn(func() error {
return l0.Transfer(ctx, account, prev, 1)
})
}
time.Sleep(time.Second * 10)
close(done)
wg.Wait()
var total ledger.Money
for i := 0; i < nAccounts; i++ {
amount, err := l0.GetBalance(ctx, accounts[i])
require.NoError(t, err)
total += amount
}
require.Equal(t, ledger.Money(initialBalance*nAccounts), total)
2022-03-31 11:19:47 +00:00
})
}