sigs.k8s.io/controller-tools@v0.15.1-0.20240515195456-85686cb69316/pkg/crd/schema_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes 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 crd 18 19 import ( 20 "go/ast" 21 "strings" 22 "testing" 23 24 "github.com/onsi/gomega" 25 "golang.org/x/tools/go/packages" 26 pkgstest "golang.org/x/tools/go/packages/packagestest" 27 apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 28 crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers" 29 testloader "sigs.k8s.io/controller-tools/pkg/loader/testutils" 30 "sigs.k8s.io/controller-tools/pkg/markers" 31 ) 32 33 func transform(t *testing.T, expr string) *apiext.JSONSchemaProps { 34 // this is *very* hacky but I haven’t found a simple way 35 // to get an ast.Expr with all the associated metadata required 36 // to run typeToSchema upon it: 37 38 moduleName := "sigs.k8s.io/controller-tools/pkg/crd" 39 modules := []pkgstest.Module{ 40 { 41 Name: moduleName, 42 Files: map[string]interface{}{ 43 "test.go": ` 44 package crd 45 type Test ` + expr, 46 }, 47 }, 48 } 49 50 pkgs, exported, err := testloader.LoadFakeRoots(pkgstest.Modules, modules, moduleName) 51 if exported != nil { 52 t.Cleanup(exported.Cleanup) 53 } 54 55 if err != nil { 56 t.Fatalf("unable to load fake package: %s", err) 57 } 58 59 if len(pkgs) != 1 { 60 t.Fatal("expected to parse only one package") 61 } 62 63 pkg := pkgs[0] 64 pkg.NeedTypesInfo() 65 failIfErrors(t, pkg.Errors) 66 67 schemaContext := newSchemaContext(pkg, nil, true, false).ForInfo(&markers.TypeInfo{}) 68 // yick: grab the only type definition 69 definedType := pkg.Syntax[0].Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Type 70 result := typeToSchema(schemaContext, definedType) 71 failIfErrors(t, pkg.Errors) 72 return result 73 } 74 75 func failIfErrors(t *testing.T, errs []packages.Error) { 76 if len(errs) > 0 { 77 var msgs []string 78 for _, e := range errs { 79 msgs = append(msgs, e.Msg) 80 } 81 82 t.Fatalf("error loading fake package: %s", strings.Join(msgs, "; ")) 83 } 84 } 85 86 var arrayOfNumbersSchema *apiext.JSONSchemaProps = &apiext.JSONSchemaProps{ 87 Type: "array", 88 Items: &apiext.JSONSchemaPropsOrArray{ 89 Schema: &apiext.JSONSchemaProps{ 90 Type: "number", 91 }, 92 }, 93 } 94 95 func Test_Schema_ArrayOfFloat32(t *testing.T) { 96 g := gomega.NewWithT(t) 97 98 output := transform(t, "[]float32") 99 g.Expect(output).To(gomega.Equal(arrayOfNumbersSchema)) 100 } 101 102 func Test_Schema_MapOfStringToArrayOfFloat32(t *testing.T) { 103 g := gomega.NewWithT(t) 104 105 output := transform(t, "map[string][]float32") 106 g.Expect(output).To(gomega.Equal(&apiext.JSONSchemaProps{ 107 Type: "object", 108 AdditionalProperties: &apiext.JSONSchemaPropsOrBool{ 109 Allows: true, 110 Schema: arrayOfNumbersSchema, 111 }, 112 })) 113 } 114 115 func Test_Schema_ApplyMarkers(t *testing.T) { 116 g := gomega.NewWithT(t) 117 118 props := &apiext.JSONSchemaProps{} 119 ctx := &schemaContext{} 120 121 var invocations []string 122 123 applyMarkers(ctx, markers.MarkerValues{ 124 "blah": []interface{}{ 125 &testPriorityMarker{ 126 priority: 0, callback: func() { 127 invocations = append(invocations, "0") 128 }, 129 }, 130 &testPriorityMarker{priority: 2, callback: func() { 131 invocations = append(invocations, "2") 132 }}, 133 &testPriorityMarker{priority: 11, callback: func() { 134 invocations = append(invocations, "11") 135 }}, 136 &defaultPriorityMarker{callback: func() { 137 invocations = append(invocations, "default") 138 }}, 139 &testapplyFirstMarker{callback: func() { 140 invocations = append(invocations, "applyFirst") 141 }}, 142 }}, props, nil) 143 144 g.Expect(invocations).To(gomega.Equal([]string{"0", "applyFirst", "2", "default", "11"})) 145 } 146 147 type defaultPriorityMarker struct { 148 callback func() 149 } 150 151 func (m *defaultPriorityMarker) ApplyToSchema(*apiext.JSONSchemaProps) error { 152 m.callback() 153 return nil 154 } 155 156 type testPriorityMarker struct { 157 priority crdmarkers.ApplyPriority 158 callback func() 159 } 160 161 func (m *testPriorityMarker) ApplyPriority() crdmarkers.ApplyPriority { 162 return m.priority 163 } 164 165 func (m *testPriorityMarker) ApplyToSchema(*apiext.JSONSchemaProps) error { 166 m.callback() 167 return nil 168 } 169 170 type testapplyFirstMarker struct { 171 callback func() 172 } 173 174 func (m *testapplyFirstMarker) ApplyFirst() {} 175 func (m *testapplyFirstMarker) ApplyToSchema(*apiext.JSONSchemaProps) error { 176 m.callback() 177 return nil 178 }