diff --git a/pkg/ledgertesting/storage.go b/pkg/ledgertesting/storage.go index c8b524a17..549949573 100644 --- a/pkg/ledgertesting/storage.go +++ b/pkg/ledgertesting/storage.go @@ -47,9 +47,9 @@ func StorageDriver(multipleInstance bool) (*sqlstorage.Driver, func(), error) { return nil, nil, errors.New("not found driver") } -func ProvideStorageDriver() fx.Option { +func ProvideStorageDriver(withMultipleInstance bool) fx.Option { return fx.Provide(func(lc fx.Lifecycle) (*sqlstorage.Driver, error) { - driver, stopFn, err := StorageDriver(false) + driver, stopFn, err := StorageDriver(withMultipleInstance) if err != nil { return nil, err } @@ -66,7 +66,7 @@ func ProvideStorageDriver() fx.Option { func ProvideLedgerStorageDriver() fx.Option { return fx.Options( - ProvideStorageDriver(), + ProvideStorageDriver(false), fx.Provide( fx.Annotate(sqlstorage.NewLedgerStorageDriverFromRawDriver, fx.As(new(storage.Driver[ledger.Store]))), diff --git a/pkg/storage/sqlstorage/accounts_test.go b/pkg/storage/sqlstorage/accounts_test.go index fe914a7ef..a746531d6 100644 --- a/pkg/storage/sqlstorage/accounts_test.go +++ b/pkg/storage/sqlstorage/accounts_test.go @@ -1,58 +1,16 @@ -package sqlstorage +package sqlstorage_test import ( "context" - "os" "testing" + "github.com/numary/ledger/pkg/core" "github.com/numary/ledger/pkg/ledger" - "github.com/pborman/uuid" + "github.com/numary/ledger/pkg/storage/sqlstorage" "github.com/stretchr/testify/assert" ) -func TestAccounts(t *testing.T) { - d := NewDriver("sqlite", &sqliteDB{ - directory: os.TempDir(), - dbName: uuid.New(), - }, false) - - assert.NoError(t, d.Initialize(context.Background())) - - defer func(d *Driver, ctx context.Context) { - assert.NoError(t, d.Close(ctx)) - }(d, context.Background()) - - store, _, err := d.GetLedgerStore(context.Background(), "foo", true) - assert.NoError(t, err) - - _, err = store.Initialize(context.Background()) - assert.NoError(t, err) - - accountTests(t, store) -} - -func TestAccountsMultipleInstance(t *testing.T) { - d := NewDriver("sqlite", &sqliteDB{ - directory: os.TempDir(), - dbName: uuid.New(), - }, true) - - assert.NoError(t, d.Initialize(context.Background())) - - defer func(d *Driver, ctx context.Context) { - assert.NoError(t, d.Close(ctx)) - }(d, context.Background()) - - store, _, err := d.GetLedgerStore(context.Background(), "foo", true) - assert.NoError(t, err) - - _, err = store.Initialize(context.Background()) - assert.NoError(t, err) - - accountTests(t, store) -} - -func accountTests(t *testing.T, store *Store) { +func testAccounts(t *testing.T, store *sqlstorage.Store) { t.Run("success balance", func(t *testing.T) { q := ledger.AccountsQuery{ PageSize: 10, @@ -107,4 +65,21 @@ func accountTests(t *testing.T, store *Store) { _, err := store.GetAccounts(context.Background(), q) assert.NoError(t, err, "balance operator filter should not fail") }) + + t.Run("success get accounts with address filters", func(t *testing.T) { + err := store.Commit(context.Background(), tx1, tx2, tx3, tx4) + assert.NoError(t, err) + + q := ledger.AccountsQuery{ + PageSize: 10, + Filters: ledger.AccountsQueryFilters{ + Address: "users:1", + }, + } + + accounts, err := store.GetAccounts(context.Background(), q) + assert.NoError(t, err, "balance operator filter should not fail") + assert.Equal(t, len(accounts.Data), 1) + assert.Equal(t, accounts.Data[0].Address, core.AccountAddress("users:1")) + }) } diff --git a/pkg/storage/sqlstorage/balances.go b/pkg/storage/sqlstorage/balances.go index 6756ac5bd..a5ac692ce 100644 --- a/pkg/storage/sqlstorage/balances.go +++ b/pkg/storage/sqlstorage/balances.go @@ -131,7 +131,7 @@ func (s *Store) GetBalances(ctx context.Context, q ledger.BalancesQuery) (api.Cu continue } - arg := sb.Args.Add(strings.ReplaceAll(segment, "\\", "\\\\")) + arg := sb.Args.Add("^" + strings.ReplaceAll(segment, "\\", "\\\\") + "$") sb.Where(fmt.Sprintf("account_json @@ ('$[%d] like_regex \"' || %s::text || '\"')::jsonpath", i, arg)) } } else { diff --git a/pkg/storage/sqlstorage/balances_test.go b/pkg/storage/sqlstorage/balances_test.go index 956cbfefa..ca0367bd1 100644 --- a/pkg/storage/sqlstorage/balances_test.go +++ b/pkg/storage/sqlstorage/balances_test.go @@ -2,11 +2,12 @@ package sqlstorage_test import ( "context" - "github.com/numary/ledger/pkg/ledgertesting" "os" "testing" "time" + "github.com/numary/ledger/pkg/ledgertesting" + "github.com/numary/ledger/pkg/core" "github.com/numary/ledger/pkg/ledger" "github.com/numary/ledger/pkg/storage/sqlstorage" @@ -160,6 +161,33 @@ func testGetBalances(t *testing.T, store *sqlstorage.Store) { }) } +func testGetBalancesOn1Account(t *testing.T, store *sqlstorage.Store) { + err := store.Commit(context.Background(), tx1, tx2, tx3, tx4) + assert.NoError(t, err) + + t.Run("on 1 accounts", func(t *testing.T) { + cursor, err := store.GetBalances(context.Background(), + ledger.BalancesQuery{ + Filters: ledger.BalancesQueryFilters{ + AddressRegexp: []string{"users:1"}, + }, + PageSize: 10, + }) + assert.NoError(t, err) + assert.Equal(t, 10, cursor.PageSize) + assert.Equal(t, false, cursor.HasMore) + assert.Equal(t, "", cursor.Previous) + assert.Equal(t, "", cursor.Next) + assert.Equal(t, []core.AccountsBalances{ + { + "users:1": core.AssetsBalances{ + "USD": core.NewMonetaryInt(1), + }, + }, + }, cursor.Data) + }) +} + func testGetBalancesAggregated(t *testing.T, store *sqlstorage.Store) { err := store.Commit(context.Background(), tx1, tx2, tx3) assert.NoError(t, err) @@ -174,6 +202,23 @@ func testGetBalancesAggregated(t *testing.T, store *sqlstorage.Store) { }, cursor) } +func testGetBalancesAggregatedByAccount(t *testing.T, store *sqlstorage.Store) { + err := store.Commit(context.Background(), tx1, tx2, tx3, tx4) + assert.NoError(t, err) + + q := ledger.AggregatedBalancesQuery{ + PageSize: 10, + Filters: ledger.AggregatedBalancesQueryFilters{ + AddressRegexp: "users:1", + }, + } + cursor, err := store.GetBalancesAggregated(context.Background(), q) + assert.NoError(t, err) + assert.Equal(t, core.AssetsBalances{ + "USD": core.NewMonetaryInt(1), + }, cursor) +} + func testGetBalancesBigInts(t *testing.T, store *sqlstorage.Store) { if os.Getenv("NUMARY_STORAGE_POSTGRES_CONN_STRING") != "" || diff --git a/pkg/storage/sqlstorage/store_ledger_test.go b/pkg/storage/sqlstorage/store_ledger_test.go index e6e440d83..67c2cf5d1 100644 --- a/pkg/storage/sqlstorage/store_ledger_test.go +++ b/pkg/storage/sqlstorage/store_ledger_test.go @@ -29,28 +29,11 @@ func TestStore(t *testing.T) { fn func(t *testing.T, store *sqlstorage.Store) } - for _, tf := range []testingFunction{ - {name: "Commit", fn: testCommit}, - {name: "UpdateTransactionMetadata", fn: testUpdateTransactionMetadata}, - {name: "UpdateAccountMetadata", fn: testUpdateAccountMetadata}, - {name: "GetLastLog", fn: testGetLastLog}, - {name: "GetLogs", fn: testGetLogs}, - {name: "CountAccounts", fn: testCountAccounts}, - {name: "GetAssetsVolumes", fn: testGetAssetsVolumes}, - {name: "GetAccounts", fn: testGetAccounts}, - {name: "Transactions", fn: testTransactions}, - {name: "GetTransaction", fn: testGetTransaction}, - {name: "Mapping", fn: testMapping}, - {name: "TooManyClient", fn: testTooManyClient}, - {name: "GetBalances", fn: testGetBalances}, - {name: "GetBalancesBigInts", fn: testGetBalancesBigInts}, - {name: "GetBalancesAggregated", fn: testGetBalancesAggregated}, - {name: "CreateIK", fn: testIKS}, - } { - t.Run(fmt.Sprintf("%s/%s", ledgertesting.StorageDriverName(), tf.name), func(t *testing.T) { + runTest := func(tf testingFunction) func(t *testing.T) { + return func(t *testing.T) { done := make(chan struct{}) app := fx.New( - ledgertesting.ProvideStorageDriver(), + ledgertesting.ProvideStorageDriver(false), fx.NopLogger, fx.Invoke(func(driver *sqlstorage.Driver, lc fx.Lifecycle) { lc.Append(fx.Hook{ @@ -86,7 +69,32 @@ func TestStore(t *testing.T) { t.Fatal("timeout") case <-done: } - }) + } + } + + for _, tf := range []testingFunction{ + {name: "Accounts", fn: testAccounts}, + {name: "Commit", fn: testCommit}, + {name: "UpdateTransactionMetadata", fn: testUpdateTransactionMetadata}, + {name: "UpdateAccountMetadata", fn: testUpdateAccountMetadata}, + {name: "GetLastLog", fn: testGetLastLog}, + {name: "GetLogs", fn: testGetLogs}, + {name: "CountAccounts", fn: testCountAccounts}, + {name: "GetAssetsVolumes", fn: testGetAssetsVolumes}, + {name: "GetAccounts", fn: testGetAccounts}, + {name: "Transactions", fn: testTransactions}, + {name: "GetTransaction", fn: testGetTransaction}, + {name: "GetTransactionWithQueryAddress", fn: testTransactionsQueryAddress}, + {name: "Mapping", fn: testMapping}, + {name: "TooManyClient", fn: testTooManyClient}, + {name: "GetBalances", fn: testGetBalances}, + {name: "GetBalances1Accounts", fn: testGetBalancesOn1Account}, + {name: "GetBalancesBigInts", fn: testGetBalancesBigInts}, + {name: "GetBalancesAggregated", fn: testGetBalancesAggregated}, + {name: "GetBalancesAggregatedByAccount", fn: testGetBalancesAggregatedByAccount}, + {name: "CreateIK", fn: testIKS}, + } { + t.Run(fmt.Sprintf("%s/%s-singleInstance", ledgertesting.StorageDriverName(), tf.name), runTest((tf))) } } @@ -229,6 +237,104 @@ var tx3 = core.ExpandedTransaction{ }, } +var tx4 = core.ExpandedTransaction{ + Transaction: core.Transaction{ + ID: 3, + TransactionData: core.TransactionData{ + Postings: []core.Posting{ + { + Source: "central_bank", + Destination: "users:11", + Amount: core.NewMonetaryInt(1), + Asset: "USD", + }, + }, + Reference: "tx4", + Metadata: core.Metadata{ + "priority": json.RawMessage(`"high"`), + }, + Timestamp: now.Add(-1 * time.Hour), + }, + }, + PreCommitVolumes: core.AccountsAssetsVolumes{ + "central_bank": { + "USD": { + Input: core.NewMonetaryInt(200), + Output: core.NewMonetaryInt(0), + }, + }, + "users:11": { + "USD": { + Input: core.NewMonetaryInt(0), + Output: core.NewMonetaryInt(0), + }, + }, + }, + PostCommitVolumes: core.AccountsAssetsVolumes{ + "central_bank": { + "USD": { + Input: core.NewMonetaryInt(200), + Output: core.NewMonetaryInt(1), + }, + }, + "users:11": { + "USD": { + Input: core.NewMonetaryInt(1), + Output: core.NewMonetaryInt(0), + }, + }, + }, +} + +var tx5 = core.ExpandedTransaction{ + Transaction: core.Transaction{ + ID: 4, + TransactionData: core.TransactionData{ + Postings: []core.Posting{ + { + Source: "users:1", + Destination: "central_bank", + Amount: core.NewMonetaryInt(1), + Asset: "USD", + }, + }, + Reference: "tx5", + Metadata: core.Metadata{ + "priority": json.RawMessage(`"high"`), + }, + Timestamp: now.Add(-1 * time.Hour), + }, + }, + PreCommitVolumes: core.AccountsAssetsVolumes{ + "users:1": { + "USD": { + Input: core.NewMonetaryInt(200), + Output: core.NewMonetaryInt(0), + }, + }, + "central_bank": { + "USD": { + Input: core.NewMonetaryInt(0), + Output: core.NewMonetaryInt(0), + }, + }, + }, + PostCommitVolumes: core.AccountsAssetsVolumes{ + "users:!": { + "USD": { + Input: core.NewMonetaryInt(200), + Output: core.NewMonetaryInt(1), + }, + }, + "central_bank": { + "USD": { + Input: core.NewMonetaryInt(1), + Output: core.NewMonetaryInt(0), + }, + }, + }, +} + func testCommit(t *testing.T, store *sqlstorage.Store) { tx := core.ExpandedTransaction{ Transaction: core.Transaction{ @@ -503,167 +609,6 @@ func testGetAccounts(t *testing.T, store *sqlstorage.Store) { require.Len(t, accounts.Data, 1) } -func testTransactions(t *testing.T, store *sqlstorage.Store) { - err := store.Commit(context.Background(), tx1, tx2, tx3) - require.NoError(t, err) - - t.Run("Count", func(t *testing.T) { - count, err := store.CountTransactions(context.Background(), ledger.TransactionsQuery{}) - require.NoError(t, err) - // Should get all the transactions - require.EqualValues(t, 3, count) - - count, err = store.CountTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Account: "world", - }, - }) - require.NoError(t, err) - // Should get the two first transactions involving the 'world' account. - require.EqualValues(t, 2, count) - - count, err = store.CountTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Account: "world", - StartTime: now.Add(-2 * time.Hour), - EndTime: now.Add(-1 * time.Hour), - }, - }) - require.NoError(t, err) - // Should get only tx2, as StartTime is inclusive and EndTime exclusive. - require.EqualValues(t, 1, count) - - count, err = store.CountTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Metadata: map[string]string{ - "priority": "high", - }, - }, - }) - require.NoError(t, err) - require.EqualValues(t, 1, count) - }) - - t.Run("Get", func(t *testing.T) { - cursor, err := store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - PageSize: 1, - }) - require.NoError(t, err) - // Should get only the first transaction. - require.Equal(t, 1, cursor.PageSize) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - AfterTxID: cursor.Data[0].ID, - PageSize: 1, - }) - require.NoError(t, err) - // Should get only the second transaction. - require.Equal(t, 1, cursor.PageSize) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Account: "world", - Reference: "tx1", - }, - PageSize: 1, - }) - require.NoError(t, err) - require.Equal(t, 1, cursor.PageSize) - // Should get only the first transaction. - require.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Account: "users:.*", - }, - PageSize: 10, - }) - require.NoError(t, err) - require.Equal(t, 10, cursor.PageSize) - require.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Source: "central_bank", - }, - PageSize: 10, - }) - require.NoError(t, err) - require.Equal(t, 10, cursor.PageSize) - // Should get only the third transaction. - require.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Destination: "users:1", - }, - PageSize: 10, - }) - require.NoError(t, err) - require.Equal(t, 10, cursor.PageSize) - // Should get only the third transaction. - require.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Destination: "users:.*", // Use regex - }, - PageSize: 10, - }) - assert.NoError(t, err) - assert.Equal(t, 10, cursor.PageSize) - // Should get only the third transaction. - assert.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Destination: ".*:1", // Use regex - }, - PageSize: 10, - }) - assert.NoError(t, err) - assert.Equal(t, 10, cursor.PageSize) - // Should get only the third transaction. - assert.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Source: ".*bank", // Use regex - }, - PageSize: 10, - }) - assert.NoError(t, err) - assert.Equal(t, 10, cursor.PageSize) - // Should get only the third transaction. - assert.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - StartTime: now.Add(-2 * time.Hour), - EndTime: now.Add(-1 * time.Hour), - }, - PageSize: 10, - }) - require.NoError(t, err) - require.Equal(t, 10, cursor.PageSize) - // Should get only tx2, as StartTime is inclusive and EndTime exclusive. - require.Len(t, cursor.Data, 1) - - cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ - Filters: ledger.TransactionsQueryFilters{ - Metadata: map[string]string{ - "priority": "high", - }, - }, - PageSize: 10, - }) - require.NoError(t, err) - require.Equal(t, 10, cursor.PageSize) - // Should get only the third transaction. - require.Len(t, cursor.Data, 1) - }) -} - func testMapping(t *testing.T, store *sqlstorage.Store) { m := core.Mapping{ Contracts: []core.Contract{ @@ -695,17 +640,6 @@ func testMapping(t *testing.T, store *sqlstorage.Store) { assert.Len(t, mapping.Contracts, 0) } -func testGetTransaction(t *testing.T, store *sqlstorage.Store) { - err := store.Commit(context.Background(), tx1, tx2) - require.NoError(t, err) - - tx, err := store.GetTransaction(context.Background(), tx1.ID) - require.NoError(t, err) - require.Equal(t, tx1.Postings, tx.Postings) - require.Equal(t, tx1.Reference, tx.Reference) - require.Equal(t, tx1.Timestamp, tx.Timestamp) -} - func testTooManyClient(t *testing.T, store *sqlstorage.Store) { // Use of external server, ignore this test if os.Getenv("NUMARY_STORAGE_POSTGRES_CONN_STRING") != "" || diff --git a/pkg/storage/sqlstorage/transactions_test.go b/pkg/storage/sqlstorage/transactions_test.go index 882697119..3fa52f019 100644 --- a/pkg/storage/sqlstorage/transactions_test.go +++ b/pkg/storage/sqlstorage/transactions_test.go @@ -14,6 +14,7 @@ import ( "github.com/numary/ledger/pkg/ledger" "github.com/numary/ledger/pkg/ledgertesting" "github.com/numary/ledger/pkg/storage/sqlstorage" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/fx" ) @@ -21,40 +22,52 @@ import ( func BenchmarkStore_GetTransactions(b *testing.B) { b.StopTimer() - app := fx.New( - fx.NopLogger, - ledgertesting.ProvideStorageDriver(), - fx.Invoke(func(driver *sqlstorage.Driver, lc fx.Lifecycle) { - lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - ledgerName := uuid.NewString() - var store *sqlstorage.Store - var err error - for store == nil { - store, _, err = driver.GetLedgerStore(ctx, ledgerName, true) - if err != nil { - fmt.Printf("sqlstorage.Driver.GetLedgerStore: %s\n", err.Error()) - time.Sleep(3 * time.Second) - } + invokeFunc := func(driver *sqlstorage.Driver, lc fx.Lifecycle) { + lc.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + ledgerName := uuid.NewString() + var store *sqlstorage.Store + var err error + for store == nil { + store, _, err = driver.GetLedgerStore(ctx, ledgerName, true) + if err != nil { + fmt.Printf("sqlstorage.Driver.GetLedgerStore: %s\n", err.Error()) + time.Sleep(3 * time.Second) } - defer func(store ledger.Store, ctx context.Context) { - require.NoError(b, store.Close(ctx)) - }(store, context.Background()) + } + defer func(store ledger.Store, ctx context.Context) { + require.NoError(b, store.Close(ctx)) + }(store, context.Background()) - if _, err = store.Initialize(context.Background()); err != nil { - return err - } + if _, err = store.Initialize(context.Background()); err != nil { + return err + } - benchGetTransactions(b, store) - return nil - }, - }) - })) + benchGetTransactions(b, store) + return nil + }, + }) + } - require.NoError(b, app.Start(context.Background())) + appSingleInstance := fx.New( + fx.NopLogger, + ledgertesting.ProvideStorageDriver(false), + fx.Invoke(invokeFunc), + ) + require.NoError(b, appSingleInstance.Start(context.Background())) defer func(app *fx.App, ctx context.Context) { require.NoError(b, app.Stop(ctx)) - }(app, context.Background()) + }(appSingleInstance, context.Background()) + + appMultipleInstance := fx.New( + fx.NopLogger, + ledgertesting.ProvideStorageDriver(true), + fx.Invoke(invokeFunc), + ) + require.NoError(b, appMultipleInstance.Start(context.Background())) + defer func(app *fx.App, ctx context.Context) { + require.NoError(b, app.Stop(ctx)) + }(appMultipleInstance, context.Background()) } func benchGetTransactions(b *testing.B, store *sqlstorage.Store) { @@ -279,3 +292,234 @@ func getTxQueries(b *testing.B, store *sqlstorage.Store, pageSize, maxNumTxs int require.Equal(b, maxNumTxs, numTxs) return } + +func testGetTransaction(t *testing.T, store *sqlstorage.Store) { + err := store.Commit(context.Background(), tx1, tx2) + require.NoError(t, err) + + tx, err := store.GetTransaction(context.Background(), tx1.ID) + require.NoError(t, err) + require.Equal(t, tx1.Postings, tx.Postings) + require.Equal(t, tx1.Reference, tx.Reference) + require.Equal(t, tx1.Timestamp, tx.Timestamp) +} + +func testTransactions(t *testing.T, store *sqlstorage.Store) { + err := store.Commit(context.Background(), tx1, tx2, tx3) + require.NoError(t, err) + + t.Run("Count", func(t *testing.T) { + count, err := store.CountTransactions(context.Background(), ledger.TransactionsQuery{}) + require.NoError(t, err) + // Should get all the transactions + require.EqualValues(t, 3, count) + + count, err = store.CountTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Account: "world", + }, + }) + require.NoError(t, err) + // Should get the two first transactions involving the 'world' account. + require.EqualValues(t, 2, count) + + count, err = store.CountTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Account: "world", + StartTime: now.Add(-2 * time.Hour), + EndTime: now.Add(-1 * time.Hour), + }, + }) + require.NoError(t, err) + // Should get only tx2, as StartTime is inclusive and EndTime exclusive. + require.EqualValues(t, 1, count) + + count, err = store.CountTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Metadata: map[string]string{ + "priority": "high", + }, + }, + }) + require.NoError(t, err) + require.EqualValues(t, 1, count) + }) + + t.Run("Get", func(t *testing.T) { + cursor, err := store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + PageSize: 1, + }) + require.NoError(t, err) + // Should get only the first transaction. + require.Equal(t, 1, cursor.PageSize) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + AfterTxID: cursor.Data[0].ID, + PageSize: 1, + }) + require.NoError(t, err) + // Should get only the second transaction. + require.Equal(t, 1, cursor.PageSize) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Account: "world", + Reference: "tx1", + }, + PageSize: 1, + }) + require.NoError(t, err) + require.Equal(t, 1, cursor.PageSize) + // Should get only the first transaction. + require.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Account: "users:.*", + }, + PageSize: 10, + }) + require.NoError(t, err) + require.Equal(t, 10, cursor.PageSize) + require.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Source: "central_bank", + }, + PageSize: 10, + }) + require.NoError(t, err) + require.Equal(t, 10, cursor.PageSize) + // Should get only the third transaction. + require.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Destination: "users:1", + }, + PageSize: 10, + }) + require.NoError(t, err) + require.Equal(t, 10, cursor.PageSize) + // Should get only the third transaction. + require.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Destination: "users:.*", // Use regex + }, + PageSize: 10, + }) + assert.NoError(t, err) + assert.Equal(t, 10, cursor.PageSize) + // Should get only the third transaction. + assert.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Destination: ".*:1", // Use regex + }, + PageSize: 10, + }) + assert.NoError(t, err) + assert.Equal(t, 10, cursor.PageSize) + // Should get only the third transaction. + assert.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Source: ".*bank", // Use regex + }, + PageSize: 10, + }) + assert.NoError(t, err) + assert.Equal(t, 10, cursor.PageSize) + // Should get only the third transaction. + assert.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + StartTime: now.Add(-2 * time.Hour), + EndTime: now.Add(-1 * time.Hour), + }, + PageSize: 10, + }) + require.NoError(t, err) + require.Equal(t, 10, cursor.PageSize) + // Should get only tx2, as StartTime is inclusive and EndTime exclusive. + require.Len(t, cursor.Data, 1) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Metadata: map[string]string{ + "priority": "high", + }, + }, + PageSize: 10, + }) + require.NoError(t, err) + require.Equal(t, 10, cursor.PageSize) + // Should get only the third transaction. + require.Len(t, cursor.Data, 1) + }) +} + +func testTransactionsQueryAddress(t *testing.T, store *sqlstorage.Store) { + err := store.Commit(context.Background(), tx1, tx2, tx3, tx4, tx5) + require.NoError(t, err) + + t.Run("Count", func(t *testing.T) { + count, err := store.CountTransactions(context.Background(), ledger.TransactionsQuery{}) + require.NoError(t, err) + // Should get all the transactions + require.EqualValues(t, 5, count) + + count, err = store.CountTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Account: "users:1", + }, + }) + require.NoError(t, err) + require.EqualValues(t, 2, count) + }) + + t.Run("Get transactions with address query filter", func(t *testing.T) { + cursor, err := store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Account: "users:1", + }, + PageSize: 10, + }) + assert.NoError(t, err) + assert.Equal(t, 10, cursor.PageSize) + // Should get only tx3. + assert.Len(t, cursor.Data, 2) + assert.Equal(t, cursor.Data[0].ID, tx5.ID) + assert.Equal(t, cursor.Data[1].ID, tx3.ID) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Destination: "users:1", + }, + PageSize: 10, + }) + assert.NoError(t, err) + assert.Equal(t, 10, cursor.PageSize) + // Should get only tx3. + assert.Len(t, cursor.Data, 1) + assert.Equal(t, cursor.Data[0].ID, tx3.ID) + + cursor, err = store.GetTransactions(context.Background(), ledger.TransactionsQuery{ + Filters: ledger.TransactionsQueryFilters{ + Source: "users:1", + }, + PageSize: 10, + }) + assert.NoError(t, err) + assert.Equal(t, 10, cursor.PageSize) + // Should get only tx3. + assert.Len(t, cursor.Data, 1) + assert.Equal(t, cursor.Data[0].ID, tx5.ID) + }) +}