1package cli 2 3import ( 4 "errors" 5 "flag" 6 "fmt" 7 "io/ioutil" 8 "strings" 9 "testing" 10) 11 12func TestCommandFlagParsing(t *testing.T) { 13 cases := []struct { 14 testArgs []string 15 skipFlagParsing bool 16 skipArgReorder bool 17 expectedErr error 18 }{ 19 // Test normal "not ignoring flags" flow 20 {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break")}, 21 22 // Test no arg reorder 23 {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil}, 24 25 {[]string{"test-cmd", "blah", "blah"}, true, false, nil}, // Test SkipFlagParsing without any args that look like flags 26 {[]string{"test-cmd", "blah", "-break"}, true, false, nil}, // Test SkipFlagParsing with random flag arg 27 {[]string{"test-cmd", "blah", "-help"}, true, false, nil}, // Test SkipFlagParsing with "special" help flag arg 28 } 29 30 for _, c := range cases { 31 app := NewApp() 32 app.Writer = ioutil.Discard 33 set := flag.NewFlagSet("test", 0) 34 set.Parse(c.testArgs) 35 36 context := NewContext(app, set, nil) 37 38 command := Command{ 39 Name: "test-cmd", 40 Aliases: []string{"tc"}, 41 Usage: "this is for testing", 42 Description: "testing", 43 Action: func(_ *Context) error { return nil }, 44 SkipFlagParsing: c.skipFlagParsing, 45 SkipArgReorder: c.skipArgReorder, 46 } 47 48 err := command.Run(context) 49 50 expect(t, err, c.expectedErr) 51 expect(t, []string(context.Args()), c.testArgs) 52 } 53} 54 55func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { 56 app := NewApp() 57 app.Commands = []Command{ 58 { 59 Name: "bar", 60 Before: func(c *Context) error { 61 return fmt.Errorf("before error") 62 }, 63 After: func(c *Context) error { 64 return fmt.Errorf("after error") 65 }, 66 }, 67 } 68 69 err := app.Run([]string{"foo", "bar"}) 70 if err == nil { 71 t.Fatalf("expected to receive error from Run, got none") 72 } 73 74 if !strings.Contains(err.Error(), "before error") { 75 t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) 76 } 77 if !strings.Contains(err.Error(), "after error") { 78 t.Errorf("expected text of error from After method, but got none in \"%v\"", err) 79 } 80} 81 82func TestCommand_Run_BeforeSavesMetadata(t *testing.T) { 83 var receivedMsgFromAction string 84 var receivedMsgFromAfter string 85 86 app := NewApp() 87 app.Commands = []Command{ 88 { 89 Name: "bar", 90 Before: func(c *Context) error { 91 c.App.Metadata["msg"] = "hello world" 92 return nil 93 }, 94 Action: func(c *Context) error { 95 msg, ok := c.App.Metadata["msg"] 96 if !ok { 97 return errors.New("msg not found") 98 } 99 receivedMsgFromAction = msg.(string) 100 return nil 101 }, 102 After: func(c *Context) error { 103 msg, ok := c.App.Metadata["msg"] 104 if !ok { 105 return errors.New("msg not found") 106 } 107 receivedMsgFromAfter = msg.(string) 108 return nil 109 }, 110 }, 111 } 112 113 err := app.Run([]string{"foo", "bar"}) 114 if err != nil { 115 t.Fatalf("expected no error from Run, got %s", err) 116 } 117 118 expectedMsg := "hello world" 119 120 if receivedMsgFromAction != expectedMsg { 121 t.Fatalf("expected msg from Action to match. Given: %q\nExpected: %q", 122 receivedMsgFromAction, expectedMsg) 123 } 124 if receivedMsgFromAfter != expectedMsg { 125 t.Fatalf("expected msg from After to match. Given: %q\nExpected: %q", 126 receivedMsgFromAction, expectedMsg) 127 } 128} 129 130func TestCommand_OnUsageError_hasCommandContext(t *testing.T) { 131 app := NewApp() 132 app.Commands = []Command{ 133 { 134 Name: "bar", 135 Flags: []Flag{ 136 IntFlag{Name: "flag"}, 137 }, 138 OnUsageError: func(c *Context, err error, _ bool) error { 139 return fmt.Errorf("intercepted in %s: %s", c.Command.Name, err.Error()) 140 }, 141 }, 142 } 143 144 err := app.Run([]string{"foo", "bar", "--flag=wrong"}) 145 if err == nil { 146 t.Fatalf("expected to receive error from Run, got none") 147 } 148 149 if !strings.HasPrefix(err.Error(), "intercepted in bar") { 150 t.Errorf("Expect an intercepted error, but got \"%v\"", err) 151 } 152} 153 154func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) { 155 app := NewApp() 156 app.Commands = []Command{ 157 { 158 Name: "bar", 159 Flags: []Flag{ 160 IntFlag{Name: "flag"}, 161 }, 162 OnUsageError: func(c *Context, err error, _ bool) error { 163 if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { 164 t.Errorf("Expect an invalid value error, but got \"%v\"", err) 165 } 166 return errors.New("intercepted: " + err.Error()) 167 }, 168 }, 169 } 170 171 err := app.Run([]string{"foo", "bar", "--flag=wrong"}) 172 if err == nil { 173 t.Fatalf("expected to receive error from Run, got none") 174 } 175 176 if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { 177 t.Errorf("Expect an intercepted error, but got \"%v\"", err) 178 } 179} 180 181func TestCommand_OnUsageError_WithSubcommand(t *testing.T) { 182 app := NewApp() 183 app.Commands = []Command{ 184 { 185 Name: "bar", 186 Subcommands: []Command{ 187 { 188 Name: "baz", 189 }, 190 }, 191 Flags: []Flag{ 192 IntFlag{Name: "flag"}, 193 }, 194 OnUsageError: func(c *Context, err error, _ bool) error { 195 if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { 196 t.Errorf("Expect an invalid value error, but got \"%v\"", err) 197 } 198 return errors.New("intercepted: " + err.Error()) 199 }, 200 }, 201 } 202 203 err := app.Run([]string{"foo", "bar", "--flag=wrong"}) 204 if err == nil { 205 t.Fatalf("expected to receive error from Run, got none") 206 } 207 208 if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { 209 t.Errorf("Expect an intercepted error, but got \"%v\"", err) 210 } 211} 212 213func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { 214 app := NewApp() 215 app.ErrWriter = ioutil.Discard 216 app.Commands = []Command{ 217 { 218 Name: "bar", 219 Usage: "this is for testing", 220 Subcommands: []Command{ 221 { 222 Name: "baz", 223 Usage: "this is for testing", 224 Action: func(c *Context) error { 225 if c.App.ErrWriter != ioutil.Discard { 226 return fmt.Errorf("ErrWriter not passed") 227 } 228 229 return nil 230 }, 231 }, 232 }, 233 }, 234 } 235 236 err := app.Run([]string{"foo", "bar", "baz"}) 237 if err != nil { 238 t.Fatal(err) 239 } 240} 241