Skip to content

Commit

Permalink
add unit tests for HeightShardingProxies
Browse files Browse the repository at this point in the history
  • Loading branch information
pirtleshell committed Oct 11, 2023
1 parent ab412e9 commit f04687a
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 23 deletions.
176 changes: 170 additions & 6 deletions service/proxy_test.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
package service_test

import (
"context"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"testing"

"github.com/kava-labs/kava-proxy-service/config"
"github.com/kava-labs/kava-proxy-service/decode"
"github.com/kava-labs/kava-proxy-service/service"
"github.com/stretchr/testify/require"
)

func newConfig(t *testing.T, rawHostMap string) config.Config {
parsed, err := config.ParseRawProxyBackendHostURLMap(rawHostMap)
func newConfig(t *testing.T, defaultHostMap string, pruningHostMap string) config.Config {
parsed, err := config.ParseRawProxyBackendHostURLMap(defaultHostMap)
require.NoError(t, err)
return config.Config{
ProxyBackendHostURLMapRaw: rawHostMap,
result := config.Config{
ProxyBackendHostURLMapRaw: defaultHostMap,
ProxyBackendHostURLMapParsed: parsed,
}
if pruningHostMap != "" {
result.EnableHeightBasedRouting = true
result.ProxyPruningBackendHostURLMapRaw = pruningHostMap
result.ProxyPruningBackendHostURLMap, err = config.ParseRawProxyBackendHostURLMap(pruningHostMap)
require.NoError(t, err)
}
return result
}

func TestUnitTest_NewProxies(t *testing.T) {
t.Run("returns a HostProxies when sharding disabled", func(t *testing.T) {
config := newConfig(t, dummyConfig.ProxyBackendHostURLMapRaw)
config := newConfig(t, dummyConfig.ProxyBackendHostURLMapRaw, "")
proxies := service.NewProxies(config, dummyLogger)
require.IsType(t, service.HostProxies{}, proxies)
})
// TODO: HeightShardingProxies

t.Run("returns a HeightShardingProxies when sharding enabled", func(t *testing.T) {
config := newConfig(t, dummyConfig.ProxyBackendHostURLMapRaw, dummyConfig.ProxyPruningBackendHostURLMapRaw)
proxies := service.NewProxies(config, dummyLogger)
require.IsType(t, service.HeightShardingProxies{}, proxies)
})
}

func TestUnitTest_HostProxies(t *testing.T) {
config := newConfig(t,
"magic.kava.io>magicalbackend.kava.io,archive.kava.io>archivenode.kava.io,pruning.kava.io>pruningnode.kava.io",
"",
)
proxies := service.NewProxies(config, dummyLogger)

Expand All @@ -59,6 +74,145 @@ func TestUnitTest_HostProxies(t *testing.T) {
})
}

func TestUnitTest_HeightShardingProxies(t *testing.T) {
archiveBackend := "archivenode.kava.io/"
pruningBackend := "pruningnode.kava.io/"
config := newConfig(t,
fmt.Sprintf("archive.kava.io>%s,pruning.kava.io>%s", archiveBackend, pruningBackend),
fmt.Sprintf("archive.kava.io>%s", pruningBackend),
)
proxies := service.NewProxies(config, dummyLogger)

testCases := []struct {
name string
url string
req *decode.EVMRPCRequestEnvelope
expectFound bool
expectRoute string
}{
// DEFAULT ROUTE CASES
{
name: "routes to default when not in pruning map",
url: "//pruning.kava.io",
req: &decode.EVMRPCRequestEnvelope{},
expectFound: true,
expectRoute: pruningBackend,
},
{
name: "routes to default for specific non-latest height",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_getBlockByNumber",
Params: []interface{}{"0xbaddad", false},
},
expectFound: true,
expectRoute: archiveBackend,
},
{
name: "routes to default for methods that don't have block number",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_getBlockByHash",
Params: []interface{}{"0xe9bd10bc1d62b4406dd1fb3dbf3adb54f640bdb9ebbe3dd6dfc6bcc059275e54", false},
},
expectFound: true,
expectRoute: archiveBackend,
},
{
name: "routes to default if it fails to decode req",
url: "//archive.kava.io",
req: nil,
expectFound: true,
expectRoute: archiveBackend,
},
{
name: "routes to default if it fails to parse block number",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_getBlockByNumber",
Params: []interface{}{"not-a-block-tag", false},
},
expectFound: true,
expectRoute: archiveBackend,
},
{
name: "routes to default for 'earliest' block",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_getBlockByNumber",
Params: []interface{}{"earliest", false},
},
expectFound: true,
expectRoute: archiveBackend,
},

