mirror of
https://github.com/smallstep/cli.git
synced 2025-04-19 10:42:15 +03:00
151 lines
3.9 KiB
Go
151 lines
3.9 KiB
Go
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/ThomasRooney/gexpect"
|
|
"github.com/smallstep/assert"
|
|
)
|
|
|
|
// CleanOutput returns the output from the cursor character.
|
|
func CleanOutput(str string) string {
|
|
if i := strings.Index(str, "?25h"); i > 0 {
|
|
return str[i+4:]
|
|
}
|
|
return str
|
|
}
|
|
|
|
// Command executes a shell command.
|
|
func Command(command string) *exec.Cmd {
|
|
return exec.Command("sh", "-c", command)
|
|
}
|
|
|
|
// ExitError converts an error to an exec.ExitError.
|
|
func ExitError(err error) (*exec.ExitError, bool) {
|
|
var ee *exec.ExitError
|
|
if errors.As(err, &ee) {
|
|
return ee, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// Output executes a shell command and returns output from stdout.
|
|
func Output(command string) ([]byte, error) {
|
|
return Command(command).Output()
|
|
}
|
|
|
|
// CombinedOutput executes a shell command and returns combined output from
|
|
// stdout and stderr.
|
|
func CombinedOutput(command string) ([]byte, error) {
|
|
return Command(command).CombinedOutput()
|
|
}
|
|
|
|
// WithStdin executes a shell command with a provided reader used for stdin.
|
|
func WithStdin(command string, r io.Reader) ([]byte, error) {
|
|
cmd := Command(command)
|
|
cmd.Stdin = r
|
|
return cmd.Output()
|
|
}
|
|
|
|
// CLICommand represents a command-line command to execute.
|
|
type CLICommand struct {
|
|
command string
|
|
arguments string
|
|
flags map[string]string
|
|
stdin io.Reader
|
|
}
|
|
|
|
// CLIOutput represents the output from executing a CLICommand.
|
|
type CLIOutput struct {
|
|
//nolint:unused // ignore unused field
|
|
stdout, stderr, combined string
|
|
}
|
|
|
|
// NewCLICommand generates a new CLICommand.
|
|
func NewCLICommand() CLICommand {
|
|
return CLICommand{"", "", make(map[string]string), nil}
|
|
}
|
|
|
|
func (c CLICommand) setFlag(flag, value string) CLICommand {
|
|
flags := make(map[string]string)
|
|
for k, v := range c.flags {
|
|
flags[k] = v
|
|
}
|
|
flags[flag] = value
|
|
return CLICommand{c.command, c.arguments, flags, c.stdin}
|
|
}
|
|
|
|
func (c CLICommand) setCommand(command string) CLICommand {
|
|
return CLICommand{command, c.arguments, c.flags, c.stdin}
|
|
}
|
|
|
|
func (c CLICommand) setArguments(arguments string) CLICommand {
|
|
return CLICommand{c.command, arguments, c.flags, c.stdin}
|
|
}
|
|
|
|
func (c CLICommand) setStdin(stdin string) CLICommand {
|
|
return CLICommand{c.command, c.arguments, c.flags, strings.NewReader(stdin)}
|
|
}
|
|
|
|
func (c CLICommand) cmd() string {
|
|
flags := ""
|
|
for key, value := range c.flags {
|
|
if strings.Contains(value, " ") {
|
|
value = "\"" + value + "\""
|
|
}
|
|
flags += fmt.Sprintf("--%s %s ", key, value)
|
|
}
|
|
return fmt.Sprintf("%s %s %s", c.command, c.arguments, flags)
|
|
}
|
|
|
|
func (c CLICommand) run() (CLIOutput, error) {
|
|
var stdout, stderr, combined bytes.Buffer
|
|
cmd := Command(c.cmd())
|
|
cmd.Stdout = io.MultiWriter(&stdout, &combined)
|
|
cmd.Stderr = io.MultiWriter(&stderr, &combined)
|
|
cmd.Stdin = c.stdin
|
|
err := cmd.Run()
|
|
return CLIOutput{stdout.String(), stderr.String(), combined.String()}, err
|
|
}
|
|
|
|
func (c CLICommand) spawn() (*gexpect.ExpectSubprocess, error) {
|
|
return gexpect.Spawn(c.cmd())
|
|
}
|
|
|
|
func (c CLICommand) test(t *testing.T, name, expected string, msg ...interface{}) {
|
|
t.Run(name, func(t *testing.T) {
|
|
out, err := c.run()
|
|
assert.FatalError(t, err, fmt.Sprintf("`%s`: returned error '%s'\n\nOutput:\n%s", c.cmd(), err, out.combined))
|
|
assert.Equals(t, out.combined, expected, msg...)
|
|
})
|
|
}
|
|
|
|
func (c CLICommand) fail(t *testing.T, name string, expected interface{}, msg ...interface{}) {
|
|
_ = msg
|
|
t.Run(name, func(t *testing.T) {
|
|
out, err := c.run()
|
|
if assert.NotNil(t, err) {
|
|
assert.Equals(t, err.Error(), "exit status 1", msg...)
|
|
}
|
|
switch v := expected.(type) {
|
|
case string:
|
|
assert.Equals(t, expected, out.stderr, msg...)
|
|
case *regexp.Regexp:
|
|
re := expected.(*regexp.Regexp)
|
|
if !re.MatchString(out.stderr) {
|
|
t.Errorf("Error message did not match regex:\n Regex: %s\n\n Output:\n%s", re.String(), out.stderr)
|
|
}
|
|
default:
|
|
t.Errorf("unexpected type %T", v)
|
|
}
|
|
assert.Equals(t, "", out.stdout, msg...)
|
|
})
|
|
}
|