services/rfq/api/client/suite_test.go
package client_test
import (
"fmt"
"math/big"
"sync"
"testing"
"time"
"github.com/Flaque/filet"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/phayes/freeport"
"github.com/puzpuzpuz/xsync/v2"
"github.com/stretchr/testify/suite"
"github.com/synapsecns/sanguine/core/dbcommon"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/synapsecns/sanguine/core/testsuite"
"github.com/synapsecns/sanguine/ethergo/backends"
"github.com/synapsecns/sanguine/ethergo/backends/geth"
"github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner"
"github.com/synapsecns/sanguine/ethergo/signer/wallet"
omniClient "github.com/synapsecns/sanguine/services/omnirpc/client"
omnirpcHelper "github.com/synapsecns/sanguine/services/omnirpc/testhelper"
"github.com/synapsecns/sanguine/services/rfq/api/client"
"github.com/synapsecns/sanguine/services/rfq/api/config"
"github.com/synapsecns/sanguine/services/rfq/api/db"
"github.com/synapsecns/sanguine/services/rfq/api/db/sql"
"github.com/synapsecns/sanguine/services/rfq/api/rest"
"github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge"
"golang.org/x/sync/errgroup"
)
type ClientSuite struct {
*testsuite.TestSuite
omniRPCClient omniClient.RPCClient
omniRPCTestBackends []backends.SimulatedTestBackend
testBackends map[uint64]backends.SimulatedTestBackend
fastBridgeAddressMap *xsync.MapOf[uint64, common.Address]
database db.APIDB
cfg config.Config
testWallet wallet.Wallet
handler metrics.Handler
QuoterAPIServer *rest.QuoterAPIServer
port uint16
client client.AuthenticatedClient
}
func NewTestClientSuite(tb testing.TB) *ClientSuite {
tb.Helper()
return &ClientSuite{
TestSuite: testsuite.NewTestSuite(tb),
}
}
func (c *ClientSuite) SetupTest() {
c.TestSuite.SetupTest()
metricsHandler := metrics.NewNullHandler()
c.handler = metricsHandler
dbType, err := dbcommon.DBTypeFromString("sqlite")
c.Require().NoError(err)
// TODO use temp file / in memory sqlite3 to not create in directory files
testDB, err := sql.Connect(c.GetSuiteContext(), dbType, filet.TmpDir(c.T(), ""), metricsHandler)
c.Require().NoError(err)
c.database = testDB
testOmnirpc := omnirpcHelper.NewOmnirpcServer(c.GetTestContext(), c.T(), c.omniRPCTestBackends...)
omniRPCClient := omniClient.NewOmnirpcClient(testOmnirpc, c.handler, omniClient.WithCaptureReqRes())
c.omniRPCClient = omniRPCClient
arbFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(42161)
c.True(ok)
ethFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(1)
c.True(ok)
port, err := freeport.GetFreePort()
c.port = uint16(port)
c.Require().NoError(err)
testConfig := config.Config{
Database: config.DatabaseConfig{
Type: "sqlite",
DSN: filet.TmpFile(c.T(), "", "").Name(),
},
OmniRPCURL: testOmnirpc,
Bridges: map[uint32]string{
1: ethFastBridgeAddress.Hex(),
42161: arbFastBridgeAddress.Hex(),
},
Port: fmt.Sprintf("%d", port),
}
c.cfg = testConfig
QuoterAPIServer, err := rest.NewAPI(c.GetTestContext(), c.cfg, c.handler, c.omniRPCClient, c.database)
c.Require().NoError(err)
c.QuoterAPIServer = QuoterAPIServer
go func() {
err := c.QuoterAPIServer.Run(c.GetTestContext())
c.Require().NoError(err)
}()
time.Sleep(2 * time.Second) // Wait for the server to start.
c.client, err = client.NewAuthenticatedClient(metrics.Get(), fmt.Sprintf("http://127.0.0.1:%d", port), localsigner.NewSigner(c.testWallet.PrivateKey()))
c.Require().NoError(err)
}
func (c *ClientSuite) SetupSuite() {
c.TestSuite.SetupSuite()
// let's create 2 mock chains
chainIDs := []uint64{1, 42161}
c.testBackends = make(map[uint64]backends.SimulatedTestBackend)
var mux sync.Mutex
g, _ := errgroup.WithContext(c.GetSuiteContext())
for _, chainID := range chainIDs {
chainID := chainID // capture func literal
g.Go(func() error {
// Setup backend for the suite to have RPC support
backend := geth.NewEmbeddedBackendForChainID(c.GetSuiteContext(), c.T(), new(big.Int).SetUint64(chainID))
// add the backend to the list of backends
mux.Lock()
defer mux.Unlock()
c.testBackends[chainID] = backend
c.omniRPCTestBackends = append(c.omniRPCTestBackends, backend)
return nil
})
}
// wait for all backends to be ready
if err := g.Wait(); err != nil {
c.T().Fatal(err)
}
testWallet, err := wallet.FromRandom()
c.Require().NoError(err)
c.testWallet = testWallet
for _, backend := range c.testBackends {
backend.FundAccount(c.GetSuiteContext(), c.testWallet.Address(), *big.NewInt(params.Ether))
}
c.fastBridgeAddressMap = xsync.NewIntegerMapOf[uint64, common.Address]()
g, _ = errgroup.WithContext(c.GetSuiteContext())
for _, backend := range c.testBackends {
backend := backend
g.Go(func() error {
chainID, err := backend.ChainID(c.GetSuiteContext())
if err != nil {
return fmt.Errorf("could not get chainID: %w", err)
}
// Create an auth to interact with the blockchain
auth, err := bind.NewKeyedTransactorWithChainID(c.testWallet.PrivateKey(), chainID)
c.Require().NoError(err)
// Deploy the FastBridge contract
fastBridgeAddress, tx, _, err := fastbridge.DeployFastBridge(auth, backend, c.testWallet.Address())
c.Require().NoError(err)
backend.WaitForConfirmation(c.GetSuiteContext(), tx)
// Save the contracts to the map
c.fastBridgeAddressMap.Store(chainID.Uint64(), fastBridgeAddress)
fastBridgeInstance, err := fastbridge.NewFastBridge(fastBridgeAddress, backend)
c.Require().NoError(err)
relayerRole, err := fastBridgeInstance.RELAYERROLE(&bind.CallOpts{Context: c.GetTestContext()})
c.NoError(err)
tx, err = fastBridgeInstance.GrantRole(auth, relayerRole, c.testWallet.Address())
c.Require().NoError(err)
backend.WaitForConfirmation(c.GetSuiteContext(), tx)
return nil
})
}
// wait for all backends to be ready
if err := g.Wait(); err != nil {
c.T().Fatal(err)
}
// setup config
}
// TestConfigSuite runs the integration test suite.
func TestClientSuite(t *testing.T) {
suite.Run(t, NewTestClientSuite(t))
}