synapsecns/sanguine

View on GitHub
services/omnirpc/rpcinfo/latency_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package rpcinfo_test

import (
    "context"
    "net/http"
    "net/http/httptest"
    "sync"
    "time"

    . "github.com/stretchr/testify/assert"
    "github.com/synapsecns/sanguine/core/metrics"
    "github.com/synapsecns/sanguine/ethergo/backends/geth"
    "github.com/synapsecns/sanguine/ethergo/backends/preset"
    "github.com/synapsecns/sanguine/services/omnirpc/rpcinfo"
    "golang.org/x/sync/errgroup"
)

func (r *LatencySuite) TestRPCLatency() {
    var bsc, avalanche *geth.Backend
    g, _ := errgroup.WithContext(r.GetTestContext())
    g.Go(func() error {
        bsc = preset.GetMaticMumbai().Geth(r.GetTestContext(), r.T())
        return nil
    })
    g.Go(func() error {
        avalanche = preset.GetSepolia().Geth(r.GetTestContext(), r.T())
        return nil
    })
    Nil(r.T(), g.Wait())

    latencySlice := rpcinfo.GetRPCLatency(r.GetTestContext(), time.Second*3, []string{bsc.HTTPEndpoint(), avalanche.HTTPEndpoint()}, metrics.NewNullHandler())
    NotEqual(r.T(), latencySlice[0].URL, latencySlice[1].URL)
    for _, latencyData := range latencySlice {
        False(r.T(), latencyData.HasError)
        Nil(r.T(), latencyData.Error)
    }
}

var statusCodes = []int{
    http.StatusBadRequest,
    http.StatusUnauthorized,
    http.StatusPaymentRequired,
    http.StatusNotFound,
    http.StatusMethodNotAllowed,
    http.StatusNotAcceptable,
    http.StatusRequestTimeout,
    http.StatusGone,
    http.StatusLengthRequired,
    http.StatusPreconditionRequired,
    http.StatusPreconditionFailed,
    http.StatusRequestEntityTooLarge,
    http.StatusRequestEntityTooLarge,
    // just for fun
    http.StatusTeapot,
    http.StatusMisdirectedRequest,
    http.StatusTooManyRequests,
    http.StatusRequestHeaderFieldsTooLarge,
    http.StatusInternalServerError,
    http.StatusNotImplemented,
    http.StatusGatewayTimeout,
    http.StatusBadGateway,
    http.StatusBadRequest,
    http.StatusLoopDetected,
    http.StatusNotExtended,
    http.StatusNetworkAuthenticationRequired,
}

func (r *LatencySuite) TestGetLatencyError() {
    ctx, cancel := context.WithTimeout(r.GetTestContext(), time.Minute)
    defer cancel()

    wg := &sync.WaitGroup{}

    for _, status := range statusCodes {
        // capture func literal
        status := status

        wg.Add(1)

        go func() {
            server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                w.WriteHeader(status)
            }))

            res := rpcinfo.GetLatency(ctx, server.URL)

            // there's an error, these should be nil
            Equal(r.T(), res.Latency, time.Duration(0))
            Equal(r.T(), res.BlockAge, time.Duration(0))

            server.Close()
            wg.Done()
        }()
    }

    select {
    case <-wrapWait(wg):
        // all good
    case <-ctx.Done():
        // context canceled
        r.T().Fail()
    }
}

// helper function to allow using WaitGroup in a select
// see: https://stackoverflow.com/a/70050009
func wrapWait(wg *sync.WaitGroup) <-chan struct{} {
    out := make(chan struct{})
    go func() {
        wg.Wait()
        out <- struct{}{}
    }()
    return out
}