diff --git a/cli/app/deploy.go b/cli/app/deploy.go index 9bf3828e..88a01c1d 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -2,6 +2,7 @@ package app import ( "context" + "errors" "fmt" "coopcloud.tech/abra/cli/internal" @@ -46,14 +47,12 @@ operations.`, app := internal.ValidateApp(cmd) stackName := app.StackName() - specificVersion := cmd.Args().Get(1) - if specificVersion == "" { - specificVersion = app.Recipe.Version + ok, err := validateChaosXORVersion(cmd.Args()) + if !ok { + log.Fatalf(err.Error()) } - if specificVersion != "" && internal.Chaos { - log.Fatal("cannot use and --chaos together") - } + specificVersion := getSpecifiedVersion(cmd.Args()) if specificVersion != "" { log.Debugf("overriding env file version (%s) with %s", app.Recipe.Version, specificVersion) @@ -261,3 +260,17 @@ operations.`, return nil }, } + +// This is not really xor since both can be absent +// +// but, I say we let it slide this time! +func validateChaosXORVersion(args cli.Args) (bool, error) { + if getSpecifiedVersion(args) != "" && internal.Chaos { + return false, errors.New("cannot use and --chaos together") + } + return true, nil +} + +func getSpecifiedVersion(args cli.Args) string { + return args.Get(1) +} diff --git a/cli/app/deploy_test.go b/cli/app/deploy_test.go new file mode 100644 index 00000000..307e3285 --- /dev/null +++ b/cli/app/deploy_test.go @@ -0,0 +1,97 @@ +package app + +import ( + "testing" + + "coopcloud.tech/abra/cli/internal" +) + +func TestGetSpecificVersion(t *testing.T) { + tests := []struct { + input mockArgs + expectedOutput string + }{ + // No specified version when command has one or less args + {mockArgs{}, ""}, + {mockArgs{[]string{"arg0"}}, ""}, + // Second in arg (index-1) is the specified result when command has more than 1 args + {mockArgs{[]string{"arg0", "arg1"}}, "arg1"}, + {mockArgs{[]string{"arg0", "arg1", "arg2"}}, "arg1"}, + } + + for _, test := range tests { + if test.expectedOutput != getSpecifiedVersion(&test.input) { + t.Fatalf("result for %s should be %s", test.input, test.expectedOutput) + } + } +} + +func TestValidateChaosXORVersion(t *testing.T) { + tests := []struct { + input mockArgs + isChaos bool + expectedResult bool + }{ + // Chaos = true, Specified Version absent + {mockArgs{}, true, true}, + // Chaos = false, Specified Version absent + {mockArgs{}, false, true}, + // Chaos = true, Specified Version present + {mockArgs{[]string{"arg0", "arg1"}}, true, false}, + // Chaos = false, Specified Version present + {mockArgs{[]string{"arg0", "arg1", "arg2"}}, false, true}, + } + + for _, test := range tests { + internal.Chaos = test.isChaos + res, _ := validateChaosXORVersion(&test.input) + if res != test.expectedResult { + t.Fatalf( + "When args are %s and Chaos mode is %t result needs to be %t", + test.input, + test.isChaos, + test.expectedResult, + ) + } + } +} + +type mockArgs struct { + v []string +} + +func (a *mockArgs) Get(n int) string { + if len(a.v) > n { + return a.v[n] + } + return "" +} + +func (a *mockArgs) First() string { + return a.Get(0) +} + +func (a *mockArgs) Tail() []string { + if a.Len() >= 2 { + tail := a.v[1:] + ret := make([]string, len(tail)) + copy(ret, tail) + return ret + } + + return []string{} +} + +func (a *mockArgs) Len() int { + return len(a.v) +} + +func (a *mockArgs) Present() bool { + return a.Len() != 0 +} + +func (a *mockArgs) Slice() []string { + ret := make([]string, len(a.v)) + copy(ret, a.v) + return ret +}