...

Source file src/github.com/ory/x/cmdx/helper.go

Documentation: github.com/ory/x/cmdx

     1  package cmdx
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"os"
    12  	"testing"
    13  
    14  	"github.com/spf13/cobra"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/pkg/errors"
    18  
    19  	"github.com/ory/x/logrusx"
    20  )
    21  
    22  var (
    23  	// ErrNilDependency is returned if a dependency is missing.
    24  	ErrNilDependency = errors.New("a dependency was expected to be defined but is nil. Please open an issue with the stack trace")
    25  	// ErrNoPrintButFail is returned to detect a failure state that was already reported to the user in some way
    26  	ErrNoPrintButFail = errors.New("this error should never be printed")
    27  )
    28  
    29  // FailSilently is supposed to be used within a commands RunE function.
    30  // It silences cobras error handling and returns the ErrNoPrintButFail error.
    31  func FailSilently(cmd *cobra.Command) error {
    32  	cmd.SilenceErrors = true
    33  	cmd.SilenceUsage = true
    34  	return errors.WithStack(ErrNoPrintButFail)
    35  }
    36  
    37  // Must fatals with the optional message if err is not nil.
    38  func Must(err error, message string, args ...interface{}) {
    39  	if err == nil {
    40  		return
    41  	}
    42  
    43  	_, _ = fmt.Fprintf(os.Stderr, message+"\n", args...)
    44  	os.Exit(1)
    45  }
    46  
    47  // CheckResponse fatals if err is nil or the response.StatusCode does not match the expectedStatusCode
    48  func CheckResponse(err error, expectedStatusCode int, response *http.Response) {
    49  	Must(err, "Command failed because error occurred: %s", err)
    50  
    51  	if response.StatusCode != expectedStatusCode {
    52  		out, err := ioutil.ReadAll(response.Body)
    53  		if err != nil {
    54  			out = []byte{}
    55  		}
    56  		pretty, err := json.MarshalIndent(json.RawMessage(out), "", "\t")
    57  		if err == nil {
    58  			out = pretty
    59  		}
    60  
    61  		Fatalf(
    62  			`Command failed because status code %d was expected but code %d was received.
    63  
    64  Response payload:
    65  
    66  %s`,
    67  			expectedStatusCode,
    68  			response.StatusCode,
    69  			out,
    70  		)
    71  	}
    72  }
    73  
    74  // FormatResponse takes an object and prints a json.MarshalIdent version of it or fatals.
    75  func FormatResponse(o interface{}) string {
    76  	out, err := json.MarshalIndent(o, "", "\t")
    77  	Must(err, `Command failed because an error occurred while prettifying output: %s`, err)
    78  	return string(out)
    79  }
    80  
    81  // Fatalf prints to os.Stderr and exists with code 1.
    82  func Fatalf(message string, args ...interface{}) {
    83  	if len(args) > 0 {
    84  		_, _ = fmt.Fprintf(os.Stderr, message+"\n", args...)
    85  	} else {
    86  		_, _ = fmt.Fprintln(os.Stderr, message)
    87  	}
    88  	os.Exit(1)
    89  }
    90  
    91  // ExpectDependency expects every dependency to be not nil or it fatals.
    92  func ExpectDependency(logger *logrusx.Logger, dependencies ...interface{}) {
    93  	if logger == nil {
    94  		panic("missing logger for dependency check")
    95  	}
    96  	for _, d := range dependencies {
    97  		if d == nil {
    98  			logger.WithError(errors.WithStack(ErrNilDependency)).Fatalf("A fatal issue occurred.")
    99  		}
   100  	}
   101  }
   102  
   103  // Exec runs the provided cobra command with the given reader as STD_IN and the given args.
   104  // Returns STD_OUT, STD_ERR and the error from the execution.
   105  func Exec(t testing.TB, cmd *cobra.Command, stdIn io.Reader, args ...string) (string, string, error) {
   106  	ctx, cancel := context.WithCancel(context.Background())
   107  	t.Cleanup(cancel)
   108  
   109  	return ExecCtx(ctx, cmd, stdIn, args...)
   110  }
   111  
   112  func ExecCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, args ...string) (string, string, error) {
   113  	stdOut, stdErr := &bytes.Buffer{}, &bytes.Buffer{}
   114  	cmd.SetErr(stdErr)
   115  	cmd.SetOut(stdOut)
   116  	cmd.SetIn(stdIn)
   117  	defer cmd.SetIn(nil)
   118  	if args == nil {
   119  		args = []string{}
   120  	}
   121  	cmd.SetArgs(args)
   122  	err := cmd.ExecuteContext(ctx)
   123  	return stdOut.String(), stdErr.String(), err
   124  }
   125  
   126  // ExecNoErr is a helper that assumes a successful run from Exec.
   127  // Returns STD_OUT.
   128  func ExecNoErr(t testing.TB, cmd *cobra.Command, args ...string) string {
   129  	ctx, cancel := context.WithCancel(context.Background())
   130  	t.Cleanup(cancel)
   131  
   132  	return ExecNoErrCtx(ctx, t, cmd, args...)
   133  }
   134  
   135  func ExecNoErrCtx(ctx context.Context, t require.TestingT, cmd *cobra.Command, args ...string) string {
   136  	stdOut, stdErr, err := ExecCtx(ctx, cmd, nil, args...)
   137  	require.NoError(t, err, "std_out: %s\nstd_err: %s", stdOut, stdErr)
   138  	require.Len(t, stdErr, 0, stdOut)
   139  	return stdOut
   140  }
   141  
   142  // ExecExpectedErr is a helper that assumes a failing run from Exec returning ErrNoPrintButFail
   143  // Returns STD_ERR.
   144  func ExecExpectedErr(t testing.TB, cmd *cobra.Command, args ...string) string {
   145  	ctx, cancel := context.WithCancel(context.Background())
   146  	t.Cleanup(cancel)
   147  
   148  	return ExecExpectedErrCtx(ctx, t, cmd, args...)
   149  }
   150  
   151  func ExecExpectedErrCtx(ctx context.Context, t require.TestingT, cmd *cobra.Command, args ...string) string {
   152  	stdOut, stdErr, err := ExecCtx(ctx, cmd, nil, args...)
   153  	require.True(t, errors.Is(err, ErrNoPrintButFail), "std_out: %s\nstd_err: %s", stdOut, stdErr)
   154  	require.Len(t, stdOut, 0, stdErr)
   155  	return stdErr
   156  }
   157  
   158  type CommandExecuter struct {
   159  	New            func() *cobra.Command
   160  	Ctx            context.Context
   161  	PersistentArgs []string
   162  }
   163  
   164  func (c *CommandExecuter) Exec(stdin io.Reader, args ...string) (string, string, error) {
   165  	return ExecCtx(c.Ctx, c.New(), stdin, append(c.PersistentArgs, args...)...)
   166  }
   167  
   168  func (c *CommandExecuter) ExecNoErr(t require.TestingT, args ...string) string {
   169  	return ExecNoErrCtx(c.Ctx, t, c.New(), append(c.PersistentArgs, args...)...)
   170  }
   171  
   172  func (c *CommandExecuter) ExecExpectedErr(t require.TestingT, args ...string) string {
   173  	return ExecExpectedErrCtx(c.Ctx, t, c.New(), append(c.PersistentArgs, args...)...)
   174  }
   175  

View as plain text