// PRUNING ROUTE CASES
{
name: "routes to pruning for 'latest' block",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_getBlockByNumber",
Params: []interface{}{"latest", false},
},
expectFound: true,
expectRoute: pruningBackend,
},
{
name: "routes to pruning when block number empty",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_getBlockByNumber",
Params: []interface{}{nil, false},
},
expectFound: true,
expectRoute: pruningBackend,
},
{
name: "routes to pruning for no-history methods",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_chainId",
},
expectFound: true,
expectRoute: pruningBackend,
},
{
// this is just another example of the above, but worth pointing out!
name: "routes to pruning when sending txs",
url: "//archive.kava.io",
req: &decode.EVMRPCRequestEnvelope{
Method: "eth_sendTransaction",
Params: []interface{}{
map[string]string{
"from": "0xdeadbeef00000000000000000000000000000123",
"to": "0xbaddad0000000000000000000000000000000123",
"value": "0x1",
"gas": "0xeeee",
"gasPrice": "0x12345678900",
"nonce": "0x0",
},
},
},
expectFound: true,
expectRoute: pruningBackend,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := mockJsonRpcReqToUrl(tc.url, tc.req)
proxy, found := proxies.ProxyForRequest(req)
if !tc.expectFound {
require.False(t, found, "expected proxy not to be found")
return
}
require.True(t, found, "expected proxy to be found")
require.NotNil(t, proxy)
requireProxyRoutesToUrl(t, proxy, req, tc.expectRoute)
})
}
}

func mockReqForUrl(reqUrl string) *http.Request {
parsed, err := url.Parse(reqUrl)
if err != nil {
Expand All @@ -71,6 +225,16 @@ func mockReqForUrl(reqUrl string) *http.Request {
return &http.Request{Host: parsed.Host, URL: parsed}
}

func mockJsonRpcReqToUrl(url string, evmReq *decode.EVMRPCRequestEnvelope) *http.Request {
req := mockReqForUrl(url)
// add the request into the request context, mocking previous middleware parsing
ctx := context.Background()
if evmReq != nil {
ctx = context.WithValue(ctx, service.DecodedRequestContextKey, evmReq)
}
return req.WithContext(ctx)
}

// requireProxyRoutesToUrl is a test helper that verifies that
// the given proxy maps the provided request to the expected proxy backend
// relies on the fact that reverse proxies are given a Director that rewrite the request's URL
Expand Down
41 changes: 24 additions & 17 deletions service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,38 @@ import (
)

var (
testDefaultContext = context.TODO()
proxyServiceURLMapRaw = os.Getenv("TEST_PROXY_BACKEND_HOST_URL_MAP")
databaseName = os.Getenv("DATABASE_NAME")
databaseUsername = os.Getenv("DATABASE_USERNAME")
databasePassword = os.Getenv("DATABASE_PASSWORD")
databaseEndpointURL = os.Getenv("DATABASE_ENDPOINT_URL")
testServiceLogLevel = os.Getenv("TEST_SERVICE_LOG_LEVEL")
evmQueryServiceURL = os.Getenv("TEST_EVM_QUERY_SERVICE_URL")
testDefaultContext = context.TODO()
proxyServiceDefaultURLMapRaw = os.Getenv("TEST_PROXY_BACKEND_HOST_URL_MAP")
proxyServicePruningURLMapRaw = os.Getenv("TEST_PROXY_PRUNING_BACKEND_HOST_URL_MAP")
databaseName = os.Getenv("DATABASE_NAME")
databaseUsername = os.Getenv("DATABASE_USERNAME")
databasePassword = os.Getenv("DATABASE_PASSWORD")
databaseEndpointURL = os.Getenv("DATABASE_ENDPOINT_URL")
testServiceLogLevel = os.Getenv("TEST_SERVICE_LOG_LEVEL")
evmQueryServiceURL = os.Getenv("TEST_EVM_QUERY_SERVICE_URL")

dummyConfig = func() config.Config {

proxyBackendHostURLMapParsed, err := config.ParseRawProxyBackendHostURLMap(proxyServiceURLMapRaw)

proxyBackendHostURLMapParsed, err := config.ParseRawProxyBackendHostURLMap(proxyServiceDefaultURLMapRaw)
if err != nil {
panic(err)
}
proxyPruningBackendHostURLMapParsed, err := config.ParseRawProxyBackendHostURLMap(proxyServicePruningURLMapRaw)
if err != nil {
panic(err)
}

conf := config.Config{
ProxyBackendHostURLMapRaw: proxyServiceURLMapRaw,
ProxyBackendHostURLMapParsed: proxyBackendHostURLMapParsed,
DatabaseName: databaseName,
DatabaseUserName: databaseUsername,
DatabasePassword: databasePassword,
DatabaseEndpointURL: databaseEndpointURL,
EvmQueryServiceURL: evmQueryServiceURL,
ProxyBackendHostURLMapRaw: proxyServiceDefaultURLMapRaw,
ProxyBackendHostURLMapParsed: proxyBackendHostURLMapParsed,
ProxyPruningBackendHostURLMapRaw: proxyServicePruningURLMapRaw,
ProxyPruningBackendHostURLMap: proxyPruningBackendHostURLMapParsed,

DatabaseName: databaseName,
DatabaseUserName: databaseUsername,
DatabasePassword: databasePassword,
DatabaseEndpointURL: databaseEndpointURL,
EvmQueryServiceURL: evmQueryServiceURL,
}

return conf
Expand Down

0 comments on commit f04687a

Please sign in to comment.