From 4366720327c8ccefa079604c111c5e6e286619ed Mon Sep 17 00:00:00 2001 From: qingliu Date: Sun, 26 Mar 2023 14:54:28 +0800 Subject: [PATCH] test: add unit tests for `pkg/resolution/resource` fix #6429 --- pkg/resolution/common/interface.go | 63 ++++ pkg/resolution/resource/crd_resource.go | 8 +- pkg/resolution/resource/crd_resource_test.go | 321 +++++++++++++++++++ pkg/resolution/resource/name.go | 3 + pkg/resolution/resource/name_test.go | 115 +++++++ pkg/resolution/resource/request_test.go | 76 +++++ pkg/resolution/resource/resource.go | 31 +- test/resolution.go | 56 +++- 8 files changed, 648 insertions(+), 25 deletions(-) create mode 100644 pkg/resolution/common/interface.go create mode 100644 pkg/resolution/resource/crd_resource_test.go create mode 100644 pkg/resolution/resource/name_test.go create mode 100644 pkg/resolution/resource/request_test.go diff --git a/pkg/resolution/common/interface.go b/pkg/resolution/common/interface.go new file mode 100644 index 00000000000..b1c07165b35 --- /dev/null +++ b/pkg/resolution/common/interface.go @@ -0,0 +1,63 @@ +/* +Copyright 2023 The Tekton 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 common + +import ( + "context" + + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ResolverName is the type used for a resolver's name and is mostly +// used to ensure the function signatures that accept it are clear on the +// purpose for the given string. +type ResolverName string + +// Requester is the interface implemented by a type that knows how to +// submit requests for remote resources. +type Requester interface { + // Submit accepts the name of a resolver to submit a request to + // along with the request itself. + Submit(context.Context, ResolverName, Request) (ResolvedResource, error) +} + +// Request is implemented by any type that represents a single request +// for a remote resource. Implementing this interface gives the underlying +// type an opportunity to control properties such as whether the name of +// a request has particular properties, whether the request should be made +// to a specific namespace, and precisely which parameters should be included. +type Request interface { + Name() string + Namespace() string + Params() pipelinev1beta1.Params +} + +// OwnedRequest is implemented by any type implementing Request that also needs +// to express a Kubernetes OwnerRef relationship as part of the request being +// made. +type OwnedRequest interface { + OwnerRef() metav1.OwnerReference +} + +// ResolvedResource is implemented by any type that offers a read-only +// view of the data and metadata of a resolved remote resource. +type ResolvedResource interface { + Data() ([]byte, error) + Annotations() map[string]string + RefSource() *pipelinev1beta1.RefSource +} diff --git a/pkg/resolution/resource/crd_resource.go b/pkg/resolution/resource/crd_resource.go index 328be3508ea..f50eebe65c4 100644 --- a/pkg/resolution/resource/crd_resource.go +++ b/pkg/resolution/resource/crd_resource.go @@ -122,7 +122,13 @@ func appendOwnerReference(rr *v1beta1.ResolutionRequest, req Request) { } func ownerRefsAreEqual(a, b metav1.OwnerReference) bool { - return a.APIVersion == b.APIVersion && a.Kind == b.Kind && a.Name == b.Name && a.UID == b.UID && a.Controller == b.Controller + // pointers values cannot be directly compared. + if (a.Controller == nil && b.Controller != nil) || + (a.Controller != nil && b.Controller == nil) || + (*a.Controller != *b.Controller) { + return false + } + return a.APIVersion == b.APIVersion && a.Kind == b.Kind && a.Name == b.Name && a.UID == b.UID } // readOnlyResolutionRequest is an opaque wrapper around ResolutionRequest diff --git a/pkg/resolution/resource/crd_resource_test.go b/pkg/resolution/resource/crd_resource_test.go new file mode 100644 index 00000000000..da5a06fac38 --- /dev/null +++ b/pkg/resolution/resource/crd_resource_test.go @@ -0,0 +1,321 @@ +/* +Copyright 2023 The Tekton 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 resource_test + +import ( + "context" + "encoding/base64" + "errors" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1" + ttesting "github.com/tektoncd/pipeline/pkg/reconciler/testing" + resolutioncommon "github.com/tektoncd/pipeline/pkg/resolution/common" + "github.com/tektoncd/pipeline/pkg/resolution/resource" + "github.com/tektoncd/pipeline/test" + "github.com/tektoncd/pipeline/test/diff" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/logging" + _ "knative.dev/pkg/system/testing" // Setup system.Namespace() + "sigs.k8s.io/yaml" +) + +// getCRDRequester returns an instance of the CRDRequester that has been seeded with +// d, where d represents the state of the system (existing resources) needed for the test. +func getCRDRequester(t *testing.T, d test.Data) (test.Assets, func()) { + t.Helper() + return initializeCRDRequesterAssets(t, d) +} + +func initializeCRDRequesterAssets(t *testing.T, d test.Data) (test.Assets, func()) { + t.Helper() + ctx, _ := ttesting.SetupFakeContext(t) + ctx, cancel := context.WithCancel(ctx) + c, informers := test.SeedTestData(t, ctx, d) + + return test.Assets{ + Logger: logging.FromContext(ctx), + Clients: c, + Informers: informers, + Ctx: ctx, + }, cancel +} + +func TestCRDRequesterSubmit(t *testing.T) { + ownerRef := mustParseOwnerReference(t, ` +apiVersion: tekton.dev/v1beta1 +blockOwnerDeletion: true +controller: true +kind: TaskRun +name: git-clone +uid: 727019c3-4066-4d8b-919e-90660dfd8b55 +`) + request := mustParseRawRequest(t, ` +name: git-ec247f5592afcaefa8485e34d2bd80c6 +namespace: namespace +params: + - name: url + value: https://github.com/tektoncd/catalog + - name: revision + value: main + - name: pathInRepo + value: task/git-clone/0.6/git-clone.yaml +`) + baseRR := mustParseResolutionRequest(t, ` +kind: "ResolutionRequest" +apiVersion: "resolution.tekton.dev/v1beta1" +metadata: + name: "git-ec247f5592afcaefa8485e34d2bd80c6" + namespace: "namespace" + labels: + resolution.tekton.dev/type: "git" + ownerReferences: + - apiVersion: tekton.dev/v1beta1 + blockOwnerDeletion: true + controller: true + kind: TaskRun + name: git-clone + uid: 727019c3-4066-4d8b-919e-90660dfd8b55 +spec: + params: + - name: "url" + value: "https://github.com/tektoncd/catalog" + - name: "revision" + value: "main" + - name: "pathInRepo" + value: "task/git-clone/0.6/git-clone.yaml" +`) + createdRR := baseRR.DeepCopy() + // + unknownRR := baseRR.DeepCopy() + unknownRR.Status = *mustParseResolutionRequestStatus(t, ` +conditions: + - lastTransitionTime: "2023-03-26T10:31:29Z" + status: "Unknown" + type: Succeeded +`) + // + failedRR := baseRR.DeepCopy() + failedRR.Status = *mustParseResolutionRequestStatus(t, ` +conditions: + - lastTransitionTime: "2023-03-26T10:31:29Z" + status: "Failed" + type: Succeeded + message: "error message" +`) + // + successRR := baseRR.DeepCopy() + successRR.Status = *mustParseResolutionRequestStatus(t, ` +annotations: + resolution.tekton.dev/content-type: application/x-yaml + resolution.tekton.dev/path: task/git-clone/0.6/git-clone.yaml + resolution.tekton.dev/revision: main + resolution.tekton.dev/url: https://github.com/tektoncd/catalog +conditions: + - lastTransitionTime: "2023-03-26T10:31:29Z" + status: "True" + type: Succeeded + data: e30= +`) + // + successWithoutAnnotationsRR := baseRR.DeepCopy() + successWithoutAnnotationsRR.Status = *mustParseResolutionRequestStatus(t, ` +conditions: + - lastTransitionTime: "2023-03-26T10:31:29Z" + status: "True" + type: Succeeded + data: e30= +`) + + testCases := []struct { + name string + inputRequest *test.RawRequest + inputResolutionRequest *v1beta1.ResolutionRequest + expectedResolutionRequest *v1beta1.ResolutionRequest + expectedResolvedResource *v1beta1.ResolutionRequest + expectedErr error + }{ + { + name: "resolution request does not exist and needs to be created", + inputRequest: request, + inputResolutionRequest: nil, + expectedResolutionRequest: createdRR.DeepCopy(), + expectedResolvedResource: nil, + expectedErr: resolutioncommon.ErrRequestInProgress, + }, + { + name: "resolution request exist and status is unknown", + inputRequest: request, + inputResolutionRequest: unknownRR.DeepCopy(), + expectedResolutionRequest: nil, + expectedResolvedResource: nil, + expectedErr: resolutioncommon.ErrRequestInProgress, + }, + { + name: "resolution request exist and status is succeeded", + inputRequest: request, + inputResolutionRequest: successRR.DeepCopy(), + expectedResolutionRequest: nil, + expectedResolvedResource: successRR.DeepCopy(), + expectedErr: nil, + }, + { + name: "resolution request exist and status is succeeded but annotations is nil", + inputRequest: request, + inputResolutionRequest: successWithoutAnnotationsRR.DeepCopy(), + expectedResolutionRequest: nil, + expectedResolvedResource: successWithoutAnnotationsRR.DeepCopy(), + expectedErr: nil, + }, + { + name: "resolution request exist and status is failed", + inputRequest: request, + inputResolutionRequest: failedRR.DeepCopy(), + expectedResolutionRequest: nil, + expectedResolvedResource: nil, + expectedErr: resolutioncommon.NewError(resolutioncommon.ReasonResolutionFailed, errors.New("error message")), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + d := test.Data{} + if tc.inputResolutionRequest != nil { + d.ResolutionRequests = []*v1beta1.ResolutionRequest{tc.inputResolutionRequest} + } + + testAssets, cancel := getCRDRequester(t, d) + defer cancel() + ctx := testAssets.Ctx + clients := testAssets.Clients + + resolver := resolutioncommon.ResolverName("git") + crdRequester := resource.NewCRDRequester(clients.ResolutionRequests, testAssets.Informers.ResolutionRequest.Lister()) + requestWithOwner := &ownerRequest{ + Request: tc.inputRequest.Request(), + ownerRef: *ownerRef, + } + resolvedResource, err := crdRequester.Submit(ctx, resolver, requestWithOwner) + + // check the error + if err != nil || tc.expectedErr != nil { + if err == nil || tc.expectedErr == nil { + t.Errorf("expected error %v, but got %v", tc.expectedErr, err) + } else if err.Error() != tc.expectedErr.Error() { + t.Errorf("expected error %v, but got %v", tc.expectedErr, err) + } + } + + // check the resolved resource + switch { + case tc.expectedResolvedResource == nil: + // skipping check of resolved resources. + case tc.expectedResolvedResource != nil: + if resolvedResource == nil { + t.Errorf("expected resolved resource equal %v, but got %v", tc.expectedResolvedResource, resolvedResource) + break + } + rr := tc.expectedResolvedResource + data, err := base64.StdEncoding.Strict().DecodeString(rr.Status.Data) + if err != nil { + t.Errorf("unexpected error decoding expected resource data: %v", err) + } + expectedResolvedResource := test.NewResolvedResource(data, rr.Status.Annotations, rr.Status.RefSource, nil) + assertResolvedResourceEqual(t, expectedResolvedResource, resolvedResource) + } + + // check the resolution request + if tc.expectedResolutionRequest != nil { + resolutionrequest, err := clients.ResolutionRequests.ResolutionV1beta1(). + ResolutionRequests(tc.inputRequest.Namespace).Get(ctx, tc.inputRequest.Name, metav1.GetOptions{}) + if err != nil { + t.Errorf("unexpected error getting resource requests: %v", err) + } + if d := cmp.Diff(tc.expectedResolutionRequest, resolutionrequest); d != "" { + t.Errorf("expected resolution request to match %s", diff.PrintWantGot(d)) + } + } + }) + } +} + +type ownerRequest struct { + resolutioncommon.Request + ownerRef metav1.OwnerReference +} + +func (r *ownerRequest) OwnerRef() metav1.OwnerReference { + return r.ownerRef +} + +func mustParseRawRequest(t *testing.T, yamlStr string) *test.RawRequest { + t.Helper() + output := &test.RawRequest{} + if err := yaml.Unmarshal([]byte(yamlStr), output); err != nil { + t.Errorf("parsing raw request %s: %v", yamlStr, err) + } + return output +} + +func mustParseOwnerReference(t *testing.T, yamlStr string) *metav1.OwnerReference { + t.Helper() + output := &metav1.OwnerReference{} + if err := yaml.Unmarshal([]byte(yamlStr), output); err != nil { + t.Errorf("parsing owner reference %s: %v", yamlStr, err) + } + return output +} + +func mustParseResolutionRequest(t *testing.T, yamlStr string) *v1beta1.ResolutionRequest { + t.Helper() + output := &v1beta1.ResolutionRequest{} + if err := yaml.Unmarshal([]byte(yamlStr), output); err != nil { + t.Errorf("parsing resolution request %s: %v", yamlStr, err) + } + return output +} + +func mustParseResolutionRequestStatus(t *testing.T, yamlStr string) *v1beta1.ResolutionRequestStatus { + t.Helper() + output := &v1beta1.ResolutionRequestStatus{} + if err := yaml.Unmarshal([]byte(yamlStr), output); err != nil { + t.Errorf("parsing resolution request status %s: %v", yamlStr, err) + } + return output +} + +func assertResolvedResourceEqual(t *testing.T, expected, actual resolutioncommon.ResolvedResource) { + t.Helper() + expectedBytes, err := expected.Data() + if err != nil { + t.Errorf("unexpected error getting expected resource data: %v", err) + } + actualBytes, err := actual.Data() + if err != nil { + t.Errorf("unexpected error getting acutal resource data: %v", err) + } + if d := cmp.Diff(expectedBytes, actualBytes); d != "" { + t.Errorf("expected resolved resource Data to match %s", diff.PrintWantGot(d)) + } + if d := cmp.Diff(expected.Annotations(), actual.Annotations()); d != "" { + t.Errorf("expected resolved resource Annotations to match %s", diff.PrintWantGot(d)) + } + if d := cmp.Diff(expected.RefSource(), actual.RefSource()); d != "" { + t.Errorf("expected resolved resource Source to match %s", diff.PrintWantGot(d)) + } +} diff --git a/pkg/resolution/resource/name.go b/pkg/resolution/resource/name.go index 115b584c723..a48d0a4d5e3 100644 --- a/pkg/resolution/resource/name.go +++ b/pkg/resolution/resource/name.go @@ -42,6 +42,9 @@ func GenerateDeterministicName(prefix, base string, params v1beta1.Params) (stri } sortedParams := make(v1beta1.Params, len(params)) + for i := range params { + sortedParams[i] = *params[i].DeepCopy() + } sort.SliceStable(sortedParams, func(i, j int) bool { return sortedParams[i].Name < sortedParams[j].Name }) diff --git a/pkg/resolution/resource/name_test.go b/pkg/resolution/resource/name_test.go new file mode 100644 index 00000000000..07bc26d7cc2 --- /dev/null +++ b/pkg/resolution/resource/name_test.go @@ -0,0 +1,115 @@ +/* +Copyright 2023 The Tekton 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 resource_test + +import ( + "testing" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/resolution/resource" +) + +func TestGenerateDeterministicName(t *testing.T) { + type args struct { + prefix string + base string + params []v1beta1.Param + } + golden := args{ + prefix: "prefix", + base: "base", + params: []v1beta1.Param{ + { + Name: "string-param", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "value1", + }, + }, + { + Name: "array-param", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeArray, + ArrayVal: []string{"value1", "value2"}, + }, + }, + { + Name: "object-param", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeObject, + ObjectVal: map[string]string{"key": "value"}, + }, + }, + }, + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "only contains prefix", + args: args{ + prefix: golden.prefix, + }, + want: "prefix-6c62272e07bb014262b821756295c58d", + }, + { + name: "only contains base", + args: args{ + base: golden.base, + }, + want: "-6989337ae0757277b806e97e86444ef0", + }, + { + name: "only contains params", + args: args{ + params: golden.params, + }, + want: "-52921b17d3c2930a34419c618d6af0e9", + }, + { + name: "params with different order should generate same hash", + args: args{ + params: []v1beta1.Param{ + golden.params[2], + golden.params[1], + golden.params[0], + }, + }, + want: "-52921b17d3c2930a34419c618d6af0e9", + }, + { + name: "contain all fields", + args: golden, + want: "prefix-ba2f256f318de7f4154da577c283cb9e", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := resource.GenerateDeterministicName(tt.args.prefix, tt.args.base, tt.args.params) + if (err != nil) != tt.wantErr { + t.Errorf("GenerateDeterministicName() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GenerateDeterministicName() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/resolution/resource/request_test.go b/pkg/resolution/resource/request_test.go new file mode 100644 index 00000000000..d7605a783a4 --- /dev/null +++ b/pkg/resolution/resource/request_test.go @@ -0,0 +1,76 @@ +/* +Copyright 2023 The Tekton 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 resource_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/resolution/resource" + "github.com/tektoncd/pipeline/test/diff" +) + +func TestNewRequest(t *testing.T) { + type args struct { + name string + namespace string + params v1beta1.Params + } + type want = args + golden := args{ + name: "test-name", + namespace: "test-namespace", + params: v1beta1.Params{ + {Name: "param1", Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeString, StringVal: "value1"}}, + {Name: "param2", Value: v1beta1.ParamValue{Type: v1beta1.ParamTypeString, StringVal: "value2"}}, + }, + } + tests := []struct { + name string + args args + want want + }{ + { + name: "empty", + args: args{}, + want: want{}, + }, + { + name: "all", + args: golden, + want: golden, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + request := resource.NewRequest(tt.args.name, tt.args.namespace, tt.args.params) + if request == nil { + t.Errorf("NewRequest() return nil") + } + if request.Name() != tt.want.name { + t.Errorf("NewRequest().Name() = %v, want %v", request.Name(), tt.want.name) + } + if request.Namespace() != tt.want.namespace { + t.Errorf("NewRequest().Namespace() = %v, want %v", request.Namespace(), tt.want.namespace) + } + if d := cmp.Diff(request.Params(), tt.want.params); d != "" { + t.Errorf("expected params to match %s", diff.PrintWantGot(d)) + } + }) + } +} diff --git a/pkg/resolution/resource/resource.go b/pkg/resolution/resource/resource.go index b18b9d7d4d7..9952cdbf65b 100644 --- a/pkg/resolution/resource/resource.go +++ b/pkg/resolution/resource/resource.go @@ -17,47 +17,32 @@ limitations under the License. package resource import ( - "context" - - pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/tektoncd/pipeline/pkg/resolution/common" ) +// This is an alias for avoiding cycle import + // ResolverName is the type used for a resolver's name and is mostly // used to ensure the function signatures that accept it are clear on the // purpose for the given string. -type ResolverName string +type ResolverName = common.ResolverName // Requester is the interface implemented by a type that knows how to // submit requests for remote resources. -type Requester interface { - // Submit accepts the name of a resolver to submit a request to - // along with the request itself. - Submit(context.Context, ResolverName, Request) (ResolvedResource, error) -} +type Requester = common.Requester // Request is implemented by any type that represents a single request // for a remote resource. Implementing this interface gives the underlying // type an opportunity to control properties such as whether the name of // a request has particular properties, whether the request should be made // to a specific namespace, and precisely which parameters should be included. -type Request interface { - Name() string - Namespace() string - Params() pipelinev1beta1.Params -} +type Request = common.Request // OwnedRequest is implemented by any type implementing Request that also needs // to express a Kubernetes OwnerRef relationship as part of the request being // made. -type OwnedRequest interface { - OwnerRef() metav1.OwnerReference -} +type OwnedRequest = common.OwnedRequest // ResolvedResource is implemented by any type that offers a read-only // view of the data and metadata of a resolved remote resource. -type ResolvedResource interface { - Data() ([]byte, error) - Annotations() map[string]string - RefSource() *pipelinev1beta1.RefSource -} +type ResolvedResource = common.ResolvedResource diff --git a/test/resolution.go b/test/resolution.go index d136d0e19a3..7d46d6ac100 100644 --- a/test/resolution.go +++ b/test/resolution.go @@ -24,7 +24,7 @@ import ( "github.com/google/go-cmp/cmp" pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" - resolution "github.com/tektoncd/pipeline/pkg/resolution/resource" + resolution "github.com/tektoncd/pipeline/pkg/resolution/common" "github.com/tektoncd/pipeline/test/diff" ) @@ -122,3 +122,57 @@ func (r *ResolvedResource) Annotations() map[string]string { func (r *ResolvedResource) RefSource() *pipelinev1beta1.RefSource { return r.ResolvedRefSource } + +// RawRequest stores the raw request data +type RawRequest struct { + // the request name + Name string + // the request namespace + Namespace string + // the params for the request + Params []pipelinev1beta1.Param +} + +// Request returns a Request interface based on the RawRequest. +func (r *RawRequest) Request() resolution.Request { + if r == nil { + r = &RawRequest{} + } + return &Request{ + RawRequest: *r, + } +} + +// Request implements resolution.Request and makes it easier to mock input for submit +// Using inline structs is to avoid conflicts between field names and method names. +type Request struct { + RawRequest +} + +var _ resolution.Request = &Request{} + +// NewRequest creates a mock request that is populated with the given name namespace and params +func NewRequest(name, namespace string, params []pipelinev1beta1.Param) *Request { + return &Request{ + RawRequest: RawRequest{ + Name: name, + Namespace: namespace, + Params: params, + }, + } +} + +// Name implements resolution.Request and returns the mock name given to it on initialization. +func (r *Request) Name() string { + return r.RawRequest.Name +} + +// Namespace implements resolution.Request and returns the mock namespace given to it on initialization. +func (r *Request) Namespace() string { + return r.RawRequest.Namespace +} + +// Params implements resolution.Request and returns the mock params given to it on initialization. +func (r *Request) Params() pipelinev1beta1.Params { + return r.RawRequest.Params +}