From fa143c6ddf18f051f1c6e5c7261d731e69f668d3 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Sat, 5 Aug 2017 13:25:53 +0800 Subject: [PATCH] add tests --- hack/make-rules/test-cmd-util.sh | 7 ++ pkg/kubectl/cmd/resource/get_test.go | 95 +++++++++++++++++++++++++++- pkg/kubectl/resource/builder_test.go | 45 ++++++++++++- pkg/kubectl/resource/helper_test.go | 66 +++++++++++++++++++ 4 files changed, 210 insertions(+), 3 deletions(-) diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index 8da6bd86a3b..50e873dd752 100755 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -430,6 +430,13 @@ run_pod_tests() { kubectl create -f test/fixtures/doc-yaml/admin/limitrange/valid-pod.yaml "${kube_flags[@]}" # Post-condition: valid-pod POD is created kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" 'valid-pod:' + # Command + output_message=$(kubectl get pods --field-selector metadata.name=valid-pod "${kube_flags[@]}") + kube::test::if_has_string "${output_message}" "valid-pod" + # Command + phase=$(kubectl get "${kube_flags[@]}" pod valid-pod -o go-template='{{ .status.phase }}') + output_message=$(kubectl get pods --field-selector status.phase="${phase}" "${kube_flags[@]}") + kube::test::if_has_string "${output_message}" "valid-pod" ### Delete PODs with no parameter mustn't kill everything # Pre-condition: valid-pod POD exists diff --git a/pkg/kubectl/cmd/resource/get_test.go b/pkg/kubectl/cmd/resource/get_test.go index 1054004fbe8..7ecfb2c4160 100644 --- a/pkg/kubectl/cmd/resource/get_test.go +++ b/pkg/kubectl/cmd/resource/get_test.go @@ -825,7 +825,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { } } -func TestGetMultipleTypeObjectsWithSelector(t *testing.T) { +func TestGetMultipleTypeObjectsWithLabelSelector(t *testing.T) { pods, svc, _ := testData() f, tf, codec, _ := cmdtesting.NewAPIFactory() @@ -868,6 +868,49 @@ func TestGetMultipleTypeObjectsWithSelector(t *testing.T) { } } +func TestGetMultipleTypeObjectsWithFieldSelector(t *testing.T) { + pods, svc, _ := testData() + + f, tf, codec, _ := cmdtesting.NewAPIFactory() + tf.Printer = &testPrinter{} + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: unstructuredSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + if req.URL.Query().Get(metav1.FieldSelectorQueryParam("v1")) != "a=b" { + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + } + switch req.URL.Path { + case "/namespaces/test/pods": + return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil + case "/namespaces/test/services": + return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + errBuf := bytes.NewBuffer([]byte{}) + + cmd := NewCmdGet(f, buf, errBuf) + cmd.SetOutput(buf) + + cmd.Flags().Set("field-selector", "a=b") + cmd.Run(cmd, []string{"pods,services"}) + + expected, err := extractResourceList([]runtime.Object{pods, svc}) + if err != nil { + t.Fatal(err) + } + verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) + + if len(buf.String()) == 0 { + t.Errorf("unexpected empty output") + } +} + func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { _, svc, _ := testData() node := &api.Node{ @@ -1006,7 +1049,7 @@ func watchTestData() ([]api.Pod, []watch.Event) { return pods, events } -func TestWatchSelector(t *testing.T) { +func TestWatchLabelSelector(t *testing.T) { pods, events := watchTestData() f, tf, codec, _ := cmdtesting.NewAPIFactory() @@ -1054,6 +1097,54 @@ func TestWatchSelector(t *testing.T) { } } +func TestWatchFieldSelector(t *testing.T) { + pods, events := watchTestData() + + f, tf, codec, _ := cmdtesting.NewAPIFactory() + tf.Printer = &testPrinter{} + podList := &api.PodList{ + Items: pods, + ListMeta: metav1.ListMeta{ + ResourceVersion: "10", + }, + } + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: unstructuredSerializer, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + if req.URL.Query().Get(metav1.FieldSelectorQueryParam("v1")) != "a=b" { + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + } + switch req.URL.Path { + case "/namespaces/test/pods": + if req.URL.Query().Get("watch") == "true" { + return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[2:])}, nil + } + return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, podList)}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + errBuf := bytes.NewBuffer([]byte{}) + + cmd := NewCmdGet(f, buf, errBuf) + cmd.SetOutput(buf) + + cmd.Flags().Set("watch", "true") + cmd.Flags().Set("field-selector", "a=b") + cmd.Run(cmd, []string{"pods"}) + + expected := []runtime.Object{&pods[0], &pods[1], events[2].Object, events[3].Object} + verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) + + if len(buf.String()) == 0 { + t.Errorf("unexpected empty output") + } +} + func TestWatchResource(t *testing.T) { pods, events := watchTestData() diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index 83fe86fae2f..ec21bf67f1b 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -46,7 +46,6 @@ import ( restclientwatch "k8s.io/client-go/rest/watch" utiltesting "k8s.io/client-go/util/testing" "k8s.io/kubernetes/pkg/kubectl/scheme" - "k8s.io/kubernetes/pkg/api/legacyscheme" ) var ( @@ -763,6 +762,50 @@ func TestLabelSelectorRequiresKnownTypes(t *testing.T) { } } +func TestFieldSelector(t *testing.T) { + pods, svc := testData() + fieldKey := metav1.FieldSelectorQueryParam(corev1GV.String()) + b := NewBuilder(restmapper, LegacyCategoryExpander, scheme.Scheme, fakeClientWith("", t, map[string]string{ + "/namespaces/test/pods?" + fieldKey + "=a%3Db": runtime.EncodeOrDie(corev1Codec, pods), + "/namespaces/test/services?" + fieldKey + "=a%3Db": runtime.EncodeOrDie(corev1Codec, svc), + }), corev1Codec). + FieldSelectorParam("a=b"). + NamespaceParam("test"). + Flatten() + + test := &testVisitor{} + singleItemImplied := false + + if b.Do().Err() == nil { + t.Errorf("unexpected non-error") + } + + b.ResourceTypeOrNameArgs(true, "pods,service") + + err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle) + if err != nil || singleItemImplied || len(test.Infos) != 3 { + t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos) + } + if !apiequality.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svc.Items[0]}, test.Objects()) { + t.Errorf("unexpected visited objects: %#v", test.Objects()) + } + + if _, err := b.Do().ResourceMapping(); err == nil { + t.Errorf("unexpected non-error") + } +} + +func TestFieldSelectorRequiresKnownTypes(t *testing.T) { + b := NewBuilder(restmapper, LegacyCategoryExpander, scheme.Scheme, fakeClient(), corev1Codec). + FieldSelectorParam("a=b"). + NamespaceParam("test"). + ResourceTypes("unknown") + + if b.Do().Err() == nil { + t.Errorf("unexpected non-error") + } +} + func TestSingleResourceType(t *testing.T) { b := NewBuilder(restmapper, LegacyCategoryExpander, scheme.Scheme, fakeClient(), corev1Codec). LabelSelectorParam("a=b"). diff --git a/pkg/kubectl/resource/helper_test.go b/pkg/kubectl/resource/helper_test.go index 55be32a766c..96a2d7789ca 100644 --- a/pkg/kubectl/resource/helper_test.go +++ b/pkg/kubectl/resource/helper_test.go @@ -385,6 +385,72 @@ func TestHelperList(t *testing.T) { } } +func TestHelperListSelectorCombination(t *testing.T) { + tests := []struct { + Name string + Err bool + ErrMsg string + FieldSelector string + LabelSelector string + }{ + { + Name: "No selector", + Err: false, + }, + { + Name: "Only Label Selector", + Err: false, + LabelSelector: "foo=baz", + }, + { + Name: "Only Field Selector", + Err: false, + FieldSelector: "xyz=zyx", + }, + { + Name: "Both Label and Field Selector", + Err: false, + LabelSelector: "foo=baz", + FieldSelector: "xyz=zyx", + }, + } + + resp := &http.Response{ + StatusCode: http.StatusOK, + Header: header(), + Body: objBody(&corev1.PodList{ + Items: []corev1.Pod{{ + ObjectMeta: metav1.ObjectMeta{Name: "foo"}, + }, + }, + }), + } + client := &fake.RESTClient{ + NegotiatedSerializer: scheme.Codecs, + Resp: resp, + Err: nil, + } + modifier := &Helper{ + RESTClient: client, + NamespaceScoped: true, + } + + for _, test := range tests { + _, err := modifier.List("bar", + corev1GV.String(), + false, + &metav1.ListOptions{LabelSelector: test.LabelSelector, FieldSelector: test.FieldSelector}) + if test.Err { + if err == nil { + t.Errorf("%q expected error: %q", test.Name, test.ErrMsg) + } + if err != nil && err.Error() != test.ErrMsg { + t.Errorf("%q expected error: %q", test.Name, test.ErrMsg) + } + } + } +} + func TestHelperReplace(t *testing.T) { expectPut := func(path string, req *http.Request) bool { if req.Method != "PUT" {