fix(cli): tighten chain command validation
Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
41f2d52979
commit
d6f31dbe57
4 changed files with 91 additions and 0 deletions
|
|
@ -6,6 +6,8 @@
|
||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
|
@ -75,3 +77,16 @@ func ensureChainDataDirExists(dataDir string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateChainOptions(dataDir, seed string) error {
|
||||||
|
if dataDir == "" {
|
||||||
|
return coreerr.E("validateChainOptions", "data dir is required", nil)
|
||||||
|
}
|
||||||
|
if seed == "" {
|
||||||
|
return coreerr.E("validateChainOptions", "seed is required", nil)
|
||||||
|
}
|
||||||
|
if _, _, err := net.SplitHostPort(seed); err != nil {
|
||||||
|
return coreerr.E("validateChainOptions", fmt.Sprintf("seed %q must be host:port", seed), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,68 @@ func TestAddChainCommands_Good_PersistentFlags(t *testing.T) {
|
||||||
assert.NotNil(t, chainCmd.PersistentFlags().Lookup("seed"))
|
assert.NotNil(t, chainCmd.PersistentFlags().Lookup("seed"))
|
||||||
assert.NotNil(t, chainCmd.PersistentFlags().Lookup("testnet"))
|
assert.NotNil(t, chainCmd.PersistentFlags().Lookup("testnet"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateChainOptions_Good(t *testing.T) {
|
||||||
|
err := validateChainOptions("/tmp/lethean", "seed.example:36942")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateChainOptions_Bad(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
dataDir string
|
||||||
|
seed string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{name: "missing data dir", dataDir: "", seed: "seed.example:36942", want: "data dir is required"},
|
||||||
|
{name: "missing seed", dataDir: "/tmp/lethean", seed: "", want: "seed is required"},
|
||||||
|
{name: "malformed seed", dataDir: "/tmp/lethean", seed: "seed.example", want: "must be host:port"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := validateChainOptions(tt.dataDir, tt.seed)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainSyncCommand_BadMutuallyExclusiveFlags(t *testing.T) {
|
||||||
|
dataDir := t.TempDir()
|
||||||
|
seed := "seed.example:36942"
|
||||||
|
testnet := false
|
||||||
|
|
||||||
|
cmd := newChainSyncCommand(&dataDir, &seed, &testnet)
|
||||||
|
cmd.SetArgs([]string{"--daemon", "--stop"})
|
||||||
|
|
||||||
|
err := cmd.Execute()
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "cannot be combined")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainSyncCommand_BadArgsRejected(t *testing.T) {
|
||||||
|
dataDir := t.TempDir()
|
||||||
|
seed := "seed.example:36942"
|
||||||
|
testnet := false
|
||||||
|
|
||||||
|
cmd := newChainSyncCommand(&dataDir, &seed, &testnet)
|
||||||
|
cmd.SetArgs([]string{"extra"})
|
||||||
|
|
||||||
|
err := cmd.Execute()
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "unknown command")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainExplorerCommand_BadSeedRejected(t *testing.T) {
|
||||||
|
dataDir := t.TempDir()
|
||||||
|
seed := "bad-seed"
|
||||||
|
testnet := false
|
||||||
|
|
||||||
|
cmd := newChainExplorerCommand(&dataDir, &seed, &testnet)
|
||||||
|
cmd.SetArgs(nil)
|
||||||
|
|
||||||
|
err := cmd.Execute()
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "must be host:port")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ func newChainExplorerCommand(dataDir, seed *string, testnet *bool) *cobra.Comman
|
||||||
Use: "explorer",
|
Use: "explorer",
|
||||||
Short: "TUI block explorer",
|
Short: "TUI block explorer",
|
||||||
Long: "Interactive terminal block explorer with live sync status.",
|
Long: "Interactive terminal block explorer with live sync status.",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return validateChainOptions(*dataDir, *seed)
|
||||||
|
},
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runChainExplorer(*dataDir, *seed, *testnet)
|
return runChainExplorer(*dataDir, *seed, *testnet)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,13 @@ func newChainSyncCommand(dataDir, seed *string, testnet *bool) *cobra.Command {
|
||||||
Use: "sync",
|
Use: "sync",
|
||||||
Short: "Headless P2P chain sync",
|
Short: "Headless P2P chain sync",
|
||||||
Long: "Sync the blockchain from P2P peers without the TUI explorer.",
|
Long: "Sync the blockchain from P2P peers without the TUI explorer.",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if daemon && stop {
|
||||||
|
return coreerr.E("newChainSyncCommand", "flags --daemon and --stop cannot be combined", nil)
|
||||||
|
}
|
||||||
|
return validateChainOptions(*dataDir, *seed)
|
||||||
|
},
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if stop {
|
if stop {
|
||||||
return stopChainSyncDaemon(*dataDir)
|
return stopChainSyncDaemon(*dataDir)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue