2021-06-18 18:45:29 +00:00
|
|
|
package kit
|
2021-06-14 10:07:50 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/multiformats/go-multiaddr"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
lcli "github.com/urfave/cli/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
type MockCLI struct {
|
|
|
|
t *testing.T
|
|
|
|
cmds []*lcli.Command
|
|
|
|
cctx *lcli.Context
|
|
|
|
out *bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewMockCLI(ctx context.Context, t *testing.T, cmds []*lcli.Command) *MockCLI {
|
|
|
|
// Create a CLI App with an --api-url flag so that we can specify which node
|
|
|
|
// the command should be executed against
|
|
|
|
app := &lcli.App{
|
|
|
|
Flags: []lcli.Flag{
|
|
|
|
&lcli.StringFlag{
|
|
|
|
Name: "api-url",
|
|
|
|
Hidden: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Commands: cmds,
|
|
|
|
}
|
|
|
|
|
|
|
|
var out bytes.Buffer
|
|
|
|
app.Writer = &out
|
|
|
|
app.Setup()
|
|
|
|
|
|
|
|
cctx := lcli.NewContext(app, &flag.FlagSet{}, nil)
|
|
|
|
cctx.Context = ctx
|
|
|
|
return &MockCLI{t: t, cmds: cmds, cctx: cctx, out: &out}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockCLI) Client(addr multiaddr.Multiaddr) *MockCLIClient {
|
|
|
|
return &MockCLIClient{t: c.t, cmds: c.cmds, addr: addr, cctx: c.cctx, out: c.out}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MockCLIClient runs commands against a particular node
|
|
|
|
type MockCLIClient struct {
|
|
|
|
t *testing.T
|
|
|
|
cmds []*lcli.Command
|
|
|
|
addr multiaddr.Multiaddr
|
|
|
|
cctx *lcli.Context
|
|
|
|
out *bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockCLIClient) RunCmd(input ...string) string {
|
|
|
|
out, err := c.RunCmdRaw(input...)
|
|
|
|
require.NoError(c.t, err, "output:\n%s", out)
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given an input, find the corresponding command or sub-command.
|
|
|
|
// eg "paych add-funds"
|
|
|
|
func (c *MockCLIClient) cmdByNameSub(input []string) (*lcli.Command, []string) {
|
|
|
|
name := input[0]
|
|
|
|
for _, cmd := range c.cmds {
|
|
|
|
if cmd.Name == name {
|
|
|
|
return c.findSubcommand(cmd, input[1:])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, []string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockCLIClient) findSubcommand(cmd *lcli.Command, input []string) (*lcli.Command, []string) {
|
|
|
|
// If there are no sub-commands, return the current command
|
|
|
|
if len(cmd.Subcommands) == 0 {
|
|
|
|
return cmd, input
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check each sub-command for a match against the name
|
|
|
|
subName := input[0]
|
|
|
|
for _, subCmd := range cmd.Subcommands {
|
|
|
|
if subCmd.Name == subName {
|
|
|
|
// Found a match, recursively search for sub-commands
|
|
|
|
return c.findSubcommand(subCmd, input[1:])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, []string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockCLIClient) RunCmdRaw(input ...string) (string, error) {
|
|
|
|
cmd, input := c.cmdByNameSub(input)
|
|
|
|
if cmd == nil {
|
|
|
|
panic("Could not find command " + input[0] + " " + input[1])
|
|
|
|
}
|
|
|
|
|
|
|
|
// prepend --api-url=<node api listener address>
|
|
|
|
apiFlag := "--api-url=" + c.addr.String()
|
|
|
|
input = append([]string{apiFlag}, input...)
|
|
|
|
|
|
|
|
fs := c.flagSet(cmd)
|
|
|
|
err := fs.Parse(input)
|
|
|
|
require.NoError(c.t, err)
|
|
|
|
|
|
|
|
err = cmd.Action(lcli.NewContext(c.cctx.App, fs, c.cctx))
|
|
|
|
|
|
|
|
// Get the output
|
|
|
|
str := strings.TrimSpace(c.out.String())
|
|
|
|
c.out.Reset()
|
|
|
|
return str, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockCLIClient) flagSet(cmd *lcli.Command) *flag.FlagSet {
|
|
|
|
// Apply app level flags (so we can process --api-url flag)
|
|
|
|
fs := &flag.FlagSet{}
|
|
|
|
for _, f := range c.cctx.App.Flags {
|
|
|
|
err := f.Apply(fs)
|
|
|
|
if err != nil {
|
|
|
|
c.t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Apply command level flags
|
|
|
|
for _, f := range cmd.Flags {
|
|
|
|
err := f.Apply(fs)
|
|
|
|
if err != nil {
|
|
|
|
c.t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockCLIClient) RunInteractiveCmd(cmd []string, interactive []string) string {
|
|
|
|
c.toStdin(strings.Join(interactive, "\n") + "\n")
|
|
|
|
return c.RunCmd(cmd...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockCLIClient) toStdin(s string) {
|
|
|
|
c.cctx.App.Metadata["stdin"] = bytes.NewBufferString(s)
|
|
|
|
}
|