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 }