synapsecns/sanguine

View on GitHub
services/cctp-relayer/api/server_test.go

Summary

Maintainability
A
0 mins
Test Coverage
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)
}