diff --git a/docs/cmd/flux_create_helmrelease.md b/docs/cmd/flux_create_helmrelease.md
index 136c365bb00c22f7f50cbfb110251846a5bda909..99915ae6c7695889e3b637404b6315392dfda452 100644
--- a/docs/cmd/flux_create_helmrelease.md
+++ b/docs/cmd/flux_create_helmrelease.md
@@ -76,10 +76,10 @@ flux create helmrelease [name] [flags]
   -h, --help                                help for helmrelease
       --release-name string                 name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'
       --service-account string              the name of the service account to impersonate when reconciling this HelmRelease
-      --source helmChartSource              source that contains the chart in the format '<kind>/<name>',where kind can be one of: (HelmRepository, GitRepository, Bucket)
+      --source helmChartSource              source that contains the chart in the format '<kind>/<name>', where kind must be one of: (HelmRepository, GitRepository, Bucket)
       --target-namespace string             namespace to install this release, defaults to the HelmRelease namespace
       --values string                       local path to the values.yaml file
-      --values-from helmReleaseValuesFrom   Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>',where kind can be one of: (Secret, ConfigMap)
+      --values-from helmReleaseValuesFrom   Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', where kind must be one of: (Secret, ConfigMap)
 ```
 
 ### Options inherited from parent commands
diff --git a/docs/cmd/flux_create_kustomization.md b/docs/cmd/flux_create_kustomization.md
index 804f06dd5a2c7452a585347be69b822bf66bf189..ac4cb60778abf3b6ea4c2fc92df1eaffb08f1d7e 100644
--- a/docs/cmd/flux_create_kustomization.md
+++ b/docs/cmd/flux_create_kustomization.md
@@ -53,7 +53,7 @@ flux create kustomization [name] [flags]
       --path safeRelativePath                    path to the directory containing a kustomization.yaml file (default ./)
       --prune                                    enable garbage collection
       --service-account string                   the name of the service account to impersonate when reconciling this Kustomization
-      --source kustomizationSource               source that contains the Kubernetes manifests in the format '[<kind>/]<name>',where kind can be one of: (GitRepository, Bucket), if kind is not specified it defaults to GitRepository
+      --source kustomizationSource               source that contains the Kubernetes manifests in the format '[<kind>/]<name>', where kind must be one of: (GitRepository, Bucket), if kind is not specified it defaults to GitRepository
       --target-namespace string                  overrides the namespace of all Kustomization objects reconciled by this Kustomization
       --validation string                        validate the manifests before applying them on the cluster, can be 'client' or 'server'
 ```
