github.com/clusterize-io/tusk@v0.6.3-0.20211001020217-cfe8a8cd0d4a/appcli/app_test.go (about) 1 package appcli 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "reflect" 11 "syscall" 12 "testing" 13 14 "github.com/clusterize-io/tusk/runner" 15 "github.com/clusterize-io/tusk/ui" 16 "github.com/urfave/cli" 17 ) 18 19 func TestNewFlagApp(t *testing.T) { 20 cfgText := []byte(`options: 21 foo: 22 short: f 23 default: foovalue 24 25 tasks: 26 mytask: 27 run: echo ${foo} 28 `) 29 30 flagApp, err := newMetaApp(cfgText) 31 if err != nil { 32 t.Fatalf( 33 "newFlagApp():\nconfig: `%s`\nunexpected err: %s", 34 string(cfgText), err, 35 ) 36 } 37 38 args := []string{"tusk", "mytask", "--foo", "other"} 39 if err = flagApp.Run(args); err != nil { 40 t.Fatalf( 41 "flagApp.Run():\nconfig: `%s`\nunexpected err: %s", 42 string(cfgText), err, 43 ) 44 } 45 46 command, ok := flagApp.Metadata["command"].(*cli.Command) 47 if !ok { 48 t.Fatalf( 49 "flagApp.Metadata:\nconfig: `%s`\nMetadata command not a *cli.Command: %#v", 50 string(cfgText), flagApp.Metadata["command"], 51 ) 52 } 53 54 commandName := command.Name 55 commandExpected := "mytask" 56 57 if commandExpected != commandName { 58 t.Errorf( 59 "flagApp.Metadata[\"command\"] for args(%s):\n expected: %s\nactual: %s", 60 args, commandExpected, commandName, 61 ) 62 } 63 64 flagsActual, ok := flagApp.Metadata["flagsPassed"].(map[string]string) 65 if !ok { 66 t.Fatalf( 67 "flagApp.Metadata:\nconfig: `%s`\nMetadata flagsPassed not a map: %#v", 68 string(cfgText), flagApp.Metadata["flagsPassed"], 69 ) 70 } 71 72 flagsExpected := map[string]string{ 73 "foo": "other", 74 } 75 76 if !reflect.DeepEqual(flagsExpected, flagsActual) { 77 t.Errorf( 78 "flagApp.Metadata for args(%s):\n expected: %#v\nactual: %#v", 79 args, flagsExpected, flagsActual, 80 ) 81 } 82 } 83 84 func TestNewFlagApp_no_options(t *testing.T) { 85 cfgText := []byte(`tasks: 86 mytask: 87 run: echo foo 88 `) 89 90 flagApp, err := newMetaApp(cfgText) 91 if err != nil { 92 t.Fatalf( 93 "newFlagApp():\nconfig: `%s`\nunexpected err: %s", 94 string(cfgText), err, 95 ) 96 } 97 98 args := []string{"tusk", "mytask"} 99 if err = flagApp.Run(args); err != nil { 100 t.Fatalf( 101 "flagApp.Run():\nconfig: `%s`\nunexpected err: %s", 102 string(cfgText), err, 103 ) 104 } 105 106 command, ok := flagApp.Metadata["command"].(*cli.Command) 107 if !ok { 108 t.Fatalf( 109 "flagApp.Metadata:\nconfig: `%s`\nMetadata command not a *cli.Command: %#v", 110 string(cfgText), flagApp.Metadata["command"], 111 ) 112 } 113 114 commandName := command.Name 115 commandExpected := "mytask" 116 117 if commandExpected != commandName { 118 t.Errorf( 119 "flagApp.Metadata[\"command\"] for args(%s):\n expected: %s\nactual: %s", 120 args, commandExpected, commandName, 121 ) 122 } 123 124 flagsActual, ok := flagApp.Metadata["flagsPassed"].(map[string]string) 125 if !ok { 126 t.Fatalf( 127 "flagApp.Metadata:\nconfig: `%s`\nMetadata flagsPassed not a map: %#v", 128 string(cfgText), flagApp.Metadata["flagsPassed"], 129 ) 130 } 131 132 flagsExpected := map[string]string{} 133 134 if !reflect.DeepEqual(flagsExpected, flagsActual) { 135 t.Errorf( 136 "flagApp.Metadata for args(%s):\n expected: %#v\nactual: %#v", 137 args, flagsExpected, flagsActual, 138 ) 139 } 140 } 141 142 func TestNewApp(t *testing.T) { 143 taskName := "foo" 144 name := "new-name" 145 usage := "new usage" 146 args := []string{"tusk", taskName} 147 148 cfgText := []byte(fmt.Sprintf(` 149 name: %s 150 usage: %s 151 tasks: { %q: {} } 152 `, 153 name, usage, taskName, 154 )) 155 meta := &runner.Metadata{CfgText: cfgText} 156 157 app, err := NewApp(args, meta) 158 if err != nil { 159 t.Errorf("NewApp(): unexpected error: %v", err) 160 } 161 162 if len(app.Commands) != 1 { 163 t.Errorf( 164 "For config: `%s`\nexpected 1 command, got %#v", 165 string(cfgText), app.Commands, 166 ) 167 } 168 169 if name != app.Name { 170 t.Errorf( 171 `NewApp().name => %q, want %q`, 172 app.Name, name, 173 ) 174 } 175 176 if usage != app.Usage { 177 t.Errorf( 178 `NewApp().usage => %q, want %q`, 179 app.Usage, usage, 180 ) 181 } 182 } 183 184 func TestNewApp_exit_code(t *testing.T) { 185 args := []string{"tusk", "foo"} 186 expectedCode := 99 187 cfgText := []byte(` 188 tasks: 189 foo: 190 run: exit 99`) 191 meta := &runner.Metadata{ 192 CfgText: cfgText, 193 Logger: ui.Noop(), 194 } 195 196 app, err := NewApp(args, meta) 197 if err != nil { 198 t.Errorf("NewApp(): unexpected error: %v", err) 199 } 200 201 if len(app.Commands) != 1 { 202 t.Fatalf( 203 "For config: `%s`\nexpected 1 command, got %#v", 204 string(cfgText), app.Commands, 205 ) 206 } 207 208 exitErr, ok := app.Run(args).(*exec.ExitError) 209 if !ok { 210 t.Fatalf("app.Run(%v): expected exit err, got %#v", args, err) 211 } 212 213 if actual := exitErr.Sys().(syscall.WaitStatus).ExitStatus(); actual != expectedCode { 214 t.Fatalf("app.Run(%v): expected error code 99, actual: %d", args, actual) 215 } 216 } 217 218 func TestNewApp_private_task(t *testing.T) { 219 args := []string{"tusk", "public"} 220 expectedCode := 99 221 cfgText := []byte(` 222 tasks: 223 private: 224 private: true 225 run: exit 99 226 public: 227 run: {task: private}`) 228 meta := &runner.Metadata{ 229 CfgText: cfgText, 230 Logger: ui.Noop(), 231 } 232 233 app, err := NewApp(args, meta) 234 if err != nil { 235 t.Errorf("NewApp(): unexpected error: %v", err) 236 } 237 238 if len(app.Commands) != 1 { 239 t.Fatalf( 240 "For config: `%s`\nexpected 1 command, got %#v", 241 string(cfgText), app.Commands, 242 ) 243 } 244 245 // Ensure private task still runs as subtask 246 exitErr, ok := app.Run(args).(*exec.ExitError) 247 if !ok { 248 t.Fatalf("app.Run(%v): expected exit err, got %#v", args, err) 249 } 250 251 if actual := exitErr.Sys().(syscall.WaitStatus).ExitStatus(); actual != expectedCode { 252 t.Fatalf("app.Run(%v): expected error code 99, actual: %d", args, actual) 253 } 254 } 255 256 func TestNewApp_fails_bad_config(t *testing.T) { 257 args := []string{"tusk"} 258 cfgText := []byte(`invalid`) 259 meta := &runner.Metadata{CfgText: cfgText} 260 _, err := NewApp(args, meta) 261 if err == nil { 262 t.Fatal("expected error for invalid config text") 263 } 264 } 265 266 func TestNewApp_fails_bad_flag(t *testing.T) { 267 args := []string{"tusk", "--invalid"} 268 cfgText := []byte{} 269 meta := &runner.Metadata{CfgText: cfgText} 270 _, err := NewApp(args, meta) 271 if err == nil { 272 t.Fatal("expected error for invalid flag") 273 } 274 } 275 276 func TestGetConfigMetadata_defaults(t *testing.T) { 277 args := []string{"tusk"} 278 279 metadata, err := GetConfigMetadata(args) 280 if err != nil { 281 t.Fatalf( 282 "GetConfigMetadata(%s): unexpected err: %s", 283 args, err, 284 ) 285 } 286 287 // The project's tuskfile should be found in the project root. 288 wd, err := os.Getwd() 289 if err != nil { 290 t.Fatalf("os.Getwd(): unexpected err: %s", err) 291 } 292 293 directory := filepath.Dir(wd) 294 if directory != metadata.Directory { 295 t.Errorf( 296 "GetConfigMetadata(%s): expected Directory: %s, actual: %s", 297 args, directory, metadata.Directory, 298 ) 299 } 300 301 if metadata.PrintVersion { 302 t.Errorf( 303 "GetConfigMetadata(%s): expected RunVersion: false, actual: true", 304 args, 305 ) 306 } 307 308 if metadata.Logger.Verbosity != ui.VerbosityLevelNormal { 309 t.Errorf( 310 "GetConfigMetadata(%s): expected: %s, actual: %s", 311 args, 312 ui.VerbosityLevelNormal, 313 metadata.Logger.Verbosity, 314 ) 315 } 316 } 317 318 func TestGetConfigMetadata_file(t *testing.T) { 319 cfgPath := "testdata/example.yml" 320 args := []string{"tusk", "--file", cfgPath} 321 322 metadata, err := GetConfigMetadata(args) 323 if err != nil { 324 t.Fatalf( 325 "GetConfigMetadata(%s): unexpected err: %s", 326 args, err, 327 ) 328 } 329 330 directory := "testdata" 331 332 if directory != metadata.Directory { 333 t.Errorf( 334 "GetConfigMetadata(%s): expected Directory: %s, actual: %s", 335 args, directory, metadata.Directory, 336 ) 337 } 338 339 cfgText, err := ioutil.ReadFile(cfgPath) 340 if err != nil { 341 t.Fatalf( 342 "ioutil.ReadFile(%s): unexpected err: %s", 343 cfgPath, err, 344 ) 345 } 346 347 expected := string(cfgText) 348 actual := string(metadata.CfgText) 349 350 if expected != actual { 351 t.Errorf( 352 "GetConfigMetadata(%s):\nexpected config text: %s\nactual: %s", 353 args, expected, actual, 354 ) 355 } 356 } 357 358 func TestGetConfigMetadata_fileNoExist(t *testing.T) { 359 args := []string{"tusk", "--file", "fakefile.yml"} 360 361 _, err := GetConfigMetadata(args) 362 if !errors.Is(err, os.ErrNotExist) { 363 t.Errorf( 364 "GetConfigMetadata(%s): unexpected err: os.IsNotExist, actual: %s", 365 args, err, 366 ) 367 } 368 } 369 370 func TestGetConfigMetadata_version(t *testing.T) { 371 args := []string{"tusk", "--version"} 372 373 metadata, err := GetConfigMetadata(args) 374 if err != nil { 375 t.Fatalf( 376 "GetConfigMetadata(%s):\nunexpected err: %s", 377 args, err, 378 ) 379 } 380 381 if !metadata.PrintVersion { 382 t.Errorf( 383 "GetConfigMetadata(%s): expected RunVersion: true, actual: false", 384 args, 385 ) 386 } 387 } 388 389 var verbosityFlagTests = []struct { 390 args []string 391 expected ui.VerbosityLevel 392 }{ 393 { 394 []string{"tusk"}, 395 ui.VerbosityLevelNormal, 396 }, 397 { 398 []string{"tusk", "--silent"}, 399 ui.VerbosityLevelSilent, 400 }, 401 { 402 []string{"tusk", "--quiet"}, 403 ui.VerbosityLevelQuiet, 404 }, 405 { 406 []string{"tusk", "--verbose"}, 407 ui.VerbosityLevelVerbose, 408 }, 409 { 410 []string{"tusk", "--quiet", "--verbose"}, 411 ui.VerbosityLevelQuiet, 412 }, 413 { 414 []string{"tusk", "--silent", "--quiet"}, 415 ui.VerbosityLevelSilent, 416 }, 417 { 418 []string{"tusk", "--silent", "--verbose"}, 419 ui.VerbosityLevelSilent, 420 }, 421 { 422 []string{"tusk", "--silent", "--quiet", "--verbose"}, 423 ui.VerbosityLevelSilent, 424 }, 425 } 426 427 func TestGetConfigMetadata_verbosity(t *testing.T) { 428 for _, tt := range verbosityFlagTests { 429 metadata, err := GetConfigMetadata(tt.args) 430 if err != nil { 431 t.Errorf( 432 "GetConfigMetadata(%s):\nunexpected err: %s", 433 tt.args, err, 434 ) 435 continue 436 } 437 438 if metadata.Logger.Verbosity != tt.expected { 439 t.Errorf( 440 "GetConfigMetadata(%s): expected %s, actual: %s", 441 tt.args, 442 tt.expected, 443 metadata.Logger.Verbosity, 444 ) 445 } 446 } 447 }