// Copyright © 2021 The Knative 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 commands import ( "fmt" "os" "path" "testing" "github.com/spf13/cobra" "gotest.tools/v3/assert" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" clienteventingv1beta2 "knative.dev/client/pkg/eventing/v1beta2" v1beta1 "knative.dev/client/pkg/messaging/v1" clientv1beta1 "knative.dev/client/pkg/serving/v1beta1" clientsourcesv1 "knative.dev/client/pkg/sources/v1" "knative.dev/client/pkg/sources/v1beta2" eventingv1beta2 "knative.dev/eventing/pkg/apis/eventing/v1beta2" messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" sourcesv1beta2 "knative.dev/eventing/pkg/apis/sources/v1beta2" sourcesv1fake "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1/fake" sourcesv1beta2fake "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1beta2/fake" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" clienttesting "k8s.io/client-go/testing" clienteventingv1 "knative.dev/client/pkg/eventing/v1" v1 "knative.dev/client/pkg/serving/v1" eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1/fake" beta2fake "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake" servingv1 "knative.dev/serving/pkg/apis/serving/v1" servingv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1" servingv1fake "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1/fake" servingv1beta1fake "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1beta1/fake" ) type testType struct { name string namespace string p *KnParams args []string toComplete string resource string } type mockMessagingClient struct { channelsClient v1beta1.KnChannelsClient subscriptionsClient v1beta1.KnSubscriptionsClient } func (m *mockMessagingClient) ChannelsClient() v1beta1.KnChannelsClient { return m.channelsClient } func (m *mockMessagingClient) SubscriptionsClient() v1beta1.KnSubscriptionsClient { return m.subscriptionsClient } const ( testNs = "test-ns" errorNs = "error-ns" ) var ( testSvc1 = servingv1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-svc-1", Namespace: testNs}, } testSvc2 = servingv1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-svc-2", Namespace: testNs}, } testSvc3 = servingv1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-svc-3", Namespace: testNs}, } testNsServices = []servingv1.Service{testSvc1, testSvc2, testSvc3} fakeServing = &servingv1fake.FakeServingV1{Fake: &clienttesting.Fake{}} fakeServingAlpha = &servingv1beta1fake.FakeServingV1beta1{Fake: &clienttesting.Fake{}} ) var ( testBroker1 = eventingv1.Broker{ TypeMeta: metav1.TypeMeta{ Kind: "Broker", APIVersion: "eventing.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-broker-1", Namespace: testNs}, } testBroker2 = eventingv1.Broker{ TypeMeta: metav1.TypeMeta{ Kind: "Broker", APIVersion: "eventing.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-broker-2", Namespace: testNs}, } testBroker3 = eventingv1.Broker{ TypeMeta: metav1.TypeMeta{ Kind: "Broker", APIVersion: "eventing.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-broker-3", Namespace: testNs}, } testNsBrokers = []eventingv1.Broker{testBroker1, testBroker2, testBroker3} fakeEventing = &fake.FakeEventingV1{Fake: &clienttesting.Fake{}} ) var ( testRev1 = servingv1.Revision{ TypeMeta: metav1.TypeMeta{ Kind: "Revision", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-rev-1", Namespace: testNs}, } testRev2 = servingv1.Revision{ TypeMeta: metav1.TypeMeta{ Kind: "Revision", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-rev-2", Namespace: testNs}, } testRev3 = servingv1.Revision{ TypeMeta: metav1.TypeMeta{ Kind: "Revision", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-rev-3", Namespace: testNs}, } testNsRevs = []servingv1.Revision{testRev1, testRev2, testRev3} ) var ( testRoute1 = servingv1.Route{ TypeMeta: metav1.TypeMeta{ Kind: "Route", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-route-1", Namespace: testNs}, } testRoute2 = servingv1.Route{ TypeMeta: metav1.TypeMeta{ Kind: "Route", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-route-2", Namespace: testNs}, } testRoute3 = servingv1.Route{ TypeMeta: metav1.TypeMeta{ Kind: "Route", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-route-3", Namespace: testNs}, } testNsRoutes = []servingv1.Route{testRoute1, testRoute2, testRoute3} ) var ( testDomain1 = servingv1beta1.DomainMapping{ TypeMeta: metav1.TypeMeta{ Kind: "DomainMapping", APIVersion: "serving.knative.dev/v1beta1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-domain-1", Namespace: testNs}, } testDomain2 = servingv1beta1.DomainMapping{ TypeMeta: metav1.TypeMeta{ Kind: "DomainMapping", APIVersion: "serving.knative.dev/v1beta1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-domain-2", Namespace: testNs}, } testDomain3 = servingv1beta1.DomainMapping{ TypeMeta: metav1.TypeMeta{ Kind: "DomainMapping", APIVersion: "serving.knative.dev/v1beta1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-domain-3", Namespace: testNs}, } testNsDomains = []servingv1beta1.DomainMapping{testDomain1, testDomain2, testDomain3} ) var ( testTrigger1 = eventingv1.Trigger{ TypeMeta: metav1.TypeMeta{ Kind: "Trigger", APIVersion: "eventing.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-1", Namespace: testNs}, } testTrigger2 = eventingv1.Trigger{ TypeMeta: metav1.TypeMeta{ Kind: "Trigger", APIVersion: "eventing.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-2", Namespace: testNs}, } testTrigger3 = eventingv1.Trigger{ TypeMeta: metav1.TypeMeta{ Kind: "Trigger", APIVersion: "eventing.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-3", Namespace: testNs}, } testNsTriggers = []eventingv1.Trigger{testTrigger1, testTrigger2, testTrigger3} ) var ( testContainerSource1 = sourcesv1.ContainerSource{ TypeMeta: metav1.TypeMeta{ Kind: "ContainerSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-container-source-1", Namespace: testNs}, } testContainerSource2 = sourcesv1.ContainerSource{ TypeMeta: metav1.TypeMeta{ Kind: "ContainerSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-container-source-2", Namespace: testNs}, } testContainerSource3 = sourcesv1.ContainerSource{ TypeMeta: metav1.TypeMeta{ Kind: "ContainerSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-container-source-3", Namespace: testNs}, } testNsContainerSources = []sourcesv1.ContainerSource{testContainerSource1, testContainerSource2, testContainerSource3} fakeSources = &sourcesv1fake.FakeSourcesV1{Fake: &clienttesting.Fake{}} ) var ( testApiServerSource1 = sourcesv1.ApiServerSource{ TypeMeta: metav1.TypeMeta{ Kind: "ApiServerSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-ApiServer-source-1", Namespace: testNs}, } testApiServerSource2 = sourcesv1.ApiServerSource{ TypeMeta: metav1.TypeMeta{ Kind: "ApiServerSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-ApiServer-source-2", Namespace: testNs}, } testApiServerSource3 = sourcesv1.ApiServerSource{ TypeMeta: metav1.TypeMeta{ Kind: "ApiServerSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-ApiServer-source-3", Namespace: testNs}, } testNsApiServerSources = []sourcesv1.ApiServerSource{testApiServerSource1, testApiServerSource2, testApiServerSource3} ) var ( testSinkBinding1 = sourcesv1.SinkBinding{ TypeMeta: metav1.TypeMeta{ Kind: "SinkBinding", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-sink-binding-1", Namespace: testNs}, } testSinkBinding2 = sourcesv1.SinkBinding{ TypeMeta: metav1.TypeMeta{ Kind: "SinkBinding", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-sink-binding-2", Namespace: testNs}, } testSinkBinding3 = sourcesv1.SinkBinding{ TypeMeta: metav1.TypeMeta{ Kind: "SinkBinding", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-sink-binding-3", Namespace: testNs}, } testNsSinkBindings = []sourcesv1.SinkBinding{testSinkBinding1, testSinkBinding2, testSinkBinding3} ) var ( testPingSource1 = sourcesv1beta2.PingSource{ TypeMeta: metav1.TypeMeta{ Kind: "PingSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-ping-source-1", Namespace: testNs}, } testPingSource2 = sourcesv1beta2.PingSource{ TypeMeta: metav1.TypeMeta{ Kind: "PingSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-ping-source-2", Namespace: testNs}, } testPingSource3 = sourcesv1beta2.PingSource{ TypeMeta: metav1.TypeMeta{ Kind: "PingSource", APIVersion: "sources.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-ping-source-3", Namespace: testNs}, } testNsPingSources = []sourcesv1beta2.PingSource{testPingSource1, testPingSource2, testPingSource3} fakeSourcesV1Beta2 = &sourcesv1beta2fake.FakeSourcesV1beta2{Fake: &clienttesting.Fake{}} ) var ( testChannel1 = messagingv1.Channel{ TypeMeta: metav1.TypeMeta{ Kind: "Channel", APIVersion: "messaging.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-channel-1", Namespace: testNs}, } testChannel2 = messagingv1.Channel{ TypeMeta: metav1.TypeMeta{ Kind: "Channel", APIVersion: "messaging.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-channel-2", Namespace: testNs}, } testChannel3 = messagingv1.Channel{ TypeMeta: metav1.TypeMeta{ Kind: "Channel", APIVersion: "messaging.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-channel-3", Namespace: testNs}, } testNsChannels = []messagingv1.Channel{testChannel1, testChannel2, testChannel3} ) var ( testSubscription1 = messagingv1.Subscription{ TypeMeta: metav1.TypeMeta{ Kind: "Subscription", APIVersion: "messaging.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-subscription-1", Namespace: testNs}, } testSubscription2 = messagingv1.Subscription{ TypeMeta: metav1.TypeMeta{ Kind: "Subscription", APIVersion: "messaging.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-subscription-2", Namespace: testNs}, } testSubscription3 = messagingv1.Subscription{ TypeMeta: metav1.TypeMeta{ Kind: "Subscription", APIVersion: "messaging.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-subscription-3", Namespace: testNs}, } testNsSubscriptions = []messagingv1.Subscription{testSubscription1, testSubscription2, testSubscription3} ) var ( testEventtype1 = eventingv1beta2.EventType{ TypeMeta: metav1.TypeMeta{ Kind: "EventType", APIVersion: "eventing.knative.dev/v1beta1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-eventtype-1", Namespace: testNs}, } testEventtype2 = eventingv1beta2.EventType{ TypeMeta: metav1.TypeMeta{ Kind: "EventType", APIVersion: "eventing.knative.dev/v1beta1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-eventtype-2", Namespace: testNs}, } testEventtype3 = eventingv1beta2.EventType{ TypeMeta: metav1.TypeMeta{ Kind: "EventType", APIVersion: "eventing.knative.dev/v1beta1", }, ObjectMeta: metav1.ObjectMeta{Name: "test-eventtype-3", Namespace: testNs}, } testEventtypes = []eventingv1beta2.EventType{testEventtype1, testEventtype2, testEventtype3} fakeEventingBeta2Client = &beta2fake.FakeEventingV1beta2{Fake: &clienttesting.Fake{}} ) var knParams = initialiseKnParams() func initialiseKnParams() *KnParams { blankConfig, err := clientcmd.NewClientConfigFromBytes([]byte(`kind: Config version: v1beta2 users: - name: u clusters: - name: c cluster: server: example.com contexts: - name: x context: user: u cluster: c current-context: x `)) if err != nil { panic(err) } return &KnParams{ NewServingClient: func(namespace string) (v1.KnServingClient, error) { return v1.NewKnServingClient(fakeServing, namespace), nil }, NewGitopsServingClient: func(namespace string, dir string) (v1.KnServingClient, error) { return v1.NewKnServingGitOpsClient(namespace, dir), nil }, NewEventingClient: func(namespace string) (clienteventingv1.KnEventingClient, error) { return clienteventingv1.NewKnEventingClient(fakeEventing, namespace), nil }, NewServingV1beta1Client: func(namespace string) (clientv1beta1.KnServingClient, error) { return clientv1beta1.NewKnServingClient(fakeServingAlpha, namespace), nil }, NewSourcesClient: func(namespace string) (clientsourcesv1.KnSourcesClient, error) { return clientsourcesv1.NewKnSourcesClient(fakeSources, namespace), nil }, NewSourcesV1beta2Client: func(namespace string) (v1beta2.KnSourcesClient, error) { return v1beta2.NewKnSourcesClient(fakeSourcesV1Beta2, namespace), nil }, NewEventingV1beta2Client: func(namespace string) (clienteventingv1beta2.KnEventingV1Beta2Client, error) { return clienteventingv1beta2.NewKnEventingV1Beta2Client(fakeEventingBeta2Client, namespace), nil }, ClientConfig: blankConfig, } } func TestResourceNameCompletionFuncService(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeServing.AddReactor("list", "services", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list services")) } return true, &servingv1.ServiceList{Items: testNsServices}, nil }) tests := []testType{ { "Empty suggestions when no parent command found", testNs, knParams, nil, "", "no-parent", }, { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "service", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "service", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "service", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "service", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "service", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncBroker(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeEventing.AddReactor("list", "brokers", func(action clienttesting.Action) (bool, runtime.Object, error) { if action.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list brokers")) } return true, &eventingv1.BrokerList{Items: testNsBrokers}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "broker", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "broker", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "broker", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "broker", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "broker", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncRevision(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeServing.AddReactor("list", "revisions", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list revisions")) } return true, &servingv1.RevisionList{Items: testNsRevs}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "revision", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "revision", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "revision", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "revision", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "revision", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncGitOps(t *testing.T) { tempDir := setupTempDir(t) completionFunc := ResourceNameCompletionFunc(knParams) tests := []testType{ { "Empty suggestions when no parent command found", testNs, knParams, nil, "", "service", }, { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "service", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "service", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "service", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "service", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "service", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] cmd.Flags().String("target", tempDir, "target directory") cmd.Flags().Set("namespace", tt.namespace) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncRoute(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeServing.AddReactor("list", "routes", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list routes")) } return true, &servingv1.RouteList{Items: testNsRoutes}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "route", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "route", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "route", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "route", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "route", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncDomain(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeServingAlpha.AddReactor("list", "domainmappings", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list domains")) } return true, &servingv1beta1.DomainMappingList{Items: testNsDomains}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "domain", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "domain", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "domain", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "domain", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "domain", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncTrigger(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeServing.AddReactor("list", "triggers", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list triggers")) } return true, &eventingv1.TriggerList{Items: testNsTriggers}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "trigger", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "trigger", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "trigger", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "trigger", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "trigger", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncContainerSource(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeSources.AddReactor("list", "containersources", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list container sources")) } return true, &sourcesv1.ContainerSourceList{Items: testNsContainerSources}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "container", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "container", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "container", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "container", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "container", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncApiserverSource(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeSources.AddReactor("list", "apiserversources", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list apiserver sources")) } return true, &sourcesv1.ApiServerSourceList{Items: testNsApiServerSources}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "apiserver", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "apiserver", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "apiserver", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "apiserver", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "apiserver", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncBindingSource(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeSources.AddReactor("list", "sinkbindings", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list binding sources")) } return true, &sourcesv1.SinkBindingList{Items: testNsSinkBindings}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "binding", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "binding", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "binding", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "binding", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "binding", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncPingSource(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeSourcesV1Beta2.AddReactor("list", "pingsources", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list ping sources")) } return true, &sourcesv1beta2.PingSourceList{Items: testNsPingSources}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "ping", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "ping", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "ping", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "ping", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "ping", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func TestResourceNameCompletionFuncChannel(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) channelClient := v1beta1.NewMockKnChannelsClient(t) channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) channelClient.Recorder().ListChannel(&messagingv1.ChannelList{}, fmt.Errorf("error listing channels")) channelClient.Recorder().ListChannel(&messagingv1.ChannelList{}, fmt.Errorf("error listing channels")) messagingClient := &mockMessagingClient{channelClient, nil} knParams.NewMessagingClient = func(namespace string) (v1beta1.KnMessagingClient, error) { return messagingClient, nil } tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "channel", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "channel", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "channel", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "channel", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "channel", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } channelClient.Recorder().Validate() } func TestResourceNameCompletionFuncSubscription(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) subscriptionsClient := v1beta1.NewMockKnSubscriptionsClient(t) subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{}, fmt.Errorf("error listing channels")) subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{}, fmt.Errorf("error listing channels")) messagingClient := &mockMessagingClient{nil, subscriptionsClient} knParams.NewMessagingClient = func(namespace string) (v1beta1.KnMessagingClient, error) { return messagingClient, nil } tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "subscription", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "subscription", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "subscription", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "subscription", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "subscription", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } subscriptionsClient.Recorder().Validate() } func TestResourceNameCompletionFuncEventtype(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) fakeEventingBeta2Client.AddReactor("list", "eventtypes", func(a clienttesting.Action) (bool, runtime.Object, error) { if a.GetNamespace() == errorNs { return true, nil, errors.NewInternalError(fmt.Errorf("unable to list eventtypes")) } return true, &eventingv1beta2.EventTypeList{Items: testEventtypes}, nil }) tests := []testType{ { "Empty suggestions when non-zero args", testNs, knParams, []string{"xyz"}, "", "eventtype", }, { "Empty suggestions when no namespace flag", "", knParams, nil, "", "eventtype", }, { "Suggestions when test-ns namespace set", testNs, knParams, nil, "", "eventtype", }, { "Empty suggestions when toComplete is not a prefix", testNs, knParams, nil, "xyz", "eventtype", }, { "Empty suggestions when error during list operation", errorNs, knParams, nil, "", "eventtype", }, } for _, tt := range tests { cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") t.Run(tt.name, func(t *testing.T) { config := &completionConfig{ params: tt.p, command: cmd, args: tt.args, toComplete: tt.toComplete, } expectedFunc := resourceToFuncMap[tt.resource] if expectedFunc == nil { expectedFunc = func(config *completionConfig) []string { return []string{} } } cmd.Flags().Set("namespace", tt.namespace) actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) expectedSuggestions := expectedFunc(config) expectedDirective := cobra.ShellCompDirectiveNoFileComp assert.DeepEqual(t, actualSuggestions, expectedSuggestions) assert.Equal(t, actualDirective, expectedDirective) }) } } func getResourceCommandWithTestSubcommand(resource string, addNamespace, addSubcommand bool) *cobra.Command { testCommand := &cobra.Command{ Use: resource, } testSubCommand := &cobra.Command{ Use: "test", } if addSubcommand { testCommand.AddCommand(testSubCommand) } if addNamespace { AddNamespaceFlags(testCommand.Flags(), true) AddNamespaceFlags(testSubCommand.Flags(), true) } return testSubCommand } func setupTempDir(t *testing.T) string { tempDir := t.TempDir() svcPath := path.Join(tempDir, "test-ns", "ksvc") err := os.MkdirAll(svcPath, 0700) assert.NilError(t, err) for i, testSvc := range []servingv1.Service{testSvc1, testSvc2, testSvc3} { tempFile, err := os.Create(path.Join(svcPath, fmt.Sprintf("test-svc-%d.yaml", i+1))) assert.NilError(t, err) writeToFile(t, testSvc, tempFile) } return tempDir } func writeToFile(t *testing.T, testSvc servingv1.Service, tempFile *os.File) { yamlPrinter, err := genericclioptions.NewJSONYamlPrintFlags().ToPrinter("yaml") assert.NilError(t, err) err = yamlPrinter.PrintObj(&testSvc, tempFile) assert.NilError(t, err) defer tempFile.Close() }