mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
366 lines
8.8 KiB
Go
366 lines
8.8 KiB
Go
// Copyright (C) 2022, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package utils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/application"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
luxlog "github.com/luxfi/log"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/mod/semver"
|
|
)
|
|
|
|
var _ VersionMapper = &testMapper{}
|
|
|
|
type testContext struct {
|
|
// expected mapping of binaries to their versions
|
|
expected map[string]string
|
|
// fake versions set for the evm binaries, faking github
|
|
sourceEVM string
|
|
// fake versions set for the node binaries, faking github
|
|
sourceLux string
|
|
// should the test fail
|
|
shouldFail bool
|
|
// name of the test
|
|
name string
|
|
}
|
|
|
|
// testMapper is used to bypass github,
|
|
// to test the `GetVersionMapping` function
|
|
// We want to make sure that given a set of
|
|
// versions mocking the structure of github releases API,
|
|
// `GetVersionMapping` is able to correctly evaluate
|
|
// the set of compatible versions for each test.
|
|
type testMapper struct {
|
|
app *application.Lux
|
|
currentContext *testContext
|
|
srv *httptest.Server
|
|
t *testing.T
|
|
}
|
|
|
|
func newTestMapper(t *testing.T) *testMapper {
|
|
app := &application.Lux{
|
|
Downloader: application.NewDownloader(),
|
|
Log: luxlog.NewNoOpLogger(),
|
|
}
|
|
return &testMapper{
|
|
app,
|
|
nil,
|
|
nil,
|
|
t,
|
|
}
|
|
}
|
|
|
|
// implement VersionMapper
|
|
func (*testMapper) GetEligibleVersions(sorted []string, _ string, _ *application.Lux) ([]string, error) {
|
|
// tests were written with the assumption that the first version is always in progress
|
|
return sorted[1:], nil
|
|
}
|
|
|
|
// implement VersionMapper
|
|
func (m *testMapper) GetLatestLuxByProtoVersion(_ *application.Lux, rpcVersion int, _ string) (string, error) {
|
|
cBytes := []byte(m.currentContext.sourceLux)
|
|
|
|
var compat models.LuxCompatiblity
|
|
if err := json.Unmarshal(cBytes, &compat); err != nil {
|
|
return "", err
|
|
}
|
|
vers := compat[strconv.Itoa(rpcVersion)]
|
|
if len(vers) == 0 {
|
|
return "", errors.New("test zero length versions")
|
|
}
|
|
if len(vers) > 1 {
|
|
semver.Sort(vers)
|
|
}
|
|
// take the last
|
|
return vers[len(vers)-1], nil
|
|
}
|
|
|
|
// implement VersionMapper
|
|
// We just set a currentContext for a duration of a single test,
|
|
// so that when the faked github URL is called,
|
|
// it knows what faked versions to return
|
|
func (m *testMapper) getVersionMapping(tc *testContext) (map[string]string, error) {
|
|
binaryToVersion = nil
|
|
// allows to know which test is currently running
|
|
m.currentContext = tc
|
|
return GetVersionMapping(m)
|
|
}
|
|
|
|
// implement VersionMapper
|
|
func (m *testMapper) GetApp() *application.Lux {
|
|
return m.app
|
|
}
|
|
|
|
// GetCompatURL fakes a github endpoint for
|
|
// evm release
|
|
// implement VersionMapper
|
|
func (m *testMapper) GetCompatURL(vmType models.VMType) string {
|
|
switch vmType {
|
|
case models.EVM:
|
|
return m.srv.URL + "/evm"
|
|
default:
|
|
m.t.Fatalf("unexpected vmType: %T", vmType)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetLuxURL fakes a github endpoint for
|
|
// node releases
|
|
// implement VersionMapper
|
|
func (m *testMapper) GetLuxURL() string {
|
|
return m.srv.URL + "/lux"
|
|
}
|
|
|
|
// This is the server function which the local
|
|
// httptest.NewServer() will serve for tests.
|
|
// Therefore, the tests hit this endpoint,
|
|
// and get a faked list of versions (simulating github)
|
|
func (m *testMapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Add("Content-Type", "application/json")
|
|
var err error
|
|
// return the correct faked versions based on the URL
|
|
// which is being requested, returning the faked
|
|
// versions for each binary release endpoint
|
|
switch r.URL.Path {
|
|
case "/evm":
|
|
_, err = w.Write([]byte(m.currentContext.sourceEVM))
|
|
case "/lux":
|
|
_, err = w.Write([]byte(m.currentContext.sourceLux))
|
|
default:
|
|
m.t.Fatalf("Unexpected path URL for test server: %s\n", r.URL.Path)
|
|
}
|
|
if err != nil {
|
|
m.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// TestGetVersionMapping tests that mapping the binaries
|
|
// to versions function (`GetVersionMapping`) returns
|
|
// the expected values.
|
|
// For the test to be meaningful, we start a httptest HTTP
|
|
// server locally, which then returns fake versions for each request
|
|
// (sourceEVM, sourceLux) which then
|
|
// the mapping code in `GetVersionMapping` is expected
|
|
// to correctly evaluate for the global `binaryToVersion` map,
|
|
// used by the tests to know which version to use for which test.
|
|
func TestGetVersionMapping(t *testing.T) {
|
|
require := require.New(t)
|
|
m := newTestMapper(t)
|
|
// start local test HTTP server
|
|
srv := httptest.NewServer(m)
|
|
defer srv.Close()
|
|
m.srv = srv
|
|
|
|
testContexts := []*testContext{
|
|
{
|
|
// This test contains a combination
|
|
// of versions which will be used
|
|
// by `GetVersionMapping` to evaluate versions.
|
|
// The function should be able to correctly
|
|
// evaluate compatible versions, hence
|
|
// `shouldFail` is false
|
|
name: "latest evm match latest lux",
|
|
shouldFail: false,
|
|
expected: map[string]string{
|
|
SoloEVMKey1: "v0.4.2",
|
|
SoloEVMKey2: "v0.4.1",
|
|
SoloLuxKey: "v1.9.1",
|
|
OnlyLuxKey: OnlyLuxValue,
|
|
MultiLux1Key: "v1.9.3",
|
|
MultiLux2Key: "v1.9.2",
|
|
MultiLuxEVMKey: "v0.4.3",
|
|
LatestEVM2LuxKey: "v0.4.3",
|
|
LatestLux2EVMKey: "v1.9.3",
|
|
},
|
|
sourceEVM: `{
|
|
"rpcChainVMProtocolVersion": {
|
|
"v0.4.4": 19,
|
|
"v0.4.3": 19,
|
|
"v0.4.2": 18,
|
|
"v0.4.1": 18,
|
|
"v0.4.0": 17
|
|
}
|
|
}`,
|
|
sourceLux: `{
|
|
"19": [
|
|
"v1.9.2",
|
|
"v1.9.3"
|
|
],
|
|
"18": [
|
|
"v1.9.1"
|
|
],
|
|
"17": [
|
|
"v1.9.0"
|
|
]
|
|
}`,
|
|
},
|
|
{
|
|
// This test does the same, but a different
|
|
// constellation of versions
|
|
name: ">0 major version",
|
|
shouldFail: false,
|
|
expected: map[string]string{
|
|
SoloEVMKey1: "v0.9.9",
|
|
SoloEVMKey2: "v0.9.8",
|
|
SoloLuxKey: "v2.3.4",
|
|
OnlyLuxKey: OnlyLuxValue,
|
|
MultiLux1Key: "v2.3.4",
|
|
MultiLux2Key: "v2.3.3",
|
|
MultiLuxEVMKey: "v0.9.9",
|
|
LatestEVM2LuxKey: "v0.9.9",
|
|
LatestLux2EVMKey: "v2.3.4",
|
|
},
|
|
sourceEVM: `{
|
|
"rpcChainVMProtocolVersion": {
|
|
"v1.0.0": 100,
|
|
"v0.9.9": 99,
|
|
"v0.9.8": 99,
|
|
"v0.4.2": 18,
|
|
"v0.4.1": 18,
|
|
"v0.4.0": 17
|
|
}
|
|
}`,
|
|
sourceLux: `{
|
|
"99": [
|
|
"v2.3.4",
|
|
"v2.3.3"
|
|
],
|
|
"18": [
|
|
"v1.9.1"
|
|
],
|
|
"17": [
|
|
"v1.9.0"
|
|
]
|
|
}`,
|
|
},
|
|
{
|
|
// This test does the same, but a different
|
|
// constellation of versions
|
|
name: "subsecuent evm versions are older",
|
|
shouldFail: false,
|
|
expected: map[string]string{
|
|
SoloEVMKey1: "v0.4.2",
|
|
SoloEVMKey2: "v0.4.1",
|
|
SoloLuxKey: "v2.1.1",
|
|
OnlyLuxKey: OnlyLuxValue,
|
|
MultiLux1Key: "v2.1.1",
|
|
MultiLux2Key: "v2.1.0",
|
|
MultiLuxEVMKey: "v0.4.2",
|
|
LatestEVM2LuxKey: "v0.9.9",
|
|
LatestLux2EVMKey: "v4.3.2",
|
|
},
|
|
sourceEVM: `{
|
|
"rpcChainVMProtocolVersion": {
|
|
"v1.0.0": 100,
|
|
"v0.9.9": 99,
|
|
"v0.5.3": 88,
|
|
"v0.5.2": 87,
|
|
"v0.5.1": 86,
|
|
"v0.4.2": 77,
|
|
"v0.4.1": 77,
|
|
"v0.4.0": 17
|
|
}
|
|
}`,
|
|
sourceLux: `{
|
|
"99": [
|
|
"v4.3.2"
|
|
],
|
|
"88": [
|
|
"v2.3.4"
|
|
],
|
|
"87": [
|
|
"v2.2.2"
|
|
],
|
|
"86": [
|
|
"v2.2.1"
|
|
],
|
|
"77": [
|
|
"v2.1.1",
|
|
"v2.1.0"
|
|
],
|
|
"18": [
|
|
"v1.9.1"
|
|
],
|
|
"17": [
|
|
"v1.9.0"
|
|
]
|
|
}`,
|
|
},
|
|
{
|
|
// this test should fail, simulating that
|
|
// the APIs would return empty releases for some reason
|
|
name: "all-empty responses",
|
|
shouldFail: true,
|
|
expected: map[string]string{},
|
|
sourceEVM: `{}`,
|
|
sourceLux: `{}`,
|
|
},
|
|
{
|
|
// this test should fail, simulating that
|
|
// the APIs would return empty releases for some reason
|
|
// but only got sourceEVM versions
|
|
name: "only evm",
|
|
shouldFail: true,
|
|
expected: map[string]string{},
|
|
sourceLux: `{}`,
|
|
sourceEVM: `{
|
|
"rpcChainVMProtocolVersion": {
|
|
"v1.0.0": 100,
|
|
"v0.9.9": 99,
|
|
"v0.9.8": 99,
|
|
"v0.4.2": 18,
|
|
"v0.4.1": 18,
|
|
"v0.4.0": 17
|
|
}
|
|
}`,
|
|
},
|
|
{
|
|
// this test should fail, simulating that
|
|
// the APIs would return empty releases for some reason
|
|
// but only got sourceLux versions
|
|
name: "only lux",
|
|
shouldFail: true,
|
|
expected: map[string]string{},
|
|
sourceEVM: `{}`,
|
|
sourceLux: `{
|
|
"99": [
|
|
"v2.3.4",
|
|
"v2.3.3"
|
|
],
|
|
"18": [
|
|
"v1.9.1"
|
|
],
|
|
"17": [
|
|
"v1.9.0"
|
|
]
|
|
}`,
|
|
},
|
|
}
|
|
|
|
for i, tc := range testContexts {
|
|
t.Run(tc.name, func(tt *testing.T) {
|
|
// run the function, but use the testMapper,
|
|
// so that we can set the currentContext
|
|
mapping, err := m.getVersionMapping(tc)
|
|
if tc.shouldFail {
|
|
require.Error(err)
|
|
return
|
|
}
|
|
require.NoError(err)
|
|
// make sure the mapping returned from `GetVersionMapping`
|
|
// matches the expected one
|
|
require.Equal(tc.expected, mapping, fmt.Sprintf("iteration: %d", i))
|
|
})
|
|
}
|
|
}
|