mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
483 lines
14 KiB
Go
483 lines
14 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/formancehq/go-libs/v3/bun/bunpaginate"
|
|
"github.com/formancehq/go-libs/v3/logging"
|
|
"github.com/formancehq/go-libs/v3/query"
|
|
"github.com/formancehq/go-libs/v3/time"
|
|
"github.com/formancehq/payments/internal/models"
|
|
"github.com/google/uuid"
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
poolID1 = uuid.New()
|
|
poolID2 = uuid.New()
|
|
poolID3 = uuid.New()
|
|
)
|
|
|
|
func defaultPools() []models.Pool {
|
|
defaultAccounts := defaultAccounts()
|
|
return []models.Pool{
|
|
{
|
|
ID: poolID1,
|
|
Name: "test1",
|
|
CreatedAt: now.Add(-60 * time.Minute).UTC().Time,
|
|
PoolAccounts: []models.AccountID{defaultAccounts[0].ID, defaultAccounts[1].ID},
|
|
},
|
|
{
|
|
ID: poolID2,
|
|
Name: "test2",
|
|
CreatedAt: now.Add(-30 * time.Minute).UTC().Time,
|
|
PoolAccounts: []models.AccountID{defaultAccounts[2].ID},
|
|
},
|
|
{
|
|
ID: poolID3,
|
|
Name: "test3",
|
|
CreatedAt: now.Add(-55 * time.Minute).UTC().Time,
|
|
PoolAccounts: []models.AccountID{defaultAccounts[2].ID},
|
|
},
|
|
}
|
|
}
|
|
|
|
func upsertPool(t *testing.T, ctx context.Context, storage Storage, pool models.Pool) {
|
|
require.NoError(t, storage.PoolsUpsert(ctx, pool))
|
|
}
|
|
|
|
func TestPoolsUpsert(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := logging.TestingContext()
|
|
store := newStore(t)
|
|
defer store.Close()
|
|
|
|
upsertConnector(t, ctx, store, defaultConnector)
|
|
upsertAccounts(t, ctx, store, defaultAccounts())
|
|
upsertPool(t, ctx, store, defaultPools()[0])
|
|
upsertPool(t, ctx, store, defaultPools()[1])
|
|
|
|
t.Run("upsert with same name", func(t *testing.T) {
|
|
poolID3 := uuid.New()
|
|
p := models.Pool{
|
|
ID: poolID3,
|
|
Name: "test1",
|
|
CreatedAt: now.Add(-30 * time.Minute).UTC().Time,
|
|
PoolAccounts: []models.AccountID{defaultAccounts()[2].ID},
|
|
}
|
|
|
|
err := store.PoolsUpsert(ctx, p)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("upsert with same id", func(t *testing.T) {
|
|
upsertPool(t, ctx, store, defaultPools()[1])
|
|
|
|
actual, err := store.PoolsGet(ctx, defaultPools()[1].ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, defaultPools()[1], *actual)
|
|
})
|
|
|
|
t.Run("upsert with same id but more related accounts", func(t *testing.T) {
|
|
p := defaultPools()[0]
|
|
p.PoolAccounts = append(p.PoolAccounts, defaultAccounts()[2].ID)
|
|
|
|
upsertPool(t, ctx, store, p)
|
|
|
|
actual, err := store.PoolsGet(ctx, defaultPools()[0].ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, p, *actual)
|
|
})
|
|
|
|
t.Run("upsert but account does not exist", func(t *testing.T) {
|
|
p := defaultPools()[0]
|
|
p.PoolAccounts = append(p.PoolAccounts, models.AccountID{
|
|
Reference: "unknown",
|
|
ConnectorID: defaultConnector.ID,
|
|
})
|
|
|
|
err := store.PoolsUpsert(ctx, p)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestPoolsGet(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := logging.TestingContext()
|
|
store := newStore(t)
|
|
defer store.Close()
|
|
|
|
upsertConnector(t, ctx, store, defaultConnector)
|
|
upsertAccounts(t, ctx, store, defaultAccounts())
|
|
upsertPool(t, ctx, store, defaultPools()[0])
|
|
upsertPool(t, ctx, store, defaultPools()[1])
|
|
upsertPool(t, ctx, store, defaultPools()[2])
|
|
|
|
t.Run("get existing pool", func(t *testing.T) {
|
|
for _, p := range defaultPools() {
|
|
actual, err := store.PoolsGet(ctx, p.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, p, *actual)
|
|
}
|
|
})
|
|
|
|
t.Run("get non-existing pool", func(t *testing.T) {
|
|
p, err := store.PoolsGet(ctx, uuid.New())
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrNotFound)
|
|
require.Nil(t, p)
|
|
})
|
|
}
|
|
|
|
func TestPoolsDelete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := logging.TestingContext()
|
|
store := newStore(t)
|
|
defer store.Close()
|
|
|
|
upsertConnector(t, ctx, store, defaultConnector)
|
|
upsertAccounts(t, ctx, store, defaultAccounts())
|
|
upsertPool(t, ctx, store, defaultPools()[0])
|
|
upsertPool(t, ctx, store, defaultPools()[1])
|
|
upsertPool(t, ctx, store, defaultPools()[2])
|
|
|
|
t.Run("delete unknown pool", func(t *testing.T) {
|
|
deleted, err := store.PoolsDelete(ctx, uuid.New())
|
|
require.NoError(t, err)
|
|
require.False(t, deleted)
|
|
for _, p := range defaultPools() {
|
|
actual, err := store.PoolsGet(ctx, p.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, p, *actual)
|
|
}
|
|
})
|
|
|
|
t.Run("delete existing pool", func(t *testing.T) {
|
|
deleted, err := store.PoolsDelete(ctx, defaultPools()[0].ID)
|
|
require.NoError(t, err)
|
|
require.True(t, deleted)
|
|
|
|
_, err = store.PoolsGet(ctx, defaultPools()[0].ID)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrNotFound)
|
|
|
|
actual, err := store.PoolsGet(ctx, defaultPools()[1].ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, defaultPools()[1], *actual)
|
|
})
|
|
}
|
|
|
|
func TestPoolsAddAccount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := logging.TestingContext()
|
|
store := newStore(t)
|
|
defer store.Close()
|
|
|
|
upsertConnector(t, ctx, store, defaultConnector)
|
|
upsertAccounts(t, ctx, store, defaultAccounts())
|
|
upsertPool(t, ctx, store, defaultPools()[0])
|
|
upsertPool(t, ctx, store, defaultPools()[1])
|
|
|
|
t.Run("add account to unknown pool", func(t *testing.T) {
|
|
err := store.PoolsAddAccount(ctx, uuid.New(), defaultAccounts()[0].ID)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("add account to pool", func(t *testing.T) {
|
|
require.NoError(t, store.PoolsAddAccount(ctx, defaultPools()[0].ID, defaultAccounts()[2].ID))
|
|
|
|
p := defaultPools()[0]
|
|
p.PoolAccounts = append(p.PoolAccounts, defaultAccounts()[2].ID)
|
|
|
|
actual, err := store.PoolsGet(ctx, defaultPools()[0].ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, p, *actual)
|
|
})
|
|
|
|
t.Run("add account to pool but account does not exist", func(t *testing.T) {
|
|
err := store.PoolsAddAccount(ctx, defaultPools()[0].ID, models.AccountID{
|
|
Reference: "unknown",
|
|
ConnectorID: defaultConnector.ID,
|
|
})
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestPoolsRemoveAccount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := logging.TestingContext()
|
|
store := newStore(t)
|
|
defer store.Close()
|
|
|
|
upsertConnector(t, ctx, store, defaultConnector)
|
|
upsertAccounts(t, ctx, store, defaultAccounts())
|
|
upsertPool(t, ctx, store, defaultPools()[0])
|
|
upsertPool(t, ctx, store, defaultPools()[1])
|
|
|
|
t.Run("remove unknown account from pool", func(t *testing.T) {
|
|
require.NoError(t, store.PoolsRemoveAccount(ctx, defaultPools()[0].ID, models.AccountID{
|
|
Reference: "unknown",
|
|
ConnectorID: defaultConnector.ID,
|
|
}))
|
|
})
|
|
|
|
t.Run("remove account from unknown pool", func(t *testing.T) {
|
|
require.NoError(t, store.PoolsRemoveAccount(ctx, uuid.New(), defaultAccounts()[0].ID))
|
|
})
|
|
|
|
t.Run("remove account from pool", func(t *testing.T) {
|
|
require.NoError(t, store.PoolsRemoveAccount(ctx, defaultPools()[0].ID, defaultAccounts()[1].ID))
|
|
|
|
p := defaultPools()[0]
|
|
p.PoolAccounts = p.PoolAccounts[:1]
|
|
|
|
actual, err := store.PoolsGet(ctx, defaultPools()[0].ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, p, *actual)
|
|
})
|
|
}
|
|
|
|
func TestPoolsRemoveAccountFromConnectorID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := logging.TestingContext()
|
|
store := newStore(t)
|
|
defer store.Close()
|
|
|
|
upsertConnector(t, ctx, store, defaultConnector)
|
|
upsertAccounts(t, ctx, store, defaultAccounts())
|
|
upsertPool(t, ctx, store, defaultPools()[0])
|
|
|
|
t.Run("remove accounts from unknown connector", func(t *testing.T) {
|
|
require.NoError(t, store.PoolsRemoveAccountsFromConnectorID(ctx, models.ConnectorID{
|
|
Reference: uuid.New(),
|
|
Provider: "unknown",
|
|
}))
|
|
|
|
actual, err := store.PoolsGet(ctx, defaultPools()[0].ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, defaultPools()[0], *actual)
|
|
})
|
|
|
|
t.Run("remove accounts from connector", func(t *testing.T) {
|
|
require.NoError(t, store.PoolsRemoveAccountsFromConnectorID(ctx, defaultConnector.ID))
|
|
|
|
actual, err := store.PoolsGet(ctx, defaultPools()[0].ID)
|
|
require.NoError(t, err)
|
|
require.Len(t, actual.PoolAccounts, 0)
|
|
})
|
|
}
|
|
|
|
func TestPoolsList(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := logging.TestingContext()
|
|
store := newStore(t)
|
|
defer store.Close()
|
|
|
|
upsertConnector(t, ctx, store, defaultConnector)
|
|
upsertAccounts(t, ctx, store, defaultAccounts())
|
|
upsertPool(t, ctx, store, defaultPools()[0])
|
|
upsertPool(t, ctx, store, defaultPools()[1])
|
|
upsertPool(t, ctx, store, defaultPools()[2])
|
|
|
|
t.Run("wrong query builder operator when listing by name", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Lt("name", "test1")),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.Error(t, err)
|
|
require.Nil(t, cursor)
|
|
assert.True(t, errors.Is(err, ErrValidation))
|
|
assert.Regexp(t, "name", err.Error())
|
|
})
|
|
|
|
t.Run("list pools by name", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("name", "test1")),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.False(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools()[0]}, cursor.Data)
|
|
})
|
|
|
|
t.Run("list pools by unknown name", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("name", "unknown")),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cursor.Data)
|
|
require.False(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
})
|
|
|
|
t.Run("list pools by id", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("id", poolID1.String())),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.False(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools()[0]}, cursor.Data)
|
|
})
|
|
|
|
t.Run("list pools by unknown id", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("id", uuid.New().String())),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cursor.Data)
|
|
require.False(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
})
|
|
|
|
t.Run("list pools by account id 1", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("account_id", defaultAccounts()[0].ID.String())),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.False(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools()[0]}, cursor.Data)
|
|
})
|
|
|
|
t.Run("list pools by account id 2", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("account_id", defaultAccounts()[2].ID.String())),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 2)
|
|
require.False(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools()[1], defaultPools()[2]}, cursor.Data)
|
|
})
|
|
|
|
t.Run("list pools by unknown account id", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("account_id", uuid.New().String())),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cursor.Data)
|
|
require.False(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
})
|
|
|
|
t.Run("unknown query builder key when listing", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(15).
|
|
WithQueryBuilder(query.Match("unknown", "test1")),
|
|
)
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.Error(t, err)
|
|
require.Nil(t, cursor)
|
|
})
|
|
|
|
t.Run("list pools test cursor", func(t *testing.T) {
|
|
q := NewListPoolsQuery(
|
|
bunpaginate.NewPaginatedQueryOptions(PoolQuery{}).
|
|
WithPageSize(1),
|
|
)
|
|
defaultPools := defaultPools()
|
|
|
|
cursor, err := store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.True(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.NotEmpty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools[1]}, cursor.Data)
|
|
|
|
err = bunpaginate.UnmarshalCursor(cursor.Next, &q)
|
|
require.NoError(t, err)
|
|
cursor, err = store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.True(t, cursor.HasMore)
|
|
require.NotEmpty(t, cursor.Previous)
|
|
require.NotEmpty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools[2]}, cursor.Data)
|
|
|
|
err = bunpaginate.UnmarshalCursor(cursor.Next, &q)
|
|
require.NoError(t, err)
|
|
cursor, err = store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.False(t, cursor.HasMore)
|
|
require.NotEmpty(t, cursor.Previous)
|
|
require.Empty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools[0]}, cursor.Data)
|
|
|
|
err = bunpaginate.UnmarshalCursor(cursor.Previous, &q)
|
|
require.NoError(t, err)
|
|
cursor, err = store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.True(t, cursor.HasMore)
|
|
require.NotEmpty(t, cursor.Previous)
|
|
require.NotEmpty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools[2]}, cursor.Data)
|
|
|
|
err = bunpaginate.UnmarshalCursor(cursor.Previous, &q)
|
|
require.NoError(t, err)
|
|
cursor, err = store.PoolsList(ctx, q)
|
|
require.NoError(t, err)
|
|
require.Len(t, cursor.Data, 1)
|
|
require.True(t, cursor.HasMore)
|
|
require.Empty(t, cursor.Previous)
|
|
require.NotEmpty(t, cursor.Next)
|
|
require.Equal(t, []models.Pool{defaultPools[1]}, cursor.Data)
|
|
})
|
|
}
|