From e352d94e45cdd7f92574ac02e37fc3ab78a34798 Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Tue, 14 Feb 2023 18:00:08 -0800 Subject: [PATCH] Refactor to use openapi fake client --- .../openapi/openapitest/fakeclient.go | 79 +++++++++++++++++++ .../kubectl/pkg/explain/v2/explain_test.go | 59 +++----------- 2 files changed, 91 insertions(+), 47 deletions(-) create mode 100644 staging/src/k8s.io/client-go/openapi/openapitest/fakeclient.go diff --git a/staging/src/k8s.io/client-go/openapi/openapitest/fakeclient.go b/staging/src/k8s.io/client-go/openapi/openapitest/fakeclient.go new file mode 100644 index 00000000000..ec3d84322ec --- /dev/null +++ b/staging/src/k8s.io/client-go/openapi/openapitest/fakeclient.go @@ -0,0 +1,79 @@ +/* +Copyright 2023 The Kubernetes 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 openapitest + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/openapi" +) + +// FakeClient implements openapi.Client interface, with hard-coded +// return values, including the possibility to force errors. +type FakeClient struct { + // Hard-coded paths to return from Paths() function. + PathsMap map[string]openapi.GroupVersion + // Hard-coded returned error. + ForcedErr error +} + +// Validate FakeClient implements openapi.Client interface. +var _ openapi.Client = &FakeClient{} + +// NewFakeClient returns a fake openapi client with an empty PathsMap. +func NewFakeClient() *FakeClient { + return &FakeClient{PathsMap: make(map[string]openapi.GroupVersion)} +} + +// Paths returns stored PathsMap field, creating an empty one if +// it does not already exist. If ForcedErr is set, this function +// returns the error instead. +func (f FakeClient) Paths() (map[string]openapi.GroupVersion, error) { + if f.ForcedErr != nil { + return nil, f.ForcedErr + } + return f.PathsMap, nil +} + +// FakeGroupVersion implements openapi.GroupVersion with hard-coded +// return GroupVersion specification bytes. If ForcedErr is set, then +// "Schema()" function returns the error instead of the GVSpec. +type FakeGroupVersion struct { + // Hard-coded GroupVersion specification + GVSpec []byte + // Hard-coded returned error. + ForcedErr error +} + +// FileOpenAPIGroupVersion implements the openapi.GroupVersion interface. +var _ openapi.GroupVersion = &FakeGroupVersion{} + +// Schema returns the hard-coded byte slice, including creating an +// empty slice if it has not been set yet. If the ForcedErr is set, +// this function returns the error instead of the GVSpec field. If +// content type other than application/json is passed, and error is +// returned. +func (f FakeGroupVersion) Schema(contentType string) ([]byte, error) { + if contentType != runtime.ContentTypeJSON { + return nil, fmt.Errorf("application/json is only content type supported: %s", contentType) + } + if f.ForcedErr != nil { + return nil, f.ForcedErr + } + return f.GVSpec, nil +} diff --git a/staging/src/k8s.io/kubectl/pkg/explain/v2/explain_test.go b/staging/src/k8s.io/kubectl/pkg/explain/v2/explain_test.go index a6d5a3bd3ea..327e3a65170 100644 --- a/staging/src/k8s.io/kubectl/pkg/explain/v2/explain_test.go +++ b/staging/src/k8s.io/kubectl/pkg/explain/v2/explain_test.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/openapi" "k8s.io/client-go/openapi/openapitest" ) @@ -39,22 +38,24 @@ var apiGroupsGVR = schema.GroupVersionResource{ func TestExplainErrors(t *testing.T) { var buf bytes.Buffer - // Validate error when "Paths()" returns error. - err := PrintModelDescription(nil, &buf, &forceErrorClient{}, apiGroupsGVR, false, "unknown-format") - require.ErrorContains(t, err, "failed to fetch list of groupVersions") - // Validate error when GVR is not found in returned paths map. - emptyClient := &emptyPathsClient{} - err = PrintModelDescription(nil, &buf, emptyClient, schema.GroupVersionResource{ + fakeClient := openapitest.NewFakeClient() + err := PrintModelDescription(nil, &buf, fakeClient, schema.GroupVersionResource{ Group: "test0.example.com", Version: "v1", Resource: "doesntmatter", }, false, "unknown-format") require.ErrorContains(t, err, "could not locate schema") + // Validate error when openapi client returns error. + fakeClient.ForcedErr = fmt.Errorf("Always fails") + err = PrintModelDescription(nil, &buf, fakeClient, apiGroupsGVR, false, "unknown-format") + require.ErrorContains(t, err, "failed to fetch list of groupVersions") + // Validate error when GroupVersion "Schema()" call returns error. - fakeClient := &fakeOpenAPIClient{values: make(map[string]openapi.GroupVersion)} - fakeClient.values["apis/test1.example.com/v1"] = &forceErrorGV{} + fakeClient = openapitest.NewFakeClient() + forceErrorGV := openapitest.FakeGroupVersion{ForcedErr: fmt.Errorf("Always fails")} + fakeClient.PathsMap["apis/test1.example.com/v1"] = &forceErrorGV err = PrintModelDescription(nil, &buf, fakeClient, schema.GroupVersionResource{ Group: "test1.example.com", Version: "v1", @@ -63,7 +64,8 @@ func TestExplainErrors(t *testing.T) { require.ErrorContains(t, err, "failed to fetch openapi schema ") // Validate error when returned bytes from GroupVersion "Schema" are invalid. - fakeClient.values["apis/test2.example.com/v1"] = &parseErrorGV{} + parseErrorGV := openapitest.FakeGroupVersion{GVSpec: []byte(``)} + fakeClient.PathsMap["apis/test2.example.com/v1"] = &parseErrorGV err = PrintModelDescription(nil, &buf, fakeClient, schema.GroupVersionResource{ Group: "test2.example.com", Version: "v1", @@ -113,40 +115,3 @@ func TestExplainOpenAPIClient(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedContext, actualContext) } - -// forceErrorClient always returns an error for "Paths()". -type forceErrorClient struct{} - -func (f *forceErrorClient) Paths() (map[string]openapi.GroupVersion, error) { - return nil, fmt.Errorf("Always fails") -} - -// emptyPathsClient returns an empty map for "Paths()". -type emptyPathsClient struct{} - -func (f *emptyPathsClient) Paths() (map[string]openapi.GroupVersion, error) { - return map[string]openapi.GroupVersion{}, nil -} - -// fakeOpenAPIClient returns hard-coded map for "Paths()". -type fakeOpenAPIClient struct { - values map[string]openapi.GroupVersion -} - -func (f *fakeOpenAPIClient) Paths() (map[string]openapi.GroupVersion, error) { - return f.values, nil -} - -// forceErrorGV always returns an error for "Schema()". -type forceErrorGV struct{} - -func (f *forceErrorGV) Schema(contentType string) ([]byte, error) { - return nil, fmt.Errorf("Always fails") -} - -// parseErrorGV always returns invalid JSON for "Schema()". -type parseErrorGV struct{} - -func (f *parseErrorGV) Schema(contentType string) ([]byte, error) { - return []byte(``), nil -}