diff --git a/internal/flags/arch_test.go b/internal/flags/arch_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..9902fff4c06d2c9e0a16ce056750393fda0b5c50
--- /dev/null
+++ b/internal/flags/arch_test.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestArch_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "amd64", "amd64", false},
+		{"unsupported", "unsupported", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var a Arch
+			if err := a.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := a.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/decryption_provider.go b/internal/flags/decryption_provider.go
index c836c8493b2a1d9ec957718a3308a294b984eaef..9a4164673f9bb904755eede7aef909e9f02f7c04 100644
--- a/internal/flags/decryption_provider.go
+++ b/internal/flags/decryption_provider.go
@@ -32,6 +32,10 @@ func (d *DecryptionProvider) String() string {
 }
 
 func (d *DecryptionProvider) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		return fmt.Errorf("no decryption provider given, must be one of: %s",
+			strings.Join(supportedDecryptionProviders, ", "))
+	}
 	if !utils.ContainsItemString(supportedDecryptionProviders, str) {
 		return fmt.Errorf("unsupported decryption provider '%s', must be one of: %s",
 			str, strings.Join(supportedDecryptionProviders, ", "))
diff --git a/internal/flags/decryption_provider_test.go b/internal/flags/decryption_provider_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc1b2f07f8e068b7c630b474f68cf4ec6a021800
--- /dev/null
+++ b/internal/flags/decryption_provider_test.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestDecryptionProvider_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "sops", "sops", false},
+		{"unsupported", "unsupported", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var p DecryptionProvider
+			if err := p.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := p.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/ecdsa_curve.go b/internal/flags/ecdsa_curve.go
index d73924f2acb2bea355e67c753e48ae838806fa17..9be8469bfefcb33a7c24f5d3b83fcfc279ac7b73 100644
--- a/internal/flags/ecdsa_curve.go
+++ b/internal/flags/ecdsa_curve.go
@@ -45,7 +45,7 @@ func (c *ECDSACurve) Set(str string) error {
 		*c = ECDSACurve{v}
 		return nil
 	}
-	return fmt.Errorf("unsupported curve '%s', should be one of: %s", str, strings.Join(ecdsaCurves(), ", "))
+	return fmt.Errorf("unsupported curve '%s', must be one of: %s", str, strings.Join(ecdsaCurves(), ", "))
 }
 
 func (c *ECDSACurve) Type() string {
diff --git a/internal/flags/ecdsa_curve_test.go b/internal/flags/ecdsa_curve_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d1b1f05e715d781331a61a4f7b9cc66bbbf23f9c
--- /dev/null
+++ b/internal/flags/ecdsa_curve_test.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestECDSACurve_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "p256", "p256", false},
+		{"unsupported", "unsupported", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var c ECDSACurve
+			if err := c.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := c.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/helm_chart_source.go b/internal/flags/helm_chart_source.go
index ed1898df8f54ff7ff840176520ef9848baa1c50f..5055c75b655c25f0d909abb9f3774a008a670e34 100644
--- a/internal/flags/helm_chart_source.go
+++ b/internal/flags/helm_chart_source.go
@@ -31,42 +31,42 @@ type HelmChartSource struct {
 	Name string
 }
 
-func (h *HelmChartSource) String() string {
-	if h.Name == "" {
+func (s *HelmChartSource) String() string {
+	if s.Name == "" {
 		return ""
 	}
-	return fmt.Sprintf("%s/%s", h.Kind, h.Name)
+	return fmt.Sprintf("%s/%s", s.Kind, s.Name)
 }
 
-func (h *HelmChartSource) Set(str string) error {
+func (s *HelmChartSource) Set(str string) error {
 	if strings.TrimSpace(str) == "" {
 		return fmt.Errorf("no helm chart source given, please specify %s",
-			h.Description())
+			s.Description())
 	}
 
 	sourceKind, sourceName := utils.ParseObjectKindName(str)
-	if sourceKind == "" {
+	if sourceKind == "" || sourceName == "" {
 		return fmt.Errorf("invalid helm chart source '%s', must be in format <kind>/<name>", str)
 	}
 	if !utils.ContainsItemString(supportedHelmChartSourceKinds, sourceKind) {
-		return fmt.Errorf("source kind '%s' is not supported, can be one of: %s",
+		return fmt.Errorf("source kind '%s' is not supported, must be one of: %s",
 			sourceKind, strings.Join(supportedHelmChartSourceKinds, ", "))
 	}
 
-	h.Name = sourceName
-	h.Kind = sourceKind
+	s.Name = sourceName
+	s.Kind = sourceKind
 
 	return nil
 }
 
-func (h *HelmChartSource) Type() string {
+func (s *HelmChartSource) Type() string {
 	return "helmChartSource"
 }
 
-func (h *HelmChartSource) Description() string {
+func (s *HelmChartSource) Description() string {
 	return fmt.Sprintf(
-		"source that contains the chart in the format '<kind>/<name>',"+
-			"where kind can be one of: (%s)",
+		"source that contains the chart in the format '<kind>/<name>', "+
+			"where kind must be one of: (%s)",
 		strings.Join(supportedHelmChartSourceKinds, ", "),
 	)
 }
diff --git a/internal/flags/helm_chart_source_test.go b/internal/flags/helm_chart_source_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..298f3553bfe1bf8bd44d756f1569e24f023a91d0
--- /dev/null
+++ b/internal/flags/helm_chart_source_test.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"fmt"
+	"testing"
+
+	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+)
+
+func TestHelmChartSource_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", fmt.Sprintf("%s/foo", sourcev1.HelmRepositoryKind), fmt.Sprintf("%s/foo", sourcev1.HelmRepositoryKind), false},
+		{"unsupported", "Unsupported/kind", "", true},
+		{"invalid format", sourcev1.HelmRepositoryKind, "", true},
+		{"missing name", fmt.Sprintf("%s/", sourcev1.HelmRepositoryKind), "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var s HelmChartSource
+			if err := s.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := s.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/helm_release_values.go b/internal/flags/helm_release_values.go
index c7ae1f48801544fa037b2188f65ab0285af5b504..84509c67be9829af79237563fad2d8ac50c88687 100644
--- a/internal/flags/helm_release_values.go
+++ b/internal/flags/helm_release_values.go
@@ -30,17 +30,17 @@ type HelmReleaseValuesFrom struct {
 	Name string
 }
 
-func (h *HelmReleaseValuesFrom) String() string {
-	if h.Name == "" {
+func (v *HelmReleaseValuesFrom) String() string {
+	if v.Name == "" {
 		return ""
 	}
-	return fmt.Sprintf("%s/%s", h.Kind, h.Name)
+	return fmt.Sprintf("%s/%s", v.Kind, v.Name)
 }
 
-func (h *HelmReleaseValuesFrom) Set(str string) error {
+func (v *HelmReleaseValuesFrom) Set(str string) error {
 	if strings.TrimSpace(str) == "" {
 		return fmt.Errorf("no values given, please specify %s",
-			h.Description())
+			v.Description())
 	}
 
 	sourceKind, sourceName := utils.ParseObjectKindName(str)
@@ -48,24 +48,24 @@ func (h *HelmReleaseValuesFrom) Set(str string) error {
 		return fmt.Errorf("invalid Kubernetes object reference '%s', must be in format <kind>/<name>", str)
 	}
 	if !utils.ContainsItemString(supportedHelmReleaseValuesFromKinds, sourceKind) {
-		return fmt.Errorf("reference kind '%s' is not supported, can be one of: %s",
+		return fmt.Errorf("reference kind '%s' is not supported, must be one of: %s",
 			sourceKind, strings.Join(supportedHelmReleaseValuesFromKinds, ", "))
 	}
 
-	h.Name = sourceName
-	h.Kind = sourceKind
+	v.Name = sourceName
+	v.Kind = sourceKind
 
 	return nil
 }
 
-func (h *HelmReleaseValuesFrom) Type() string {
+func (v *HelmReleaseValuesFrom) Type() string {
 	return "helmReleaseValuesFrom"
 }
 
-func (h *HelmReleaseValuesFrom) Description() string {
+func (v *HelmReleaseValuesFrom) Description() string {
 	return fmt.Sprintf(
-		"Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>',"+
-			"where kind can be one of: (%s)",
+		"Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', "+
+			"where kind must be one of: (%s)",
 		strings.Join(supportedHelmReleaseValuesFromKinds, ", "),
 	)
 }
diff --git a/internal/flags/helm_release_values_test.go b/internal/flags/helm_release_values_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4b18b8474023670cf55ac0d2fed5db2eb7cd4b2d
--- /dev/null
+++ b/internal/flags/helm_release_values_test.go
@@ -0,0 +1,46 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestHelmReleaseValuesFrom_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "Secret/foo", "Secret/foo", false},
+		{"unsupported", "Unsupported/kind", "", true},
+		{"invalid format", "Secret", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var h HelmReleaseValuesFrom
+			if err := h.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := h.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/kustomization_source.go b/internal/flags/kustomization_source.go
index f696defaeb15f00e54204a9bab57b60e3084837d..109132b2c326eebfa953b3607442296a3521a327 100644
--- a/internal/flags/kustomization_source.go
+++ b/internal/flags/kustomization_source.go
@@ -31,42 +31,48 @@ type KustomizationSource struct {
 	Name string
 }
 
-func (k *KustomizationSource) String() string {
-	if k.Name == "" {
+func (s *KustomizationSource) String() string {
+	if s.Name == "" {
 		return ""
 	}
-	return fmt.Sprintf("%s/%s", k.Kind, k.Name)
+	return fmt.Sprintf("%s/%s", s.Kind, s.Name)
 }
 
-func (k *KustomizationSource) Set(str string) error {
+func (s *KustomizationSource) Set(str string) error {
 	if strings.TrimSpace(str) == "" {
-		return fmt.Errorf("no kustomization source given, please specify %s",
-			k.Description())
+		return fmt.Errorf("no Kustomization source given, please specify %s",
+			s.Description())
 	}
 
 	sourceKind, sourceName := utils.ParseObjectKindName(str)
+	if sourceName == "" {
+		return fmt.Errorf("no name given for source of kind '%s'", sourceKind)
+	}
 	if sourceKind == "" {
+		if utils.ContainsItemString(supportedKustomizationSourceKinds, sourceName) {
+			return fmt.Errorf("no name given for source of kind '%s'", sourceName)
+		}
 		sourceKind = sourcev1.GitRepositoryKind
 	}
 	if !utils.ContainsItemString(supportedKustomizationSourceKinds, sourceKind) {
-		return fmt.Errorf("source kind '%s' is not supported, can be one of: %s",
+		return fmt.Errorf("source kind '%s' is not supported, must be one of: %s",
 			sourceKind, strings.Join(supportedKustomizationSourceKinds, ", "))
 	}
 
-	k.Name = sourceName
-	k.Kind = sourceKind
+	s.Name = sourceName
+	s.Kind = sourceKind
 
 	return nil
 }
 
-func (k *KustomizationSource) Type() string {
+func (s *KustomizationSource) Type() string {
 	return "kustomizationSource"
 }
 
-func (k *KustomizationSource) Description() string {
+func (s *KustomizationSource) Description() string {
 	return fmt.Sprintf(
-		"source that contains the Kubernetes manifests in the format '[<kind>/]<name>',"+
-			"where kind can be one of: (%s), if kind is not specified it defaults to GitRepository",
+		"source that contains the Kubernetes manifests in the format '[<kind>/]<name>', "+
+			"where kind must be one of: (%s), if kind is not specified it defaults to GitRepository",
 		strings.Join(supportedKustomizationSourceKinds, ", "),
 	)
 }
diff --git a/internal/flags/kustomization_source_test.go b/internal/flags/kustomization_source_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..09106137aa76b4236ac6ca98ab87dadcfe15765c
--- /dev/null
+++ b/internal/flags/kustomization_source_test.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"fmt"
+	"testing"
+
+	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+)
+
+func TestKustomizationSource_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", fmt.Sprintf("%s/foo", sourcev1.GitRepositoryKind), fmt.Sprintf("%s/foo", sourcev1.GitRepositoryKind), false},
+		{"default kind", "foo", fmt.Sprintf("%s/foo", sourcev1.GitRepositoryKind), false},
+		{"unsupported", "Unsupported/kind", "", true},
+		{"missing name", fmt.Sprintf("%s/", sourcev1.GitRepositoryKind), "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var s KustomizationSource
+			if err := s.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := s.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/log_level_test.go b/internal/flags/log_level_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bccf1ac1158ef1c4d41f55e7b28ae631d001eedf
--- /dev/null
+++ b/internal/flags/log_level_test.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestLogLevel_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "info", "info", false},
+		{"unsupported", "unsupported", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var l LogLevel
+			if err := l.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := l.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/public_key_algorithm_test.go b/internal/flags/public_key_algorithm_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..84e19c22b9dba4dbe4e8bf348500c0afefeb5a3c
--- /dev/null
+++ b/internal/flags/public_key_algorithm_test.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestPublicKeyAlgorithm_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "rsa", "rsa", false},
+		{"unsupported", "unsupported", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var a PublicKeyAlgorithm
+			if err := a.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := a.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/rsa_key_bits.go b/internal/flags/rsa_key_bits.go
index 59adf7a7bd5bd177f43a7ab9eb999d750a4c579f..e214e1bf6d8a1e98d5fc74b239dde3d7872d914f 100644
--- a/internal/flags/rsa_key_bits.go
+++ b/internal/flags/rsa_key_bits.go
@@ -39,8 +39,8 @@ func (b *RSAKeyBits) Set(str string) error {
 	if err != nil {
 		return err
 	}
-	if bits%8 != 0 {
-		return fmt.Errorf("RSA key bit size should be a multiples of 8")
+	if bits == 0 || bits%8 != 0 {
+		return fmt.Errorf("RSA key bit size must be a multiples of 8")
 	}
 	*b = RSAKeyBits(bits)
 	return nil
diff --git a/internal/flags/rsa_key_bits_test.go b/internal/flags/rsa_key_bits_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f980527007cf377cc8100cf91aad864bbf95dcec
--- /dev/null
+++ b/internal/flags/rsa_key_bits_test.go
@@ -0,0 +1,46 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestRSAKeyBits_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "4096", "4096", false},
+		{"empty (default)", "", "2048", false},
+		{"unsupported", "0", "0", true},
+		{"unsupported", "123", "0", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var b RSAKeyBits
+			if err := b.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := b.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/flags/source_bucket_provider.go b/internal/flags/source_bucket_provider.go
index e314fd9d538f072f0aaad49a7158ba43c8c99d60..87e269771f05d77172b46d1a1f7407a48d0b68e0 100644
--- a/internal/flags/source_bucket_provider.go
+++ b/internal/flags/source_bucket_provider.go
@@ -28,29 +28,28 @@ var supportedSourceBucketProviders = []string{sourcev1.GenericBucketProvider, so
 
 type SourceBucketProvider string
 
-func (s *SourceBucketProvider) String() string {
-	return string(*s)
+func (p *SourceBucketProvider) String() string {
+	return string(*p)
 }
 
-func (s *SourceBucketProvider) Set(str string) error {
+func (p *SourceBucketProvider) Set(str string) error {
 	if strings.TrimSpace(str) == "" {
 		return fmt.Errorf("no source bucket provider given, please specify %s",
-			s.Description())
+			p.Description())
 	}
-
 	if !utils.ContainsItemString(supportedSourceBucketProviders, str) {
-		return fmt.Errorf("source bucket provider '%s' is not supported, can be one of: %v",
+		return fmt.Errorf("source bucket provider '%s' is not supported, must be one of: %v",
 			str, strings.Join(supportedSourceBucketProviders, ", "))
 	}
-
+	*p = SourceBucketProvider(str)
 	return nil
 }
 
-func (s *SourceBucketProvider) Type() string {
+func (p *SourceBucketProvider) Type() string {
 	return "sourceBucketProvider"
 }
 
-func (s *SourceBucketProvider) Description() string {
+func (p *SourceBucketProvider) Description() string {
 	return fmt.Sprintf(
 		"the S3 compatible storage provider name, available options are: (%s)",
 		strings.Join(supportedSourceBucketProviders, ", "),
diff --git a/internal/flags/source_bucket_provider_test.go b/internal/flags/source_bucket_provider_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..05b4f7ce9966d393a878916369e201feeed111b3
--- /dev/null
+++ b/internal/flags/source_bucket_provider_test.go
@@ -0,0 +1,47 @@
+/*
+Copyright 2020 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+
+	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+)
+
+func TestSourceBucketProvider_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", sourcev1.GenericBucketProvider, sourcev1.GenericBucketProvider, false},
+		{"unsupported", "unsupported", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var s SourceBucketProvider
+			if err := s.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := s.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}
diff --git a/internal/utils/utils.go b/internal/utils/utils.go
index ae8577b710f275418e334a8d895855f2bf082ba2..91b9011cfad655f44ed4585354508725d9431088 100644
--- a/internal/utils/utils.go
+++ b/internal/utils/utils.go
@@ -237,7 +237,6 @@ func ParseObjectKindName(input string) (string, string) {
 	if len(parts) == 2 {
 		kind, name = parts[0], parts[1]
 	}
-
 	return kind, name
 }