github.com/cosmos/cosmos-sdk@v0.50.10/testutil/cmdtest/system.go (about)

     1  // Package cmdtest contains a framework for testing cobra Commands within Go unit tests.
     2  package cmdtest
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"io"
     8  
     9  	"github.com/spf13/cobra"
    10  )
    11  
    12  // System is a system under test.
    13  type System struct {
    14  	commands []*cobra.Command
    15  }
    16  
    17  // NewSystem returns a new System.
    18  func NewSystem() *System {
    19  	// We aren't doing any special initialization yet,
    20  	// but let's encourage a constructor to make it simpler
    21  	// to update later, if needed.
    22  	return new(System)
    23  }
    24  
    25  // AddCommands sets commands to be available to the Run family of methods on s.
    26  func (s *System) AddCommands(cmds ...*cobra.Command) {
    27  	s.commands = append(s.commands, cmds...)
    28  }
    29  
    30  // RunResult is the stdout and stderr resulting from a call to a System's Run family of methods,
    31  // and any error that was returned.
    32  type RunResult struct {
    33  	Stdout, Stderr bytes.Buffer
    34  
    35  	Err error
    36  }
    37  
    38  // Run calls s.RunC with context.Background().
    39  func (s *System) Run(args ...string) RunResult {
    40  	return s.RunC(context.Background(), args...)
    41  }
    42  
    43  // RunC calls s.RunWithInput with an empty stdin.
    44  func (s *System) RunC(ctx context.Context, args ...string) RunResult {
    45  	return s.RunWithInputC(ctx, bytes.NewReader(nil), args...)
    46  }
    47  
    48  // RunWithInput calls s.RunWithInputC with context.Background().
    49  func (s *System) RunWithInput(in io.Reader, args ...string) RunResult {
    50  	return s.RunWithInputC(context.Background(), in, args...)
    51  }
    52  
    53  // RunWithInputC executes a new root command with subcommands
    54  // that were set in s.AddCommands().
    55  // The command's stdin is set to the in argument.
    56  // RunWithInputC returns a RunResult wrapping stdout, stderr, and any returned error.
    57  func (s *System) RunWithInputC(ctx context.Context, in io.Reader, args ...string) RunResult {
    58  	rootCmd := &cobra.Command{}
    59  	rootCmd.AddCommand(s.commands...)
    60  
    61  	rootCmd.SetIn(in)
    62  
    63  	var res RunResult
    64  	rootCmd.SetOutput(&res.Stdout)
    65  	rootCmd.SetErr(&res.Stderr)
    66  
    67  	rootCmd.SetArgs(args)
    68  
    69  	res.Err = rootCmd.ExecuteContext(ctx)
    70  	return res
    71  }
    72  
    73  // MustRun calls s.Run, but also calls t.FailNow if RunResult.Err is not nil.
    74  func (s *System) MustRun(t TestingT, args ...string) RunResult {
    75  	t.Helper()
    76  
    77  	return s.MustRunC(t, context.Background(), args...)
    78  }
    79  
    80  // MustRunC calls s.RunWithInput, but also calls t.FailNow if RunResult.Err is not nil.
    81  func (s *System) MustRunC(t TestingT, ctx context.Context, args ...string) RunResult { //nolint:revive // As a variation of MustRun, t is more important than ctx.
    82  	t.Helper()
    83  
    84  	return s.MustRunWithInputC(t, ctx, bytes.NewReader(nil), args...)
    85  }
    86  
    87  // MustRunWithInput calls s.RunWithInput, but also calls t.FailNow if RunResult.Err is not nil.
    88  func (s *System) MustRunWithInput(t TestingT, in io.Reader, args ...string) RunResult {
    89  	t.Helper()
    90  
    91  	return s.MustRunWithInputC(t, context.Background(), in, args...)
    92  }
    93  
    94  // MustRunWithInputC calls s.RunWithInputC, but also calls t.FailNow if RunResult.Err is not nil.
    95  func (s *System) MustRunWithInputC(t TestingT, ctx context.Context, in io.Reader, args ...string) RunResult { //nolint:revive // As a variation of MustRun, t is more important than ctx.
    96  	t.Helper()
    97  
    98  	res := s.RunWithInputC(ctx, in, args...)
    99  	if res.Err != nil {
   100  		t.Logf("Error executing %v: %v", args, res.Err)
   101  		t.Logf("Stdout: %q", res.Stdout.String())
   102  		t.Logf("Stderr: %q", res.Stderr.String())
   103  		t.FailNow()
   104  	}
   105  
   106  	return res
   107  }
   108  
   109  // TestingT is a subset of testing.TB,
   110  // containing only what the (*System).Must methods use.
   111  //
   112  // This simplifies using other testing wrappers,
   113  // such as testify suite, etc.
   114  type TestingT interface {
   115  	Helper()
   116  
   117  	Logf(format string, args ...any)
   118  
   119  	FailNow()
   120  }