knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/testing/testing.go (about) 1 /* 2 Copyright 2019 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package testing 18 19 import ( 20 "encoding/json" 21 "sort" 22 "strings" 23 "testing" 24 25 "github.com/google/go-cmp/cmp" 26 "github.com/google/go-cmp/cmp/cmpopts" 27 jsonpatch "gomodules.xyz/jsonpatch/v2" 28 admissionv1 "k8s.io/api/admission/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "knative.dev/pkg/system" 31 pkgtest "knative.dev/pkg/testing" 32 33 // Makes system.Namespace work in tests. 34 _ "knative.dev/pkg/system/testing" 35 ) 36 37 // CreateResource creates a testing.Resource with the given name in the system namespace. 38 func CreateResource(name string) *pkgtest.Resource { 39 return &pkgtest.Resource{ 40 TypeMeta: metav1.TypeMeta{ 41 Kind: "Resource", 42 APIVersion: "v1", 43 }, 44 ObjectMeta: metav1.ObjectMeta{ 45 Namespace: system.Namespace(), 46 Name: name, 47 }, 48 Spec: pkgtest.ResourceSpec{ 49 FieldWithValidation: "magic value", 50 }, 51 } 52 } 53 54 // ExpectAllowed checks that a given admission response allows the initiating request through. 55 func ExpectAllowed(t *testing.T, resp *admissionv1.AdmissionResponse) { 56 t.Helper() 57 if !resp.Allowed { 58 t.Errorf("Expected allowed, but failed with %+v", resp.Result) 59 } 60 } 61 62 // ExpectFailsWith checks that a given admission response disallows the initiating request 63 // through and contains the provided string in its error message. 64 func ExpectFailsWith(t *testing.T, resp *admissionv1.AdmissionResponse, contains string) { 65 t.Helper() 66 if resp.Allowed { 67 t.Error("Expected denial, got allowed") 68 return 69 } 70 if !strings.Contains(resp.Result.Message, contains) { 71 t.Errorf("Expected failure containing %q got %q", contains, resp.Result.Message) 72 } 73 } 74 75 // ExpectWarnsWith checks that a given admission response warns on the initiating request 76 // containing the provided string in its warning message. 77 func ExpectWarnsWith(t *testing.T, resp *admissionv1.AdmissionResponse, contains string) { 78 t.Helper() 79 found := false 80 for _, warning := range resp.Warnings { 81 if strings.Contains(warning, contains) { 82 found = true 83 } 84 } 85 if !found { 86 t.Errorf("Expected warning containing %q got %v", contains, resp.Warnings) 87 } 88 } 89 90 // ExpectPatches checks that the provided serialized bytes consist of an expected 91 // collection of patches. This is used to verify the mutations made in a mutating 92 // admission webhook's response. 93 func ExpectPatches(t *testing.T, a []byte, e []jsonpatch.JsonPatchOperation) { 94 t.Helper() 95 var got []jsonpatch.JsonPatchOperation 96 97 err := json.Unmarshal(a, &got) 98 if err != nil { 99 t.Error("Failed to unmarshal patches:", err) 100 return 101 } 102 103 // Give the patch a deterministic ordering. 104 // Technically this can change the meaning, but the ordering is otherwise unstable 105 // and difficult to test. 106 sort.Slice(e, func(i, j int) bool { 107 lhs, rhs := e[i], e[j] 108 if lhs.Operation != rhs.Operation { 109 return lhs.Operation < rhs.Operation 110 } 111 return lhs.Path < rhs.Path 112 }) 113 sort.Slice(got, func(i, j int) bool { 114 lhs, rhs := got[i], got[j] 115 if lhs.Operation != rhs.Operation { 116 return lhs.Operation < rhs.Operation 117 } 118 return lhs.Path < rhs.Path 119 }) 120 121 // Even though diff is useful, seeing the whole objects 122 // one under another helps a lot. 123 t.Logf("Got Patches: %#v", got) 124 t.Logf("Want Patches: %#v", e) 125 if diff := cmp.Diff(e, got, cmpopts.EquateEmpty()); diff != "" { 126 t.Log("diff Patches:", diff) 127 t.Error("ExpectPatches (-want, +got) =", diff) 128 } 129 }