From 1dc184574836e34967049e2fda5f774036372207 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 22 Mar 2016 16:17:00 -0700 Subject: [PATCH 1/5] test(cli): add test pattern for cli tests --- cmd/helm/helm_test.go | 126 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 cmd/helm/helm_test.go diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go new file mode 100644 index 000000000..c2d04bb33 --- /dev/null +++ b/cmd/helm/helm_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 main + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/codegangsta/cli" + "github.com/kubernetes/helm/pkg/format" +) + +type testHelm struct { + t *testing.T + mux *http.ServeMux + server *httptest.Server + app *cli.App +} + +func setup() *testHelm { + th := &testHelm{} + + th.app = cli.NewApp() + th.app.Commands = commands + + th.app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "host,u", + Value: "https://localhost:8000/", + }, + cli.IntFlag{ + Name: "timeout", + Value: 10, + }, + cli.BoolFlag{ + Name: "debug", + }, + } + th.app.Before = func(c *cli.Context) error { + debug = c.GlobalBool("debug") + return nil + } + + th.mux = http.NewServeMux() + th.server = httptest.NewServer(th.mux) + + return th +} + +func (th *testHelm) teardown() { + th.server.Close() +} + +func (th *testHelm) URL() string { + return th.server.URL +} + +func (th *testHelm) Run(args ...string) { + args = append([]string{"helm", "--host", th.URL()}, args...) + th.app.Run(args) +} + +// CaptureOutput redirect all log/std streams, capture and replace +func CaptureOutput(fn func()) string { + logStderr := format.Stderr + logStdout := format.Stdout + osStdout := os.Stdout + osStderr := os.Stderr + + defer func() { + format.Stderr = logStderr + format.Stdout = logStdout + os.Stdout = osStdout + os.Stderr = osStderr + }() + + r, w, _ := os.Pipe() + + format.Stderr = w + format.Stdout = w + os.Stdout = w + os.Stderr = w + + fn() + + // read test output and restore previous stdout + w.Close() + b, _ := ioutil.ReadAll(r) + return string(b) +} + +func TestHelm(t *testing.T) { + th := setup() + defer th.teardown() + + th.mux.HandleFunc("/deployments", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`["guestbook.yaml"]`)) + }) + + expected := "guestbook.yaml\n" + + actual := CaptureOutput(func() { + th.Run("deployment", "list") + }) + + if expected != actual { + t.Errorf("Expected %v got %v", expected, actual) + } +} From b03f2bcc9449fb83dbfb3083f0c43995977a162d Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 22 Mar 2016 23:01:33 -0700 Subject: [PATCH 2/5] test(cli): add show depoyment unit test --- cmd/helm/deployment_test.go | 69 +++++++++++++++++++++++++++++++++++++ cmd/helm/helm_test.go | 19 ---------- 2 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 cmd/helm/deployment_test.go diff --git a/cmd/helm/deployment_test.go b/cmd/helm/deployment_test.go new file mode 100644 index 000000000..592f9ec07 --- /dev/null +++ b/cmd/helm/deployment_test.go @@ -0,0 +1,69 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 main + +import ( + "encoding/json" + "net/http" + "testing" + + "github.com/kubernetes/helm/pkg/common" +) + +func TestShowDeployment(t *testing.T) { + th := setup() + defer th.teardown() + + deployment := common.NewDeployment("guestbook.yaml") + + th.mux.HandleFunc("/deployments/", func(w http.ResponseWriter, r *http.Request) { + data, err := json.Marshal(deployment) + if err != nil { + t.Fatal(err) + } + w.Write(data) + }) + + expected := "Name: guestbook.yaml\nStatus: Created\n" + + actual := CaptureOutput(func() { + th.Run("deployment", "show", "guestbook.yaml") + }) + + if expected != actual { + t.Errorf("Expected %v got %v", expected, actual) + } +} + +func TestListDeployment(t *testing.T) { + th := setup() + defer th.teardown() + + th.mux.HandleFunc("/deployments", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`["guestbook.yaml"]`)) + }) + + expected := "guestbook.yaml\n" + + actual := CaptureOutput(func() { + th.Run("deployment", "list") + }) + + if expected != actual { + t.Errorf("Expected %v got %v", expected, actual) + } +} diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index c2d04bb33..df6ec4ba9 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -105,22 +105,3 @@ func CaptureOutput(fn func()) string { b, _ := ioutil.ReadAll(r) return string(b) } - -func TestHelm(t *testing.T) { - th := setup() - defer th.teardown() - - th.mux.HandleFunc("/deployments", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(`["guestbook.yaml"]`)) - }) - - expected := "guestbook.yaml\n" - - actual := CaptureOutput(func() { - th.Run("deployment", "list") - }) - - if expected != actual { - t.Errorf("Expected %v got %v", expected, actual) - } -} From 4b3d3cfa56aae75abb6988e923eb9dcfc47af8e9 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 22 Mar 2016 23:45:18 -0700 Subject: [PATCH 3/5] test(cli): use table test for deployments --- cmd/helm/deployment_test.go | 59 +++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/cmd/helm/deployment_test.go b/cmd/helm/deployment_test.go index 592f9ec07..834189c63 100644 --- a/cmd/helm/deployment_test.go +++ b/cmd/helm/deployment_test.go @@ -25,27 +25,48 @@ import ( ) func TestShowDeployment(t *testing.T) { - th := setup() - defer th.teardown() - - deployment := common.NewDeployment("guestbook.yaml") + var deploymentTestCases = []struct { + args []string + resp *common.Deployment + expected string + }{ + { + []string{"deployment", "show", "guestbook.yaml"}, + &common.Deployment{ + Name: "guestbook.yaml", + State: &common.DeploymentState{Status: common.CreatedStatus}, + }, + "Name: guestbook.yaml\nStatus: Created\n", + }, + { + []string{"deployment", "show", "guestbook.yaml"}, + &common.Deployment{ + Name: "guestbook.yaml", + State: &common.DeploymentState{ + common.FailedStatus, []string{"error message"}, + }, + }, + "Name: guestbook.yaml\nStatus: Failed\nErrors:\n error message\n", + }, + } - th.mux.HandleFunc("/deployments/", func(w http.ResponseWriter, r *http.Request) { - data, err := json.Marshal(deployment) - if err != nil { - t.Fatal(err) + for _, tc := range deploymentTestCases { + th := setup() + th.mux.HandleFunc("/deployments/", func(w http.ResponseWriter, r *http.Request) { + data, err := json.Marshal(tc.resp) + if err != nil { + t.Fatal(err) + } + w.Write(data) + }) + + actual := CaptureOutput(func() { + th.Run(tc.args...) + }) + if tc.expected != actual { + t.Errorf("Expected %v got %v", tc.expected, actual) } - w.Write(data) - }) - - expected := "Name: guestbook.yaml\nStatus: Created\n" - - actual := CaptureOutput(func() { - th.Run("deployment", "show", "guestbook.yaml") - }) - - if expected != actual { - t.Errorf("Expected %v got %v", expected, actual) + th.teardown() } } From caa14b19e07eaacb51505e9302fa19a09010b0e2 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 23 Mar 2016 10:46:29 -0700 Subject: [PATCH 4/5] fix(cli): use go1.5 templates circleci only supports go1.5 --- cmd/helm/deployment.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cmd/helm/deployment.go b/cmd/helm/deployment.go index b77d74d78..dd83734b3 100644 --- a/cmd/helm/deployment.go +++ b/cmd/helm/deployment.go @@ -30,13 +30,9 @@ var errMissingDeploymentArg = errors.New("First argument, deployment name, is re const defaultShowFormat = `Name: {{.Name}} Status: {{.State.Status}} -{{- with .State.Errors}} -Errors: -{{- range .}} - {{.}} -{{- end}} -{{- end}} -` +{{with .State.Errors}}Errors: +{{range .}} {{.}}{{end}} +{{end}}` func init() { addCommands(deploymentCommands()) From 92031722c997583e638bc2f938543e30053d07e9 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 23 Mar 2016 11:40:01 -0700 Subject: [PATCH 5/5] test(cli): refactor cli test setup --- cmd/helm/deployment_test.go | 45 ++++++++++-------------------- cmd/helm/helm_test.go | 55 ++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 53 deletions(-) diff --git a/cmd/helm/deployment_test.go b/cmd/helm/deployment_test.go index 834189c63..1882f04ca 100644 --- a/cmd/helm/deployment_test.go +++ b/cmd/helm/deployment_test.go @@ -24,10 +24,10 @@ import ( "github.com/kubernetes/helm/pkg/common" ) -func TestShowDeployment(t *testing.T) { +func TestDeployment(t *testing.T) { var deploymentTestCases = []struct { args []string - resp *common.Deployment + resp interface{} expected string }{ { @@ -48,43 +48,26 @@ func TestShowDeployment(t *testing.T) { }, "Name: guestbook.yaml\nStatus: Failed\nErrors:\n error message\n", }, + { + []string{"deployment", "list"}, + []string{"guestbook.yaml"}, + "guestbook.yaml\n", + }, } for _, tc := range deploymentTestCases { - th := setup() + th := testHelm(t) th.mux.HandleFunc("/deployments/", func(w http.ResponseWriter, r *http.Request) { data, err := json.Marshal(tc.resp) - if err != nil { - t.Fatal(err) - } + th.must(err) w.Write(data) }) - actual := CaptureOutput(func() { - th.Run(tc.args...) - }) - if tc.expected != actual { - t.Errorf("Expected %v got %v", tc.expected, actual) - } - th.teardown() - } -} + th.run(tc.args...) -func TestListDeployment(t *testing.T) { - th := setup() - defer th.teardown() - - th.mux.HandleFunc("/deployments", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(`["guestbook.yaml"]`)) - }) - - expected := "guestbook.yaml\n" - - actual := CaptureOutput(func() { - th.Run("deployment", "list") - }) - - if expected != actual { - t.Errorf("Expected %v got %v", expected, actual) + if tc.expected != th.output { + t.Errorf("Expected %v got %v", tc.expected, th.output) + } + th.cleanup() } } diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index df6ec4ba9..481be8a85 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -27,15 +27,16 @@ import ( "github.com/kubernetes/helm/pkg/format" ) -type testHelm struct { +type testHelmData struct { t *testing.T mux *http.ServeMux server *httptest.Server app *cli.App + output string } -func setup() *testHelm { - th := &testHelm{} +func testHelm(t *testing.T) *testHelmData { + th := &testHelmData{t: t} th.app = cli.NewApp() th.app.Commands = commands @@ -64,39 +65,49 @@ func setup() *testHelm { return th } -func (th *testHelm) teardown() { +func (th *testHelmData) cleanup() { th.server.Close() } -func (th *testHelm) URL() string { +func (th *testHelmData) URL() string { return th.server.URL } -func (th *testHelm) Run(args ...string) { - args = append([]string{"helm", "--host", th.URL()}, args...) - th.app.Run(args) +// must gives a fatal error if err is not nil. +func (th *testHelmData) must(err error) { + if err != nil { + th.t.Fatal(err) + } } -// CaptureOutput redirect all log/std streams, capture and replace -func CaptureOutput(fn func()) string { - logStderr := format.Stderr - logStdout := format.Stdout - osStdout := os.Stdout - osStderr := os.Stderr +// check gives a test non-fatal error if err is not nil. +func (th *testHelmData) check(err error) { + if err != nil { + th.t.Error(err) + } +} + +func (th *testHelmData) run(args ...string) { + th.output = "" + args = append([]string{"helm", "--host", th.URL()}, args...) + th.output = captureOutput(func() { + th.app.Run(args) + }) +} +// captureOutput redirect all log/std streams, capture and replace +func captureOutput(fn func()) string { + osStdout, osStderr := os.Stdout, os.Stderr + logStdout, logStderr := format.Stdout, format.Stderr defer func() { - format.Stderr = logStderr - format.Stdout = logStdout - os.Stdout = osStdout - os.Stderr = osStderr + os.Stdout, os.Stderr = osStdout, osStderr + format.Stdout, format.Stderr = logStdout, logStderr }() r, w, _ := os.Pipe() - format.Stderr = w - format.Stdout = w - os.Stdout = w - os.Stderr = w + os.Stdout, os.Stderr = w, w + format.Stdout, format.Stderr = w, w fn()