services/cctp-relayer/api/server_test.go
package api_test
import (
"context"
"encoding/json"
"fmt"
"github.com/phayes/freeport"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/brianvoe/gofakeit/v6"
"github.com/ethereum/go-ethereum/common"
"github.com/synapsecns/sanguine/ethergo/mocks"
"github.com/synapsecns/sanguine/services/cctp-relayer/api"
relayTypes "github.com/synapsecns/sanguine/services/cctp-relayer/types"
)
func (s *RelayerAPISuite) mockMessage(originChainID uint32, state relayTypes.MessageState) relayTypes.Message {
return relayTypes.Message{
OriginTxHash: mocks.NewMockHash(s.T()).String(),
DestTxHash: mocks.NewMockHash(s.T()).String(),
OriginChainID: originChainID,
DestChainID: originChainID + 1,
Message: []byte(gofakeit.Paragraph(10, 10, 10, " ")),
MessageHash: mocks.NewMockHash(s.T()).String(),
Attestation: []byte(gofakeit.Paragraph(10, 10, 10, " ")),
RequestVersion: 0,
FormattedRequest: []byte(gofakeit.Paragraph(10, 10, 10, " ")),
RequestID: strings.TrimPrefix(mocks.NewMockHash(s.T()).String(), "0x"),
BlockNumber: 1,
State: state,
}
}
//nolint:gosec,wrapcheck
func getTx(ctx context.Context, hash string, origin uint32, port int) (*http.Response, error) {
txURL := fmt.Sprintf("http://localhost:%d/tx", port)
params := url.Values{}
params.Set("hash", hash)
params.Set("origin", strconv.Itoa(int(origin)))
reqURL := fmt.Sprintf("%s?%s", txURL, params.Encode())
client := &http.Client{
Timeout: 5 * time.Second,
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
if err != nil {
return nil, err
}
return client.Do(req)
}
func (s *RelayerAPISuite) TestPendingTx() {
reqChan := make(chan *api.RelayRequest, 1000)
port, err := freeport.GetFreePort()
s.Nil(err)
server := api.NewRelayerAPIServer(uint16(port), "localhost", s.testStore, reqChan)
ctx, cancel := context.WithCancel(s.GetTestContext())
//nolint:errcheck
go server.Start(ctx)
defer cancel()
// store pending tx
msg := s.mockMessage(1, relayTypes.Pending)
err = s.testStore.StoreMessage(ctx, msg)
s.Nil(err)
// make api request
resp, err := getTx(s.GetTestContext(), msg.OriginTxHash, msg.OriginChainID, port)
s.Nil(err)
defer func() {
err := resp.Body.Close()
s.Nil(err)
}()
s.Equal(resp.StatusCode, http.StatusOK)
// verify response
body, err := io.ReadAll(resp.Body)
s.Nil(err)
var relayerResp api.RelayerResponse
err = json.Unmarshal(body, &relayerResp)
s.Nil(err)
s.True(relayerResp.Success)
resultMap, ok := relayerResp.Result.(map[string]interface{})
s.True(ok)
//nolint:forcetypeassert
result := api.MessageResult{
OriginHash: resultMap["origin_hash"].(string),
DestinationHash: resultMap["destination_hash"].(string),
Origin: uint32(resultMap["origin"].(float64)),
Destination: uint32(resultMap["destination"].(float64)),
RequestID: resultMap["request_id"].(string),
State: resultMap["state"].(string),
}
expectedResult := api.MessageResult{
OriginHash: msg.OriginTxHash,
DestinationHash: msg.DestTxHash,
Origin: msg.OriginChainID,
Destination: msg.DestChainID,
RequestID: msg.RequestID,
State: msg.State.String(),
}
s.Equal(expectedResult, result)
}
func (s *RelayerAPISuite) TestMissingTx() {
reqChan := make(chan *api.RelayRequest, 1000)
port, err := freeport.GetFreePort()
s.Nil(err)
server := api.NewRelayerAPIServer(uint16(port), "localhost", s.testStore, reqChan)
ctx, cancel := context.WithCancel(s.GetTestContext())
//nolint:errcheck
go server.Start(ctx)
defer cancel()
// create mock message, but don't store it
msg := s.mockMessage(1, relayTypes.Pending)
// make api request
resp, err := getTx(s.GetTestContext(), msg.OriginTxHash, msg.OriginChainID, port)
s.Nil(err)
defer func() {
err := resp.Body.Close()
s.Nil(err)
}()
s.Equal(resp.StatusCode, http.StatusOK)
// verify response
body, err := io.ReadAll(resp.Body)
s.Nil(err)
var relayerResp api.RelayerResponse
err = json.Unmarshal(body, &relayerResp)
s.Nil(err)
s.True(relayerResp.Success)
result, ok := relayerResp.Result.(string)
s.True(ok)
expectedResult := fmt.Sprintf("Successfully queued relay request from chain %d: %s", msg.OriginChainID, msg.OriginTxHash)
s.Equal(expectedResult, result)
// verify request was queued
relayReq := <-reqChan
expectedRelayReq := &api.RelayRequest{
Origin: msg.OriginChainID,
TxHash: common.HexToHash(msg.OriginTxHash),
}
s.Equal(expectedRelayReq, relayReq)
}
func (s *RelayerAPISuite) TestBadRequest() {
reqChan := make(chan *api.RelayRequest, 1000)
port, err := freeport.GetFreePort()
s.Nil(err)
server := api.NewRelayerAPIServer(uint16(port), "localhost", s.testStore, reqChan)
ctx, cancel := context.WithCancel(s.GetTestContext())
//nolint:errcheck
go server.Start(ctx)
defer cancel()
// store pending tx
msg := s.mockMessage(1, relayTypes.Pending)
err = s.testStore.StoreMessage(ctx, msg)
s.Nil(err)
// make api request with no params
txURL := fmt.Sprintf("http://localhost:%d/tx", port)
client := &http.Client{
Timeout: 5 * time.Second,
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, txURL, nil)
s.Nil(err)
resp, err := client.Do(req)
s.Nil(err)
defer func() {
err := resp.Body.Close()
s.Nil(err)
}()
s.Equal(resp.StatusCode, http.StatusBadRequest)
// verify response
body, err := io.ReadAll(resp.Body)
s.Nil(err)
var relayerResp api.RelayerResponse
err = json.Unmarshal(body, &relayerResp)
s.Nil(err)
s.False(relayerResp.Success)
resultMap, ok := relayerResp.Result.(map[string]interface{})
s.True(ok)
//nolint:forcetypeassert
reason := resultMap["reason"].(string)
expectedReason := "required parameter 'origin' is missing"
s.Equal(expectedReason, reason)
}