mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
429 lines
12 KiB
Go
429 lines
12 KiB
Go
// Copyright (C) 2022, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package subnetcmd
|
|
|
|
import (
|
|
"io"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/luxfi/cli/v2/internal/mocks"
|
|
"github.com/luxfi/cli/v2/pkg/application"
|
|
"github.com/luxfi/cli/v2/pkg/binutils"
|
|
"github.com/luxfi/cli/v2/pkg/config"
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/subnet"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/luxfi/ids"
|
|
luxlog "github.com/luxfi/log"
|
|
"github.com/luxfi/node/v2/v2/version"
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
testSubnet = "testSubnet"
|
|
)
|
|
|
|
func TestInfoKnownVMs(t *testing.T) {
|
|
require := require.New(t)
|
|
vmBinDir := t.TempDir()
|
|
expectedSHA := "a12871fee210fb8619291eaea194581cbd2531e4b23759d225f6806923f63222"
|
|
|
|
type testCase struct {
|
|
strVer string
|
|
repoName string
|
|
vmBinDir string
|
|
vmBin string
|
|
dl binutils.GithubDownloader
|
|
}
|
|
|
|
cases := []testCase{
|
|
{
|
|
strVer: "v0.9.99",
|
|
repoName: "evm",
|
|
vmBinDir: vmBinDir,
|
|
vmBin: "myEVM",
|
|
dl: binutils.NewEVMDownloader(),
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
binDir := filepath.Join(vmBinDir, c.repoName+"-"+c.strVer)
|
|
err := os.MkdirAll(binDir, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
err = os.WriteFile(filepath.Join(binDir, c.vmBin), []byte{0x1, 0x2}, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
maintrs, ver, resurl, sha, err := getInfoForKnownVMs(
|
|
c.strVer,
|
|
c.repoName,
|
|
c.vmBinDir,
|
|
c.vmBin,
|
|
c.dl,
|
|
)
|
|
require.NoError(err)
|
|
require.ElementsMatch([]string{constants.LuxMaintainers}, maintrs)
|
|
require.NoError(err)
|
|
_, err = url.Parse(resurl)
|
|
require.NoError(err)
|
|
// it's kinda useless to create the URL by building it via downloader -
|
|
// would defeat the purpose of the test
|
|
expectedURL := "https://github.com/luxfi/" +
|
|
c.repoName + "/releases/download/" +
|
|
c.strVer + "/" + c.repoName + "_" + c.strVer[1:] + "_" +
|
|
runtime.GOOS + "_" + runtime.GOARCH + ".tar.gz"
|
|
require.Equal(expectedURL, resurl)
|
|
require.Equal(&version.Semantic{
|
|
Major: 0,
|
|
Minor: 9,
|
|
Patch: 99,
|
|
}, ver)
|
|
require.Equal(expectedSHA, sha)
|
|
}
|
|
}
|
|
|
|
func TestNoRepoPath(t *testing.T) {
|
|
require, mockPrompt := setupTestEnv(t)
|
|
defer func() {
|
|
app = nil
|
|
noRepoPath = ""
|
|
forceWrite = false
|
|
}()
|
|
|
|
configureMockPrompt(mockPrompt)
|
|
|
|
sc := &models.Sidecar{
|
|
VM: models.EVM,
|
|
VMVersion: "v0.9.99",
|
|
Name: testSubnet,
|
|
Subnet: testSubnet,
|
|
Networks: map[string]models.NetworkData{
|
|
models.Testnet.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
},
|
|
}
|
|
|
|
// first try with an impossible file
|
|
noRepoPath = "/path/to/nowhere"
|
|
err := doPublish(sc, testSubnet, newTestPublisher)
|
|
// should fail as it can't create that dir
|
|
require.Error(err)
|
|
require.ErrorContains(err, "failed")
|
|
|
|
// try with existing files
|
|
noRepoPath = t.TempDir()
|
|
subnetDir := filepath.Join(noRepoPath, constants.SubnetDir)
|
|
vmDir := filepath.Join(noRepoPath, constants.VMDir)
|
|
err = os.MkdirAll(subnetDir, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
err = os.MkdirAll(vmDir, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
expectedSubnetFile := filepath.Join(subnetDir, testSubnet+constants.YAMLSuffix)
|
|
expectedVMFile := filepath.Join(vmDir, sc.Networks["Testnet"].BlockchainID.String()+constants.YAMLSuffix)
|
|
_, err = os.Create(expectedSubnetFile)
|
|
require.NoError(err)
|
|
|
|
// For Sha256 calc we are accessing the evm binary
|
|
// So we're just `touch`ing that file so the code finds it
|
|
appSubnetDir := filepath.Join(app.GetEVMBinDir(), constants.EVMRepoName+"-"+sc.VMVersion)
|
|
err = os.MkdirAll(appSubnetDir, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
_, err = os.Create(filepath.Join(appSubnetDir, constants.EVMBin))
|
|
require.NoError(err)
|
|
|
|
// reset expectations as this test (and TestPublishing) also uses the same mocks
|
|
// and the same sequence so expectations get messed up
|
|
mockPrompt.Calls = nil
|
|
mockPrompt.ExpectedCalls = nil
|
|
configureMockPrompt(mockPrompt)
|
|
|
|
// should fail as no force flag
|
|
err = doPublish(sc, testSubnet, newTestPublisher)
|
|
require.Error(err)
|
|
require.ErrorContains(err, "already exists")
|
|
err = os.Remove(expectedSubnetFile)
|
|
require.NoError(err)
|
|
_, err = os.Create(expectedVMFile)
|
|
require.NoError(err)
|
|
|
|
// next should fail as no force flag (other file)
|
|
|
|
// reset expectations as this test (and TestPublishing) also uses the same mocks
|
|
// and the same sequence so expectations get messed up
|
|
mockPrompt.Calls = nil
|
|
mockPrompt.ExpectedCalls = nil
|
|
configureMockPrompt(mockPrompt)
|
|
|
|
err = doPublish(sc, testSubnet, newTestPublisher)
|
|
require.Error(err)
|
|
require.ErrorContains(err, "already exists")
|
|
err = os.Remove(expectedVMFile)
|
|
require.NoError(err)
|
|
|
|
// this now should succeed and the file exist
|
|
|
|
// reset expectations as this test (and TestPublishing) also uses the same mocks
|
|
// and the same sequence so expectations get messed up
|
|
mockPrompt.Calls = nil
|
|
mockPrompt.ExpectedCalls = nil
|
|
configureMockPrompt(mockPrompt)
|
|
|
|
err = doPublish(sc, testSubnet, newTestPublisher)
|
|
require.NoError(err)
|
|
require.FileExists(expectedSubnetFile)
|
|
require.FileExists(expectedVMFile)
|
|
|
|
// set force flag
|
|
forceWrite = true
|
|
|
|
// should also succeed and the file exist
|
|
|
|
// reset expectations as this test (and TestPublishing) also uses the same mocks
|
|
// and the same sequence so expectations get messed up
|
|
mockPrompt.Calls = nil
|
|
mockPrompt.ExpectedCalls = nil
|
|
configureMockPrompt(mockPrompt)
|
|
|
|
err = doPublish(sc, testSubnet, newTestPublisher)
|
|
require.NoError(err)
|
|
require.FileExists(expectedSubnetFile)
|
|
require.FileExists(expectedVMFile)
|
|
|
|
// reset expectations as TestPublishing also uses the same mocks
|
|
// but those are global so expectations get messed up
|
|
mockPrompt.Calls = nil
|
|
mockPrompt.ExpectedCalls = nil
|
|
}
|
|
|
|
func TestCanPublish(t *testing.T) {
|
|
require, _ := setupTestEnv(t)
|
|
defer func() {
|
|
app = nil
|
|
}()
|
|
|
|
scCanPublishTestnet := &models.Sidecar{
|
|
VM: models.EVM,
|
|
Name: "testnet",
|
|
Subnet: "testnet",
|
|
Networks: map[string]models.NetworkData{
|
|
models.Testnet.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
},
|
|
}
|
|
|
|
scCanPublishMain := &models.Sidecar{
|
|
VM: models.EVM,
|
|
Name: "main",
|
|
Subnet: "main",
|
|
Networks: map[string]models.NetworkData{
|
|
models.Mainnet.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
},
|
|
}
|
|
|
|
scCanPublishBoth := &models.Sidecar{
|
|
VM: models.EVM,
|
|
Name: "both",
|
|
Subnet: "both",
|
|
Networks: map[string]models.NetworkData{
|
|
models.Testnet.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
models.Mainnet.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
},
|
|
}
|
|
|
|
scCanNotPublishLocal := &models.Sidecar{
|
|
VM: models.EVM,
|
|
Name: "local",
|
|
Subnet: "local",
|
|
Networks: map[string]models.NetworkData{
|
|
models.Local.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
},
|
|
}
|
|
|
|
scCanNotPublishUndefined := &models.Sidecar{
|
|
VM: models.EVM,
|
|
Name: "undefined",
|
|
Subnet: "undefined",
|
|
Networks: map[string]models.NetworkData{
|
|
models.Undefined.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
},
|
|
}
|
|
|
|
scCanNotPublishBothInvalid := &models.Sidecar{
|
|
VM: models.EVM,
|
|
Name: "bothInvalid",
|
|
Subnet: "bothInvalid",
|
|
Networks: map[string]models.NetworkData{
|
|
models.Undefined.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
models.Local.String(): {
|
|
SubnetID: ids.GenerateTestID(),
|
|
BlockchainID: ids.GenerateTestID(),
|
|
},
|
|
},
|
|
}
|
|
|
|
sidecars := []*models.Sidecar{
|
|
scCanPublishTestnet,
|
|
scCanPublishMain,
|
|
scCanPublishBoth,
|
|
scCanNotPublishLocal,
|
|
scCanNotPublishUndefined,
|
|
scCanNotPublishBothInvalid,
|
|
}
|
|
|
|
for i, sc := range sidecars {
|
|
ready := isReadyToPublish(sc)
|
|
if i < 3 {
|
|
require.True(ready)
|
|
} else {
|
|
require.False(ready)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsPublished(t *testing.T) {
|
|
require, _ := setupTestEnv(t)
|
|
defer func() {
|
|
app = nil
|
|
}()
|
|
|
|
published, err := isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.False(published)
|
|
|
|
baseDir := app.GetBaseDir()
|
|
err = os.Mkdir(filepath.Join(baseDir, testSubnet), constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
published, err = isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.False(published)
|
|
|
|
reposDir := app.GetReposDir()
|
|
err = os.MkdirAll(filepath.Join(reposDir, "dummyRepo", constants.VMDir, testSubnet), constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
published, err = isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.False(published)
|
|
|
|
goodDir1 := filepath.Join(reposDir, "dummyRepo", constants.SubnetDir, testSubnet)
|
|
err = os.MkdirAll(goodDir1, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
published, err = isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.False(published)
|
|
|
|
_, err = os.Create(filepath.Join(goodDir1, testSubnet))
|
|
require.NoError(err)
|
|
published, err = isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.True(published)
|
|
|
|
goodDir2 := filepath.Join(reposDir, "dummyRepo2", constants.SubnetDir, testSubnet)
|
|
err = os.MkdirAll(goodDir2, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
published, err = isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.True(published)
|
|
_, err = os.Create(filepath.Join(goodDir2, "myOtherTestSubnet"))
|
|
require.NoError(err)
|
|
published, err = isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.True(published)
|
|
|
|
_, err = os.Create(filepath.Join(goodDir2, testSubnet))
|
|
require.NoError(err)
|
|
published, err = isAlreadyPublished(testSubnet)
|
|
require.NoError(err)
|
|
require.True(published)
|
|
}
|
|
|
|
// TestPublishing allows unit testing of the **normal** flow for publishing
|
|
func TestPublishing(t *testing.T) {
|
|
require, mockPrompt := setupTestEnv(t)
|
|
defer func() {
|
|
app = nil
|
|
}()
|
|
|
|
configureMockPrompt(mockPrompt)
|
|
|
|
sc := &models.Sidecar{
|
|
VM: models.EVM,
|
|
VMVersion: "v0.9.99",
|
|
}
|
|
// For Sha256 calc we are accessing the evm binary
|
|
// So we're just `touch`ing that file so the code finds it
|
|
subnetDir := filepath.Join(app.GetEVMBinDir(), constants.EVMRepoName+"-"+sc.VMVersion)
|
|
err := os.MkdirAll(subnetDir, constants.DefaultPerms755)
|
|
require.NoError(err)
|
|
_, err = os.Create(filepath.Join(subnetDir, constants.EVMBin))
|
|
require.NoError(err)
|
|
|
|
err = doPublish(sc, testSubnet, newTestPublisher)
|
|
require.NoError(err)
|
|
|
|
// reset expectations as TestNoRepoPath also uses the same mocks
|
|
// but those are global so expectations get messed up
|
|
mockPrompt.Calls = nil
|
|
mockPrompt.ExpectedCalls = nil
|
|
}
|
|
|
|
func configureMockPrompt(mockPrompt *mocks.Prompter) {
|
|
mockPrompt.On("CaptureList", mock.Anything, mock.Anything).Return("Add", nil).Once()
|
|
mockPrompt.On("CaptureEmail", mock.Anything).Return("[email protected]", nil)
|
|
mockPrompt.On("CaptureList", mock.Anything, mock.Anything).Return("Done", nil).Once()
|
|
// capture string for a repo alias...
|
|
mockPrompt.On("CaptureString", mock.Anything).Return("testAlias", nil).Once()
|
|
// then the repo URL...
|
|
mockPrompt.On("CaptureString", mock.Anything).Return("https://localhost:12345", nil).Once()
|
|
// always provide an irrelevant response when empty is allowed...
|
|
mockPrompt.On("CaptureStringAllowEmpty", mock.Anything).Return("irrelevant", nil)
|
|
// finally return a semantic version
|
|
mockPrompt.On("CaptureVersion", mock.Anything).Return("v0.9.99", nil)
|
|
}
|
|
|
|
func setupTestEnv(t *testing.T) (*require.Assertions, *mocks.Prompter) {
|
|
require := require.New(t)
|
|
testDir := t.TempDir()
|
|
err := os.Mkdir(filepath.Join(testDir, "repos"), 0o755)
|
|
require.NoError(err)
|
|
ux.NewUserLog(luxlog.NewNoOpLogger(), io.Discard)
|
|
app = &application.Lux{}
|
|
mockPrompt := mocks.NewPrompter(t)
|
|
app.Setup(testDir, luxlog.NewNoOpLogger(), config.New(), mockPrompt, application.NewDownloader())
|
|
|
|
return require, mockPrompt
|
|
}
|
|
|
|
func newTestPublisher(string, string, string) subnet.Publisher {
|
|
mockPub := &mocks.Publisher{}
|
|
mockPub.On("GetRepo").Return(&git.Repository{}, nil)
|
|
mockPub.On("Publish", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
return mockPub
|
|
}
|