From 044779126b40ed4c99748cfbb8bb49d8b5be8920 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Wed, 23 Jul 2014 14:08:16 -0400 Subject: [PATCH] Add unit tests for example syntax Tests api/examples, examples, and any embedded json in readme. --- api/examples/controller-list.json | 17 +-- api/examples/pod-list.json | 6 +- api/examples/service-list.json | 4 +- examples/doc.go | 18 +++ examples/examples_test.go | 182 ++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 11 deletions(-) create mode 100644 examples/doc.go create mode 100644 examples/examples_test.go diff --git a/api/examples/controller-list.json b/api/examples/controller-list.json index 36317709578..c3c1d969d2c 100644 --- a/api/examples/controller-list.json +++ b/api/examples/controller-list.json @@ -9,13 +9,16 @@ }, "podTemplate": { "desiredState": { - "image": "dockerfile/nginx", - "networkPorts": [ - { - "hostPort": 8080, - "containerPort": 80 - } - ] + "manifest": { + "version": "v1beta1", + "image": "dockerfile/nginx", + "networkPorts": [ + { + "hostPort": 8080, + "containerPort": 80 + } + ] + } }, "labels": { "name": "testRun" diff --git a/api/examples/pod-list.json b/api/examples/pod-list.json index 7f753a151b2..e9259424929 100644 --- a/api/examples/pod-list.json +++ b/api/examples/pod-list.json @@ -11,12 +11,13 @@ "version": "v1beta1", "id": "my-pod-1", "containers": [{ + "name": "nginx", "image": "dockerfile/nginx", "ports": [{ "hostPort": 8080, "containerPort": 80 }] - } + }] } }, "currentState": { @@ -34,12 +35,13 @@ "version": "v1beta1", "id": "my-pod-2", "containers": [{ + "name": "nginx", "image": "dockerfile/nginx", "ports": [{ "hostPort": 8080, "containerPort": 80 }] - } + }] } }, "currentState": { diff --git a/api/examples/service-list.json b/api/examples/service-list.json index 2d9dea5129d..87aff593817 100644 --- a/api/examples/service-list.json +++ b/api/examples/service-list.json @@ -5,7 +5,7 @@ "port": 8000, "labels": { "name": "nginx" - } + }, "selector": { "name": "nginx" } @@ -16,7 +16,7 @@ "labels": { "env": "prod", "name": "jetty" - } + }, "selector": { "env": "prod", "name": "jetty" diff --git a/examples/doc.go b/examples/doc.go new file mode 100644 index 00000000000..a2bd90d4605 --- /dev/null +++ b/examples/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2014 Google Inc. 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. +*/ + +// Examples contains sample applications for trying out the concepts in Kubernetes. +package examples diff --git a/examples/examples_test.go b/examples/examples_test.go new file mode 100644 index 00000000000..de7fc721e6c --- /dev/null +++ b/examples/examples_test.go @@ -0,0 +1,182 @@ +/* +Copyright 2014 Google Inc. 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 examples_test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/golang/glog" +) + +func validateObject(obj interface{}) (errors []error) { + switch t := obj.(type) { + case *api.ReplicationController: + errors = api.ValidateManifest(&t.DesiredState.PodTemplate.DesiredState.Manifest) + case *api.ReplicationControllerList: + for i := range t.Items { + errors = append(errors, validateObject(&t.Items[i])...) + } + case *api.Service: + errors = api.ValidateService(t) + case *api.ServiceList: + for i := range t.Items { + errors = append(errors, validateObject(&t.Items[i])...) + } + case *api.Pod: + errors = api.ValidateManifest(&t.DesiredState.Manifest) + case *api.PodList: + for i := range t.Items { + errors = append(errors, validateObject(&t.Items[i])...) + } + default: + return []error{fmt.Errorf("no validation defined for %#v", obj)} + } + return errors +} + +func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error { + err := filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() && path != inDir { + return filepath.SkipDir + } + name := filepath.Base(path) + ext := filepath.Ext(name) + if ext != "" { + name = name[:len(name)-len(ext)] + } + if ext != ".json" { + return nil + } + glog.Infof("Testing %s", path) + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + fn(name, path, data) + return nil + }) + return err +} + +func TestApiExamples(t *testing.T) { + expected := map[string]interface{}{ + "controller": &api.ReplicationController{}, + "controller-list": &api.ReplicationControllerList{}, + "pod": &api.Pod{}, + "pod-list": &api.PodList{}, + "service": &api.Service{}, + "external-service": &api.Service{}, + "service-list": &api.ServiceList{}, + } + + tested := 0 + err := walkJSONFiles("../api/examples", func(name, path string, data []byte) { + expectedType, found := expected[name] + if !found { + t.Errorf("%s does not have a test case defined", path) + return + } + tested += 1 + if err := api.DecodeInto(data, expectedType); err != nil { + t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) + return + } + if errors := validateObject(expectedType); len(errors) > 0 { + t.Errorf("%s did not validate correctly: %v", path, errors) + } + }) + if err != nil { + t.Errorf("Expected no error, Got %v", err) + } + if tested != len(expected) { + t.Errorf("Expected %d examples, Got %d", len(expected), tested) + } +} + +func TestExamples(t *testing.T) { + expected := map[string]interface{}{ + "frontend-controller": &api.ReplicationController{}, + "redis-slave-controller": &api.ReplicationController{}, + "redis-master": &api.Pod{}, + "frontend-service": &api.Service{}, + "redis-master-service": &api.Service{}, + "redis-slave-service": &api.Service{}, + } + + tested := 0 + err := walkJSONFiles("../examples/guestbook", func(name, path string, data []byte) { + expectedType, found := expected[name] + if !found { + t.Errorf("%s does not have a test case defined", path) + return + } + tested += 1 + if err := api.DecodeInto(data, expectedType); err != nil { + t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) + return + } + if errors := validateObject(expectedType); len(errors) > 0 { + t.Errorf("%s did not validate correctly: %v", path, errors) + } + }) + if err != nil { + t.Errorf("Expected no error, Got %v", err) + } + if tested != len(expected) { + t.Errorf("Expected %d examples, Got %d", len(expected), tested) + } +} + +var jsonRegexp = regexp.MustCompile("(?ms)^```\\w*\\n(\\{.+?\\})\\w*\\n^```") + +func TestReadme(t *testing.T) { + path := "../README.md" + data, err := ioutil.ReadFile(path) + if err != nil { + t.Fatalf("Unable to read file: %v", err) + } + + match := jsonRegexp.FindStringSubmatch(string(data)) + if match == nil { + return + } + for _, json := range match[1:] { + expectedType := &api.Pod{} + if err := api.DecodeInto([]byte(json), expectedType); err != nil { + t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) + return + } + if errors := validateObject(expectedType); len(errors) > 0 { + t.Errorf("%s did not validate correctly: %v", path, errors) + } + encoded, err := api.Encode(expectedType) + if err != nil { + t.Errorf("Could not encode object: %v", err) + continue + } + t.Logf("Found pod %s\n%s", json, encoded) + } +}