From 7b96a338a3a21c6aedf20055cc42d721ae7f30d0 Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Thu, 16 Apr 2020 21:00:00 -0700 Subject: [PATCH] extract kubeconfig test utils to a type Signed-off-by: Ahmet Alp Balkan --- cmd/kubectx/kubeconfig_test.go | 31 +++++---------- internal/kubeconfig/contextmodify_test.go | 44 +++++++++++----------- internal/kubeconfig/contexts_test.go | 13 +++---- internal/kubeconfig/currentcontext_test.go | 9 ++--- internal/kubeconfig/helper_test.go | 23 +++++++++++ internal/kubeconfig/helpers_test.go | 15 -------- internal/kubeconfig/kubeconfig_test.go | 11 +++--- internal/printer/color_test.go | 25 ++++-------- internal/testutil/kubeconfigloader.go | 2 + internal/testutil/testutil.go | 18 +++++++++ 10 files changed, 95 insertions(+), 96 deletions(-) create mode 100644 internal/kubeconfig/helper_test.go create mode 100644 internal/testutil/kubeconfigloader.go create mode 100644 internal/testutil/testutil.go diff --git a/cmd/kubectx/kubeconfig_test.go b/cmd/kubectx/kubeconfig_test.go index cc396bd..4350ca4 100644 --- a/cmd/kubectx/kubeconfig_test.go +++ b/cmd/kubectx/kubeconfig_test.go @@ -8,22 +8,9 @@ import ( "testing" "github.com/ahmetb/kubectx/internal/kubeconfig" + "github.com/ahmetb/kubectx/internal/testutil" ) -func withTestVar(key, value string) func() { - // TODO(ahmetb) this method is currently duplicated - // consider extracting to internal/testutil or something - - orig, ok := os.LookupEnv(key) - os.Setenv(key, value) - return func() { - if ok { - os.Setenv(key, orig) - } else { - os.Unsetenv(key) - } - } -} func Test_homeDir(t *testing.T) { type env struct{ k, v string } @@ -72,7 +59,7 @@ func Test_homeDir(t *testing.T) { t.Run(c.name, func(tt *testing.T) { var unsets []func() for _, e := range c.envs { - unsets = append(unsets, withTestVar(e.k, e.v)) + unsets = append(unsets, testutil.WithEnvVar(e.k, e.v)) } got := homeDir() @@ -87,7 +74,7 @@ func Test_homeDir(t *testing.T) { } func Test_kubeconfigPath(t *testing.T) { - defer withTestVar("HOME", "/x/y/z")() + defer testutil.WithEnvVar("HOME", "/x/y/z")() expected := filepath.FromSlash("/x/y/z/.kube/config") got, err := kubeconfigPath() @@ -100,9 +87,9 @@ func Test_kubeconfigPath(t *testing.T) { } func Test_kubeconfigPath_noEnvVars(t *testing.T) { - defer withTestVar("XDG_CACHE_HOME", "")() - defer withTestVar("HOME", "")() - defer withTestVar("USERPROFILE", "")() + defer testutil.WithEnvVar("XDG_CACHE_HOME", "")() + defer testutil.WithEnvVar("HOME", "")() + defer testutil.WithEnvVar("USERPROFILE", "")() _, err := kubeconfigPath() if err == nil { @@ -111,7 +98,7 @@ func Test_kubeconfigPath_noEnvVars(t *testing.T) { } func Test_kubeconfigPath_envOvveride(t *testing.T) { - defer withTestVar("KUBECONFIG", "foo")() + defer testutil.WithEnvVar("KUBECONFIG", "foo")() v, err := kubeconfigPath() if err != nil { @@ -124,7 +111,7 @@ func Test_kubeconfigPath_envOvveride(t *testing.T) { func Test_kubeconfigPath_envOvverideDoesNotSupportPathSeparator(t *testing.T) { path := strings.Join([]string{"file1", "file2"}, string(os.PathListSeparator)) - defer withTestVar("KUBECONFIG", path)() + defer testutil.WithEnvVar("KUBECONFIG", path)() _, err := kubeconfigPath() if err == nil { @@ -133,7 +120,7 @@ func Test_kubeconfigPath_envOvverideDoesNotSupportPathSeparator(t *testing.T) { } func TestStandardKubeconfigLoader_returnsNotFoundErr(t *testing.T) { - defer withTestVar("KUBECONFIG", "foo")() + defer testutil.WithEnvVar("KUBECONFIG", "foo")() kc := new(kubeconfig.Kubeconfig).WithLoader(defaultLoader) err := kc.Parse() if err == nil { diff --git a/internal/kubeconfig/contextmodify_test.go b/internal/kubeconfig/contextmodify_test.go index d236d9e..f468e4c 100644 --- a/internal/kubeconfig/contextmodify_test.go +++ b/internal/kubeconfig/contextmodify_test.go @@ -1,28 +1,27 @@ package kubeconfig import ( - "strings" "testing" "github.com/google/go-cmp/cmp" ) func TestKubeconfig_DeleteContextEntry_errors(t *testing.T) { - kc := new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`[1, 2, 3]`)}) + kc := new(Kubeconfig).WithLoader(WithMockKubeconfigLoader(`[1, 2, 3]`)) _ = kc.Parse() err := kc.DeleteContextEntry("foo") if err == nil { t.Fatal("supposed to fail on non-mapping nodes") } - kc = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`a: b`)}) + kc = new(Kubeconfig).WithLoader(WithMockKubeconfigLoader(`a: b`)) _ = kc.Parse() err = kc.DeleteContextEntry("foo") if err == nil { t.Fatal("supposed to fail if contexts key does not exist") } - kc = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`contexts: "some string"`)}) + kc = new(Kubeconfig).WithLoader(WithMockKubeconfigLoader(`contexts: "some string"`)) _ = kc.Parse() err = kc.DeleteContextEntry("foo") if err == nil { @@ -31,8 +30,8 @@ func TestKubeconfig_DeleteContextEntry_errors(t *testing.T) { } func TestKubeconfig_DeleteContextEntry(t *testing.T) { - test := &testLoader{in: strings.NewReader( - `contexts: [{name: c1}, {name: c2}, {name: c3}]`)} + test := WithMockKubeconfigLoader( + `contexts: [{name: c1}, {name: c2}, {name: c3}]`) kc := new(Kubeconfig).WithLoader(test) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -45,16 +44,15 @@ func TestKubeconfig_DeleteContextEntry(t *testing.T) { } expected := "contexts: [{name: c2}, {name: c3}]\n" - out := test.out.String() + out := test.Output() if diff := cmp.Diff(expected, out); diff != "" { t.Fatalf("diff: %s", diff) } } func TestKubeconfig_ModifyCurrentContext_fieldExists(t *testing.T) { - test := &testLoader{in: strings.NewReader( - `current-context: abc -field1: value1`)} + test := WithMockKubeconfigLoader(`current-context: abc +field1: value1`) kc := new(Kubeconfig).WithLoader(test) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -68,15 +66,15 @@ field1: value1`)} expected := `current-context: foo field1: value1` + "\n" - out := test.out.String() + out := test.Output() if diff := cmp.Diff(expected, out); diff != "" { t.Fatalf("diff: %s", diff) } } func TestKubeconfig_ModifyCurrentContext_fieldMissing(t *testing.T) { - test := &testLoader{in: strings.NewReader( - `field1: value1`)} + test := WithMockKubeconfigLoader( + `field1: value1`) kc := new(Kubeconfig).WithLoader(test) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -89,7 +87,7 @@ func TestKubeconfig_ModifyCurrentContext_fieldMissing(t *testing.T) { } expected := `field1: value1` + "\n" + "current-context: foo\n" - out := test.out.String() + out := test.Output() if diff := cmp.Diff(expected, out); diff != "" { t.Fatalf("diff: %s", diff) } @@ -97,8 +95,8 @@ func TestKubeconfig_ModifyCurrentContext_fieldMissing(t *testing.T) { func TestKubeconfig_ModifyContextName_noContextsEntryError(t *testing.T) { // no context entries - test := &testLoader{in: strings.NewReader( - `a: b`)} + test := WithMockKubeconfigLoader( + `a: b`) kc := new(Kubeconfig).WithLoader(test) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -111,8 +109,8 @@ func TestKubeconfig_ModifyContextName_noContextsEntryError(t *testing.T) { func TestKubeconfig_ModifyContextName_contextsEntryNotSequenceError(t *testing.T) { // no context entries - test := &testLoader{in: strings.NewReader( - `contexts: "hello"`)} + test := WithMockKubeconfigLoader( + `contexts: "hello"`) kc := new(Kubeconfig).WithLoader(test) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -124,8 +122,8 @@ func TestKubeconfig_ModifyContextName_contextsEntryNotSequenceError(t *testing.T func TestKubeconfig_ModifyContextName_noChange(t *testing.T) { - test := &testLoader{in: strings.NewReader( - `contexts: [{name: c1}, {name: c2}, {name: c3}]`)} + test := WithMockKubeconfigLoader( + `contexts: [{name: c1}, {name: c2}, {name: c3}]`) kc := new(Kubeconfig).WithLoader(test) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -136,8 +134,8 @@ func TestKubeconfig_ModifyContextName_noChange(t *testing.T) { } func TestKubeconfig_ModifyContextName(t *testing.T) { - test := &testLoader{in: strings.NewReader( - `contexts: [{name: c1}, {name: c2}, {name: c3}]`)} + test := WithMockKubeconfigLoader( + `contexts: [{name: c1}, {name: c2}, {name: c3}]`) kc := new(Kubeconfig).WithLoader(test) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -150,7 +148,7 @@ func TestKubeconfig_ModifyContextName(t *testing.T) { } expected := "contexts: [{name: ccc}, {name: c2}, {name: c3}]\n" - out := test.out.String() + out := test.Output() if diff := cmp.Diff(expected, out); diff != "" { t.Fatalf("diff: %s", diff) } diff --git a/internal/kubeconfig/contexts_test.go b/internal/kubeconfig/contexts_test.go index 1395814..192580c 100644 --- a/internal/kubeconfig/contexts_test.go +++ b/internal/kubeconfig/contexts_test.go @@ -1,21 +1,20 @@ package kubeconfig import ( - "strings" "testing" "github.com/google/go-cmp/cmp" ) func TestKubeconfig_ContextNames(t *testing.T) { - tl := &testLoader{in: strings.NewReader(` + tl := WithMockKubeconfigLoader(` contexts: - name: abc - name: def field1: value1 - name: ghi foo: - bar: zoo`)} + bar: zoo`) kc := new(Kubeconfig).WithLoader(tl) if err := kc.Parse(); err != nil { @@ -30,7 +29,7 @@ contexts: } func TestKubeconfig_ContextNames_noContextsEntry(t *testing.T) { - tl := &testLoader{in: strings.NewReader(`a: b`)} + tl := WithMockKubeconfigLoader(`a: b`) kc := new(Kubeconfig).WithLoader(tl) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -43,7 +42,7 @@ func TestKubeconfig_ContextNames_noContextsEntry(t *testing.T) { } func TestKubeconfig_ContextNames_nonArrayContextsEntry(t *testing.T) { - tl := &testLoader{in: strings.NewReader(`contexts: "hello"`)} + tl := WithMockKubeconfigLoader(`contexts: "hello"`) kc := new(Kubeconfig).WithLoader(tl) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -56,9 +55,9 @@ func TestKubeconfig_ContextNames_nonArrayContextsEntry(t *testing.T) { } func TestKubeconfig_CheckContextExists(t *testing.T) { - tl := &testLoader{in: strings.NewReader(`contexts: + tl := WithMockKubeconfigLoader(`contexts: - name: c1 -- name: c2`)} +- name: c2`) kc := new(Kubeconfig).WithLoader(tl) if err := kc.Parse(); err != nil { diff --git a/internal/kubeconfig/currentcontext_test.go b/internal/kubeconfig/currentcontext_test.go index b0fde35..2673884 100644 --- a/internal/kubeconfig/currentcontext_test.go +++ b/internal/kubeconfig/currentcontext_test.go @@ -1,12 +1,11 @@ package kubeconfig import ( - "strings" "testing" ) func TestKubeconfig_GetCurrentContext(t *testing.T) { - tl := &testLoader{in: strings.NewReader(`current-context: foo`)} + tl := WithMockKubeconfigLoader(`current-context: foo`) kc := new(Kubeconfig).WithLoader(tl) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -20,7 +19,7 @@ func TestKubeconfig_GetCurrentContext(t *testing.T) { } func TestKubeconfig_GetCurrentContext_missingField(t *testing.T) { - tl := &testLoader{in: strings.NewReader(`abc: def`)} + tl := WithMockKubeconfigLoader(`abc: def`) kc := new(Kubeconfig).WithLoader(tl) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -34,7 +33,7 @@ func TestKubeconfig_GetCurrentContext_missingField(t *testing.T) { } func TestKubeconfig_UnsetCurrentContext(t *testing.T) { - tl := &testLoader{in: strings.NewReader(`current-context: foo`)} + tl := WithMockKubeconfigLoader(`current-context: foo`) kc := new(Kubeconfig).WithLoader(tl) if err := kc.Parse(); err != nil { t.Fatal(err) @@ -46,7 +45,7 @@ func TestKubeconfig_UnsetCurrentContext(t *testing.T) { t.Fatal(err) } - out := tl.out.String() + out := tl.Output() expected := `current-context: "" ` if out != expected { diff --git a/internal/kubeconfig/helper_test.go b/internal/kubeconfig/helper_test.go new file mode 100644 index 0000000..3732cc0 --- /dev/null +++ b/internal/kubeconfig/helper_test.go @@ -0,0 +1,23 @@ +package kubeconfig + +import ( + "bytes" + "io" + "strings" +) + +type MockKubeconfigLoader struct { + in io.Reader + out bytes.Buffer +} + +func (t *MockKubeconfigLoader) Read(p []byte) (n int, err error) { return t.in.Read(p) } +func (t *MockKubeconfigLoader) Write(p []byte) (n int, err error) { return t.out.Write(p) } +func (t *MockKubeconfigLoader) Close() error { return nil } +func (t *MockKubeconfigLoader) Reset() error { return nil } +func (t *MockKubeconfigLoader) Load() (ReadWriteResetCloser, error) { return t, nil } +func (t *MockKubeconfigLoader) Output() string { return t.out.String() } + +func WithMockKubeconfigLoader(kubecfg string) *MockKubeconfigLoader { + return &MockKubeconfigLoader{in: strings.NewReader(kubecfg)} +} diff --git a/internal/kubeconfig/helpers_test.go b/internal/kubeconfig/helpers_test.go index 001c20e..659d0cc 100644 --- a/internal/kubeconfig/helpers_test.go +++ b/internal/kubeconfig/helpers_test.go @@ -1,17 +1,2 @@ package kubeconfig -import ( - "bytes" - "strings" -) - -type testLoader struct { - in *strings.Reader - out bytes.Buffer -} - -func (t *testLoader) Read(p []byte) (n int, err error) { return t.in.Read(p) } -func (t *testLoader) Write(p []byte) (n int, err error) { return t.out.Write(p) } -func (t *testLoader) Close() error { return nil } -func (t *testLoader) Reset() error { return nil } -func (t *testLoader) Load() (ReadWriteResetCloser, error) { return t, nil } diff --git a/internal/kubeconfig/kubeconfig_test.go b/internal/kubeconfig/kubeconfig_test.go index c5544e0..8125983 100644 --- a/internal/kubeconfig/kubeconfig_test.go +++ b/internal/kubeconfig/kubeconfig_test.go @@ -1,24 +1,23 @@ package kubeconfig import ( - "strings" "testing" "github.com/google/go-cmp/cmp" ) func TestParse(t *testing.T) { - err := new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`a: [1, 2`)}).Parse() + err := new(Kubeconfig).WithLoader(WithMockKubeconfigLoader(`a: [1, 2`)).Parse() if err == nil { t.Fatal("expected error from bad yaml") } - err = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`[1, 2, 3]`)}).Parse() + err = new(Kubeconfig).WithLoader(WithMockKubeconfigLoader(`[1, 2, 3]`)).Parse() if err == nil { t.Fatal("expected error from not-mapping root node") } - err = new(Kubeconfig).WithLoader(&testLoader{in: strings.NewReader(`current-context: foo`)}).Parse() + err = new(Kubeconfig).WithLoader(WithMockKubeconfigLoader(`current-context: foo`)).Parse() if err != nil { t.Fatal(err) } @@ -26,7 +25,7 @@ func TestParse(t *testing.T) { func TestSave(t *testing.T) { in := `a: [1, 2, 3]` + "\n" - test := &testLoader{in: strings.NewReader(in)} + test := WithMockKubeconfigLoader(in) kc := new(Kubeconfig).WithLoader(test) defer kc.Close() if err := kc.Parse(); err != nil { @@ -37,7 +36,7 @@ func TestSave(t *testing.T) { t.Fatal(err) } expected := in+"current-context: hello\n" - if diff := cmp.Diff(expected, test.out.String()); diff != "" { + if diff := cmp.Diff(expected, test.Output()); diff != "" { t.Fatal(diff) } } diff --git a/internal/printer/color_test.go b/internal/printer/color_test.go index 9db2518..fd6e1f7 100644 --- a/internal/printer/color_test.go +++ b/internal/printer/color_test.go @@ -1,31 +1,20 @@ package printer import ( - "os" "testing" "github.com/google/go-cmp/cmp" -) -func withTestVar(key, value string) func() { - orig, ok := os.LookupEnv(key) - os.Setenv(key, value) - return func() { - if ok { - os.Setenv(key, orig) - } else { - os.Unsetenv(key) - } - } -} + "github.com/ahmetb/kubectx/internal/testutil" +) var ( tr, fa = true, false ) func Test_useColors_forceColors(t *testing.T) { - defer withTestVar("_KUBECTX_FORCE_COLOR", "1")() - defer withTestVar("NO_COLOR", "1")() + defer testutil.WithEnvVar("_KUBECTX_FORCE_COLOR", "1")() + defer testutil.WithEnvVar("NO_COLOR", "1")() if v := UseColors(); !cmp.Equal(v, &tr) { t.Fatalf("expected UseColors() = true; got = %v", v) @@ -33,7 +22,7 @@ func Test_useColors_forceColors(t *testing.T) { } func Test_useColors_disableColors(t *testing.T) { - defer withTestVar("NO_COLOR", "1")() + defer testutil.WithEnvVar("NO_COLOR", "1")() if v := UseColors(); !cmp.Equal(v, &fa) { t.Fatalf("expected UseColors() = false; got = %v", v) @@ -41,8 +30,8 @@ func Test_useColors_disableColors(t *testing.T) { } func Test_useColors_default(t *testing.T) { - defer withTestVar("NO_COLOR", "")() - defer withTestVar("_KUBECTX_FORCE_COLOR", "")() + defer testutil.WithEnvVar("NO_COLOR", "")() + defer testutil.WithEnvVar("_KUBECTX_FORCE_COLOR", "")() if v := UseColors(); v != nil { t.Fatalf("expected UseColors() = nil; got=%v", *v) diff --git a/internal/testutil/kubeconfigloader.go b/internal/testutil/kubeconfigloader.go new file mode 100644 index 0000000..bb1b162 --- /dev/null +++ b/internal/testutil/kubeconfigloader.go @@ -0,0 +1,2 @@ +package testutil + diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go new file mode 100644 index 0000000..12470b8 --- /dev/null +++ b/internal/testutil/testutil.go @@ -0,0 +1,18 @@ +package testutil + +import "os" + +// WithEnvVar sets an env var temporarily. Call its return value +// in defer to restore original value in env (if exists). +func WithEnvVar(key, value string) func() { + orig, ok := os.LookupEnv(key) + os.Setenv(key, value) + return func() { + if ok { + os.Setenv(key, orig) + } else { + os.Unsetenv(key) + } + } +} +