k8s.io/apiserver@v0.31.1/pkg/cel/openapi/compiling_test.go (about) 1 /* 2 Copyright 2023 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 openapi 18 19 import ( 20 "testing" 21 22 "github.com/google/cel-go/cel" 23 "github.com/google/cel-go/common/types" 24 "github.com/google/cel-go/interpreter" 25 26 "k8s.io/apimachinery/pkg/util/version" 27 apiservercel "k8s.io/apiserver/pkg/cel" 28 "k8s.io/apiserver/pkg/cel/common" 29 "k8s.io/apiserver/pkg/cel/environment" 30 "k8s.io/kube-openapi/pkg/validation/spec" 31 ) 32 33 func TestMultipleTypes(t *testing.T) { 34 env, err := buildTestEnv() 35 if err != nil { 36 t.Fatal(err) 37 } 38 for _, tc := range []struct { 39 expression string 40 expectCompileError bool 41 expectEvalResult bool 42 }{ 43 { 44 expression: "foo.foo == bar.bar", 45 expectEvalResult: true, 46 }, 47 { 48 expression: "foo.bar == 'value'", 49 expectCompileError: true, 50 }, 51 { 52 expression: "foo.foo == 'value'", 53 expectEvalResult: true, 54 }, 55 { 56 expression: "bar.bar == 'value'", 57 expectEvalResult: true, 58 }, 59 { 60 expression: "foo.common + bar.common <= 2", 61 expectEvalResult: false, // 3 > 2 62 }, 63 { 64 expression: "foo.confusion == bar.confusion", 65 expectCompileError: true, 66 }, 67 } { 68 t.Run(tc.expression, func(t *testing.T) { 69 ast, issues := env.Compile(tc.expression) 70 if issues != nil { 71 if tc.expectCompileError { 72 return 73 } 74 t.Fatalf("compile error: %v", issues) 75 } 76 if issues != nil { 77 t.Fatal(issues) 78 } 79 p, err := env.Program(ast) 80 if err != nil { 81 t.Fatal(err) 82 } 83 ret, _, err := p.Eval(&simpleActivation{ 84 foo: map[string]any{"foo": "value", "common": 1, "confusion": "114514"}, 85 bar: map[string]any{"bar": "value", "common": 2, "confusion": 114514}, 86 }) 87 if err != nil { 88 t.Fatal(err) 89 } 90 if ret.Type() != types.BoolType { 91 t.Errorf("bad result type: %v", ret.Type()) 92 } 93 if res := ret.Value().(bool); tc.expectEvalResult != res { 94 t.Errorf("expectEvalResult expression evaluates to %v, got %v", tc.expectEvalResult, res) 95 } 96 }) 97 } 98 99 } 100 101 // buildTestEnv sets up an environment that contains two variables, "foo" and 102 // "bar". 103 // foo is an object with a string field "foo", an integer field "common", and a string field "confusion" 104 // bar is an object with a string field "bar", an integer field "common", and an integer field "confusion" 105 func buildTestEnv() (*cel.Env, error) { 106 fooType := common.SchemaDeclType(simpleMapSchema("foo", spec.StringProperty()), true).MaybeAssignTypeName("fooType") 107 barType := common.SchemaDeclType(simpleMapSchema("bar", spec.Int64Property()), true).MaybeAssignTypeName("barType") 108 109 env, err := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true).Extend( 110 environment.VersionedOptions{ 111 IntroducedVersion: version.MajorMinor(1, 26), 112 EnvOptions: []cel.EnvOption{ 113 cel.Variable("foo", fooType.CelType()), 114 cel.Variable("bar", barType.CelType()), 115 }, 116 DeclTypes: []*apiservercel.DeclType{ 117 fooType, 118 barType, 119 }, 120 }, 121 ) 122 if err != nil { 123 return nil, err 124 } 125 return env.Env(environment.NewExpressions) 126 } 127 128 func simpleMapSchema(fieldName string, confusionSchema *spec.Schema) common.Schema { 129 return &Schema{Schema: &spec.Schema{ 130 SchemaProps: spec.SchemaProps{ 131 Type: []string{"object"}, 132 Properties: map[string]spec.Schema{ 133 fieldName: *spec.StringProperty(), 134 "common": *spec.Int64Property(), 135 "confusion": *confusionSchema, 136 }, 137 }, 138 }} 139 } 140 141 type simpleActivation struct { 142 foo any 143 bar any 144 } 145 146 func (a *simpleActivation) ResolveName(name string) (interface{}, bool) { 147 switch name { 148 case "foo": 149 return a.foo, true 150 case "bar": 151 return a.bar, true 152 default: 153 return nil, false 154 } 155 } 156 157 func (a *simpleActivation) Parent() interpreter.Activation { 158 return nil 159 }