github.com/clusterize-io/tusk@v0.6.3-0.20211001020217-cfe8a8cd0d4a/runner/option_test.go (about) 1 package runner 2 3 import ( 4 "os" 5 "reflect" 6 "testing" 7 8 "github.com/clusterize-io/tusk/marshal" 9 yaml "gopkg.in/yaml.v2" 10 ) 11 12 func TestOption_Dependencies(t *testing.T) { 13 option := &Option{DefaultValues: ValueList{ 14 {When: WhenList{whenFalse}, Value: "foo"}, 15 {When: WhenList{createWhen( 16 withWhenEqual("foo", "foovalue"), 17 withWhenEqual("bar", "barvalue"), 18 )}, Value: "bar"}, 19 {When: WhenList{createWhen( 20 withWhenNotEqual("baz", "bazvalue"), 21 )}, Value: "bar"}, 22 }} 23 24 expected := []string{"foo", "bar", "baz"} 25 actual := option.Dependencies() 26 if !equalUnordered(expected, actual) { 27 t.Errorf( 28 "Option.Dependencies(): expected %s, actual %s", 29 expected, actual, 30 ) 31 } 32 } 33 34 func equalUnordered(a, b []string) bool { 35 if len(a) != len(b) { 36 return false 37 } 38 39 aMap := make(map[string]interface{}) 40 for _, val := range a { 41 aMap[val] = struct{}{} 42 } 43 44 bMap := make(map[string]interface{}) 45 for _, val := range b { 46 bMap[val] = struct{}{} 47 } 48 49 return reflect.DeepEqual(aMap, bMap) 50 } 51 52 // Env var `OPTION_VAR` will be set to `option_val` 53 var valuetests = []struct { 54 desc string 55 input *Option 56 expected string 57 }{ 58 {"nil", nil, ""}, 59 {"empty option", &Option{}, ""}, 60 { 61 "default only", 62 &Option{DefaultValues: ValueList{ 63 {Value: "default"}, 64 }}, 65 "default", 66 }, 67 { 68 "command only", 69 &Option{DefaultValues: ValueList{ 70 {Command: "echo command"}, 71 }}, 72 "command", 73 }, 74 { 75 "environment variable only", 76 &Option{Environment: "OPTION_VAR"}, 77 "option_val", 78 }, 79 { 80 "passed variable only", 81 &Option{Passed: "passed"}, 82 "passed", 83 }, 84 { 85 "conditional value", 86 &Option{DefaultValues: ValueList{ 87 {When: WhenList{whenFalse}, Value: "foo"}, 88 {When: WhenList{whenTrue}, Value: "bar"}, 89 {When: WhenList{whenFalse}, Value: "baz"}, 90 }}, 91 "bar", 92 }, 93 { 94 "passed when all settings are defined", 95 &Option{ 96 Environment: "OPTION_VAR", 97 DefaultValues: ValueList{ 98 {When: WhenList{whenTrue}, Value: "when"}, 99 }, 100 Passed: "passed", 101 }, 102 "passed", 103 }, 104 } 105 106 func TestOption_Evaluate(t *testing.T) { 107 if err := os.Setenv("OPTION_VAR", "option_val"); err != nil { 108 t.Fatalf("unexpected err setting environment variable: %s", err) 109 } 110 111 for _, tt := range valuetests { 112 actual, err := tt.input.Evaluate(Context{}, nil) 113 if err != nil { 114 t.Errorf( 115 "Option.Evaluate() for %s: unexpected err: %q", 116 tt.desc, err, 117 ) 118 continue 119 } 120 121 if tt.expected != actual { 122 t.Errorf( 123 "Option.Evaluate() for %s: expected %q, actual %q", 124 tt.desc, tt.expected, actual, 125 ) 126 } 127 } 128 } 129 130 func TestOption_Evaluate_required_nothing_passed(t *testing.T) { 131 option := Option{Required: true} 132 133 if _, err := option.Evaluate(Context{}, nil); err == nil { 134 t.Fatal( 135 "Option.Evaluate() for required option: expected err, actual nil", 136 ) 137 } 138 } 139 140 func TestOption_Evaluate_passes_vars(t *testing.T) { 141 expected := "some value" 142 opt := Option{ 143 DefaultValues: ValueList{ 144 {When: WhenList{whenFalse}, Value: "wrong"}, 145 { 146 When: WhenList{createWhen(withWhenEqual("foo", "foovalue"))}, 147 Value: expected, 148 }, 149 {When: WhenList{whenFalse}, Value: "oops"}, 150 }, 151 } 152 153 actual, err := opt.Evaluate(Context{}, map[string]string{"foo": "foovalue"}) 154 if err != nil { 155 t.Fatalf("Option.Evaluate(): unexpected error: %s", err) 156 } 157 158 if expected != actual { 159 t.Errorf( 160 "Option.Evaluate(): expected %q, actual %q", 161 expected, actual, 162 ) 163 } 164 } 165 166 func TestOption_Evaluate_required_with_passed(t *testing.T) { 167 expected := "foo" 168 option := Option{Required: true, Passed: expected} 169 170 actual, err := option.Evaluate(Context{}, nil) 171 if err != nil { 172 t.Fatalf("Option.Evaluate(): unexpected error: %s", err) 173 } 174 175 if expected != actual { 176 t.Errorf( 177 "Option.Evaluate(): expected %q, actual %q", 178 expected, actual, 179 ) 180 } 181 } 182 183 func TestOption_Evaluate_required_with_environment(t *testing.T) { 184 envVar := "OPTION_VAR" 185 expected := "foo" 186 187 option := Option{Required: true, Environment: envVar} 188 if err := os.Setenv(envVar, expected); err != nil { 189 t.Fatalf("unexpected err setting environment variable: %s", err) 190 } 191 192 actual, err := option.Evaluate(Context{}, nil) 193 if err != nil { 194 t.Fatalf("Option.Evaluate(): unexpected error: %s", err) 195 } 196 197 if expected != actual { 198 t.Errorf( 199 "Option.Evaluate(): expected %q, actual %q", 200 expected, actual, 201 ) 202 } 203 } 204 205 func TestOption_Evaluate_values_none_specified(t *testing.T) { 206 expected := "" 207 option := Option{ 208 ValueWithList: ValueWithList{ 209 ValuesAllowed: marshal.StringList{"red", "herring"}, 210 }, 211 } 212 213 actual, err := option.Evaluate(Context{}, nil) 214 if err != nil { 215 t.Fatalf("Option.Evaluate(): unexpected error: %s", err) 216 } 217 218 if expected != actual { 219 t.Errorf( 220 "Option.Evaluate(): expected %q, actual %q", 221 expected, actual, 222 ) 223 } 224 } 225 226 func TestOption_Evaluate_values_with_passed(t *testing.T) { 227 expected := "foo" 228 option := Option{ 229 Passed: expected, 230 ValueWithList: ValueWithList{ 231 ValuesAllowed: marshal.StringList{"red", expected, "herring"}, 232 }, 233 } 234 235 actual, err := option.Evaluate(Context{}, nil) 236 if err != nil { 237 t.Fatalf("Option.Evaluate(): unexpected error: %s", err) 238 } 239 240 if expected != actual { 241 t.Errorf( 242 "Option.Evaluate(): expected %q, actual %q", 243 expected, actual, 244 ) 245 } 246 } 247 248 func TestOption_Evaluate_values_with_environment(t *testing.T) { 249 envVar := "OPTION_VAR" 250 expected := "foo" 251 252 option := Option{ 253 Environment: envVar, 254 ValueWithList: ValueWithList{ 255 ValuesAllowed: marshal.StringList{"red", expected, "herring"}, 256 }, 257 } 258 259 if err := os.Setenv(envVar, expected); err != nil { 260 t.Fatalf("unexpected err setting environment variable: %s", err) 261 } 262 263 actual, err := option.Evaluate(Context{}, nil) 264 if err != nil { 265 t.Fatalf("Option.Evaluate(): unexpected error: %s", err) 266 } 267 268 if expected != actual { 269 t.Errorf( 270 "Option.Evaluate(): expected %q, actual %q", 271 expected, actual, 272 ) 273 } 274 } 275 276 func TestOption_Evaluate_values_with_invalid_passed(t *testing.T) { 277 expected := "foo" 278 option := Option{ 279 Passed: expected, 280 ValueWithList: ValueWithList{ 281 ValuesAllowed: marshal.StringList{"bad", "values", "FOO"}, 282 }, 283 } 284 285 _, err := option.Evaluate(Context{}, nil) 286 if err == nil { 287 t.Fatalf( 288 "Option.Evaluate(): expected error for invalid passed value, got nil", 289 ) 290 } 291 } 292 293 func TestOption_Evaluate_values_with_invalid_environment(t *testing.T) { 294 envVar := "OPTION_VAR" 295 expected := "foo" 296 297 option := Option{ 298 Environment: envVar, 299 ValueWithList: ValueWithList{ 300 ValuesAllowed: marshal.StringList{"bad", "values", "FOO"}, 301 }, 302 } 303 304 if err := os.Setenv(envVar, expected); err != nil { 305 t.Fatalf("unexpected err setting environment variable: %s", err) 306 } 307 308 _, err := option.Evaluate(Context{}, nil) 309 if err == nil { 310 t.Fatalf( 311 "Option.Evaluate(): expected error for invalid environment value, got nil", 312 ) 313 } 314 } 315 316 var evaluteTypeDefaultTests = []struct { 317 typeName string 318 expected string 319 }{ 320 {"int", "0"}, 321 {"INTEGER", "0"}, 322 {"Float", "0"}, 323 {"float64", "0"}, 324 {"double", "0"}, 325 {"bool", "false"}, 326 {"boolean", "false"}, 327 {"", ""}, 328 } 329 330 func TestOption_Evaluate_type_defaults(t *testing.T) { 331 for _, tt := range evaluteTypeDefaultTests { 332 opt := Option{Type: tt.typeName} 333 actual, err := opt.Evaluate(Context{}, nil) 334 if err != nil { 335 t.Errorf("Option.Evaluate(): unexpected error: %s", err) 336 continue 337 } 338 339 if tt.expected != actual { 340 t.Errorf( 341 "Option.Evaluate(): expected %q, actual %q", 342 tt.expected, actual, 343 ) 344 } 345 } 346 } 347 348 func TestOption_UnmarshalYAML(t *testing.T) { 349 s := []byte(`{usage: foo, values: [foo, bar]}`) 350 expected := Option{ 351 Usage: "foo", 352 ValueWithList: ValueWithList{ 353 ValuesAllowed: []string{"foo", "bar"}, 354 }, 355 Name: "", 356 } 357 actual := Option{} 358 359 if err := yaml.UnmarshalStrict(s, &actual); err != nil { 360 t.Fatalf("yaml.UnmarshalStrict(%s, ...): unexpected error: %s", s, err) 361 } 362 363 if !reflect.DeepEqual(expected, actual) { 364 t.Errorf( 365 `yaml.UnmarshalStrict(%s, ...): expected "%#v", actual "%#v"`, 366 s, expected, actual, 367 ) 368 } 369 } 370 371 var unmarshalOptionErrorTests = []struct { 372 desc string 373 input string 374 }{ 375 { 376 "invalid option definition", 377 "string only", 378 }, 379 { 380 "short name exceeds one character", 381 "{short: foo}", 382 }, 383 { 384 "private and required defined", 385 "{private: true, required: true}", 386 }, 387 { 388 "private and environment defined", 389 "{private: true, environment: ENV_VAR}", 390 }, 391 { 392 "private and values defined", 393 "{private: true, values: [foo, bar]}", 394 }, 395 { 396 "required and default defined", 397 "{required: true, default: foo}", 398 }, 399 } 400 401 func TestOption_UnmarshalYAML_invalid_definitions(t *testing.T) { 402 for _, tt := range unmarshalOptionErrorTests { 403 o := Option{} 404 if err := yaml.UnmarshalStrict([]byte(tt.input), &o); err == nil { 405 t.Errorf( 406 "yaml.UnmarshalStrict(%s, ...): expected error for %s, actual nil", 407 tt.input, tt.desc, 408 ) 409 } 410 } 411 } 412 413 func TestGetOptionsWithOrder(t *testing.T) { 414 name := "foo" 415 env := "fooenv" 416 ms := yaml.MapSlice{ 417 {Key: name, Value: &Option{Environment: env}}, 418 {Key: "bar", Value: &Option{Environment: "barenv"}}, 419 } 420 421 options, err := getOptionsWithOrder(ms) 422 if err != nil { 423 t.Fatalf("GetOptionsWithOrder(ms) => unexpected error: %v", err) 424 } 425 426 if len(ms) != len(options) { 427 t.Fatalf( 428 "GetOptionsWithOrder(ms) => want %d items, got %d", 429 len(ms), len(options), 430 ) 431 } 432 433 opt := options[0] 434 435 if name != opt.Name { 436 t.Errorf( 437 "GetOptionsWithOrder(ms) => want opt.Name %q, got %q", 438 name, opt.Name, 439 ) 440 } 441 442 if env != opt.Environment { 443 t.Errorf( 444 "GetOptionsWithOrder(ms) => want opt.Environment %q, got %q", 445 env, opt.Environment, 446 ) 447 } 448 449 if options[1].Name != "bar" { 450 t.Errorf("GetOptionsWithOrder(ms) => want 2nd option %q, got %q", "bar", options[1].Name) 451 } 452 }