2024-06-18 10:31:23 +00:00
|
|
|
package router
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
2024-07-10 21:06:56 +00:00
|
|
|
"encoding/json"
|
2024-06-18 10:31:23 +00:00
|
|
|
"testing"
|
2024-07-10 21:06:56 +00:00
|
|
|
"time"
|
2024-06-18 10:31:23 +00:00
|
|
|
|
|
|
|
"github.com/status-im/status-go/appdatabase"
|
|
|
|
"github.com/status-im/status-go/params"
|
|
|
|
"github.com/status-im/status-go/rpc"
|
2024-08-28 12:06:50 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet/responses"
|
2024-06-18 10:31:23 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
2024-08-28 12:06:50 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet/router/routes"
|
2024-07-10 21:06:56 +00:00
|
|
|
"github.com/status-im/status-go/signal"
|
2024-06-18 10:31:23 +00:00
|
|
|
"github.com/status-im/status-go/t/helpers"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2024-07-04 10:48:14 +00:00
|
|
|
func amountOptionEqual(a, b amountOption) bool {
|
|
|
|
return a.amount.Cmp(b.amount) == 0 && a.locked == b.locked
|
|
|
|
}
|
|
|
|
|
|
|
|
func contains(slice []amountOption, val amountOption) bool {
|
|
|
|
for _, item := range slice {
|
|
|
|
if amountOptionEqual(item, val) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func amountOptionsMapsEqual(map1, map2 map[uint64][]amountOption) bool {
|
|
|
|
if len(map1) != len(map2) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for key, slice1 := range map1 {
|
|
|
|
slice2, ok := map2[key]
|
|
|
|
if !ok || len(slice1) != len(slice2) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, val1 := range slice1 {
|
|
|
|
if !contains(slice2, val1) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, val2 := range slice2 {
|
|
|
|
if !contains(slice1, val2) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-08-28 12:06:50 +00:00
|
|
|
func assertPathsEqual(t *testing.T, expected, actual routes.Route) {
|
2024-07-10 21:06:56 +00:00
|
|
|
assert.Equal(t, len(expected), len(actual))
|
2024-07-18 12:20:54 +00:00
|
|
|
if len(expected) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2024-07-10 21:06:56 +00:00
|
|
|
|
|
|
|
for _, c := range actual {
|
|
|
|
found := false
|
|
|
|
for _, expC := range expected {
|
|
|
|
if c.ProcessorName == expC.ProcessorName &&
|
|
|
|
c.FromChain.ChainID == expC.FromChain.ChainID &&
|
|
|
|
c.ToChain.ChainID == expC.ToChain.ChainID &&
|
|
|
|
c.ApprovalRequired == expC.ApprovalRequired &&
|
|
|
|
(expC.AmountOut == nil || c.AmountOut.ToInt().Cmp(expC.AmountOut.ToInt()) == 0) {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.True(t, found)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-18 10:31:23 +00:00
|
|
|
func setupTestNetworkDB(t *testing.T) (*sql.DB, func()) {
|
|
|
|
db, cleanup, err := helpers.SetupTestSQLDB(appdatabase.DbInitializer{}, "wallet-router-tests")
|
|
|
|
require.NoError(t, err)
|
|
|
|
return db, func() { require.NoError(t, cleanup()) }
|
|
|
|
}
|
|
|
|
|
2024-06-30 20:44:21 +00:00
|
|
|
func setupRouter(t *testing.T) (*Router, func()) {
|
|
|
|
db, cleanTmpDb := setupTestNetworkDB(t)
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-07-31 06:21:11 +00:00
|
|
|
client, _ := rpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, defaultNetworks, db, nil)
|
2024-06-18 10:31:23 +00:00
|
|
|
|
|
|
|
router := NewRouter(client, nil, nil, nil, nil, nil, nil, nil)
|
|
|
|
|
|
|
|
transfer := pathprocessor.NewTransferProcessor(nil, nil)
|
|
|
|
router.AddPathProcessor(transfer)
|
|
|
|
|
|
|
|
erc721Transfer := pathprocessor.NewERC721Processor(nil, nil)
|
|
|
|
router.AddPathProcessor(erc721Transfer)
|
|
|
|
|
|
|
|
erc1155Transfer := pathprocessor.NewERC1155Processor(nil, nil)
|
|
|
|
router.AddPathProcessor(erc1155Transfer)
|
|
|
|
|
2024-06-27 21:27:09 +00:00
|
|
|
hop := pathprocessor.NewHopBridgeProcessor(nil, nil, nil, nil)
|
2024-06-18 10:31:23 +00:00
|
|
|
router.AddPathProcessor(hop)
|
|
|
|
|
|
|
|
paraswap := pathprocessor.NewSwapParaswapProcessor(nil, nil, nil)
|
|
|
|
router.AddPathProcessor(paraswap)
|
|
|
|
|
|
|
|
ensRegister := pathprocessor.NewENSReleaseProcessor(nil, nil, nil)
|
|
|
|
router.AddPathProcessor(ensRegister)
|
|
|
|
|
|
|
|
ensRelease := pathprocessor.NewENSReleaseProcessor(nil, nil, nil)
|
|
|
|
router.AddPathProcessor(ensRelease)
|
|
|
|
|
|
|
|
ensPublicKey := pathprocessor.NewENSPublicKeyProcessor(nil, nil, nil)
|
|
|
|
router.AddPathProcessor(ensPublicKey)
|
|
|
|
|
|
|
|
buyStickers := pathprocessor.NewStickersBuyProcessor(nil, nil, nil)
|
|
|
|
router.AddPathProcessor(buyStickers)
|
|
|
|
|
2024-06-30 20:44:21 +00:00
|
|
|
return router, cleanTmpDb
|
|
|
|
}
|
|
|
|
|
2024-08-28 12:06:50 +00:00
|
|
|
type routerSuggestedRoutesEnvelope struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
Routes responses.RouterSuggestedRoutes `json:"event"`
|
2024-07-10 21:06:56 +00:00
|
|
|
}
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-08-28 12:06:50 +00:00
|
|
|
func setupSignalHandler(t *testing.T) (chan responses.RouterSuggestedRoutes, func()) {
|
|
|
|
suggestedRoutesCh := make(chan responses.RouterSuggestedRoutes)
|
2024-07-10 21:06:56 +00:00
|
|
|
signalHandler := signal.MobileSignalHandler(func(data []byte) {
|
|
|
|
var envelope signal.Envelope
|
|
|
|
err := json.Unmarshal(data, &envelope)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
if envelope.Type == string(signal.SuggestedRoutes) {
|
2024-08-28 12:06:50 +00:00
|
|
|
var response routerSuggestedRoutesEnvelope
|
2024-07-10 21:06:56 +00:00
|
|
|
err := json.Unmarshal(data, &response)
|
|
|
|
assert.NoError(t, err)
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
suggestedRoutesCh <- response.Routes
|
|
|
|
}
|
|
|
|
})
|
|
|
|
signal.SetMobileSignalHandler(signalHandler)
|
2024-09-03 08:41:56 +00:00
|
|
|
t.Cleanup(signal.ResetMobileSignalHandler)
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
closeFn := func() {
|
|
|
|
close(suggestedRoutesCh)
|
|
|
|
}
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
return suggestedRoutesCh, closeFn
|
|
|
|
}
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-08-28 09:23:32 +00:00
|
|
|
func TestRouter(t *testing.T) {
|
2024-07-10 21:06:56 +00:00
|
|
|
router, cleanTmpDb := setupRouter(t)
|
|
|
|
defer cleanTmpDb()
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
suggestedRoutesCh, closeSignalHandler := setupSignalHandler(t)
|
|
|
|
defer closeSignalHandler()
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
tests := getNormalTestParamsList()
|
2024-06-18 10:31:23 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
// Test blocking endpoints
|
2024-06-18 10:31:23 +00:00
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2024-08-28 09:23:32 +00:00
|
|
|
routes, err := router.SuggestedRoutes(context.Background(), tt.input)
|
2024-06-18 10:31:23 +00:00
|
|
|
|
|
|
|
if tt.expectedError != nil {
|
|
|
|
assert.Error(t, err)
|
2024-07-10 21:06:56 +00:00
|
|
|
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
2024-07-18 12:20:54 +00:00
|
|
|
if routes == nil {
|
|
|
|
assert.Empty(t, tt.expectedCandidates)
|
|
|
|
} else {
|
|
|
|
assertPathsEqual(t, tt.expectedCandidates, routes.Candidates)
|
|
|
|
}
|
2024-06-18 10:31:23 +00:00
|
|
|
} else {
|
|
|
|
assert.NoError(t, err)
|
2024-07-10 21:06:56 +00:00
|
|
|
assertPathsEqual(t, tt.expectedCandidates, routes.Candidates)
|
2024-06-18 10:31:23 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2024-07-10 21:06:56 +00:00
|
|
|
|
|
|
|
// Test async endpoints
|
|
|
|
for _, tt := range tests {
|
2024-08-28 09:23:32 +00:00
|
|
|
router.SuggestedRoutesAsync(tt.input)
|
2024-07-10 21:06:56 +00:00
|
|
|
|
|
|
|
select {
|
|
|
|
case asyncRoutes := <-suggestedRoutesCh:
|
|
|
|
assert.Equal(t, tt.input.Uuid, asyncRoutes.Uuid)
|
|
|
|
assert.Equal(t, tt.expectedError, asyncRoutes.ErrorResponse)
|
|
|
|
assertPathsEqual(t, tt.expectedCandidates, asyncRoutes.Candidates)
|
|
|
|
break
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
}
|
2024-06-18 10:31:23 +00:00
|
|
|
}
|
2024-06-30 20:44:21 +00:00
|
|
|
|
2024-08-28 09:23:32 +00:00
|
|
|
func TestNoBalanceForTheBestRouteRouter(t *testing.T) {
|
2024-06-30 20:44:21 +00:00
|
|
|
router, cleanTmpDb := setupRouter(t)
|
|
|
|
defer cleanTmpDb()
|
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
suggestedRoutesCh, closeSignalHandler := setupSignalHandler(t)
|
|
|
|
defer closeSignalHandler()
|
2024-06-30 20:44:21 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
tests := getNoBalanceTestParamsList()
|
2024-06-30 20:44:21 +00:00
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
// Test blocking endpoints
|
2024-06-30 20:44:21 +00:00
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
2024-08-28 09:23:32 +00:00
|
|
|
routes, err := router.SuggestedRoutes(context.Background(), tt.input)
|
2024-06-30 20:44:21 +00:00
|
|
|
|
|
|
|
if tt.expectedError != nil {
|
|
|
|
assert.Error(t, err)
|
2024-07-10 21:06:56 +00:00
|
|
|
assert.Equal(t, tt.expectedError.Error(), err.Error())
|
2024-08-23 14:01:49 +00:00
|
|
|
if tt.expectedError == ErrNoPositiveBalance {
|
|
|
|
assert.Nil(t, routes)
|
|
|
|
} else {
|
|
|
|
assert.NotNil(t, routes)
|
|
|
|
assertPathsEqual(t, tt.expectedCandidates, routes.Candidates)
|
|
|
|
}
|
2024-06-30 20:44:21 +00:00
|
|
|
} else {
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, len(tt.expectedCandidates), len(routes.Candidates))
|
|
|
|
assert.Equal(t, len(tt.expectedBest), len(routes.Best))
|
2024-07-10 21:06:56 +00:00
|
|
|
assertPathsEqual(t, tt.expectedCandidates, routes.Candidates)
|
|
|
|
assertPathsEqual(t, tt.expectedBest, routes.Best)
|
2024-06-30 20:44:21 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2024-07-10 21:06:56 +00:00
|
|
|
|
|
|
|
// Test async endpoints
|
|
|
|
for _, tt := range tests {
|
2024-07-27 09:27:26 +00:00
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2024-07-10 21:06:56 +00:00
|
|
|
|
2024-08-28 09:23:32 +00:00
|
|
|
router.SuggestedRoutesAsync(tt.input)
|
2024-07-27 09:27:26 +00:00
|
|
|
|
|
|
|
select {
|
|
|
|
case asyncRoutes := <-suggestedRoutesCh:
|
|
|
|
assert.Equal(t, tt.input.Uuid, asyncRoutes.Uuid)
|
|
|
|
assert.Equal(t, tt.expectedError, asyncRoutes.ErrorResponse)
|
|
|
|
assertPathsEqual(t, tt.expectedCandidates, asyncRoutes.Candidates)
|
|
|
|
if tt.expectedError == nil {
|
|
|
|
assertPathsEqual(t, tt.expectedBest, asyncRoutes.Best)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.FailNow()
|
2024-07-10 21:06:56 +00:00
|
|
|
}
|
2024-07-27 09:27:26 +00:00
|
|
|
})
|
2024-07-10 21:06:56 +00:00
|
|
|
}
|
2024-06-30 20:44:21 +00:00
|
|
|
}
|
2024-06-10 15:17:41 +00:00
|
|
|
|
2024-07-04 10:48:14 +00:00
|
|
|
func TestAmountOptions(t *testing.T) {
|
|
|
|
router, cleanTmpDb := setupRouter(t)
|
|
|
|
defer cleanTmpDb()
|
|
|
|
|
2024-07-10 21:06:56 +00:00
|
|
|
tests := getAmountOptionsTestParamsList()
|
2024-06-10 15:17:41 +00:00
|
|
|
|
2024-07-04 10:48:14 +00:00
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
|
|
selectedFromChains, _, err := router.getSelectedChains(tt.input)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2024-08-30 09:03:52 +00:00
|
|
|
router.SetTestBalanceMap(tt.input.TestParams.BalanceMap)
|
|
|
|
amountOptions, err := router.findOptionsForSendingAmount(tt.input, selectedFromChains)
|
2024-07-04 10:48:14 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, len(tt.expectedAmountOptions), len(amountOptions))
|
|
|
|
assert.True(t, amountOptionsMapsEqual(tt.expectedAmountOptions, amountOptions))
|
2024-06-10 15:17:41 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|