k8s.io/apiserver@v0.31.1/pkg/cel/mutation/unstructured/typeresolver_test.go (about) 1 /* 2 Copyright 2024 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 unstructured 18 19 import ( 20 "reflect" 21 "testing" 22 23 "github.com/google/cel-go/cel" 24 25 "k8s.io/apimachinery/pkg/util/version" 26 "k8s.io/apiserver/pkg/cel/environment" 27 "k8s.io/apiserver/pkg/cel/mutation" 28 ) 29 30 func TestTypeProvider(t *testing.T) { 31 for _, tc := range []struct { 32 name string 33 expression string 34 expectedValue any 35 }{ 36 { 37 name: "not an object", 38 expression: `string(114514)`, 39 expectedValue: "114514", 40 }, 41 { 42 name: "empty", 43 expression: "Object{}", 44 expectedValue: map[string]any{}, 45 }, 46 { 47 name: "Object.spec", 48 expression: "Object{spec: Object.spec{replicas: 3}}", 49 expectedValue: map[string]any{ 50 "spec": map[string]any{ 51 // an integer maps to int64 52 "replicas": int64(3), 53 }, 54 }, 55 }, 56 { 57 // list literal does not require new path code of the type provider 58 // comparing to the object literal. 59 // This test case serves as a note of "supported syntax" 60 name: "Object.spec.template.containers", 61 expression: `Object{ 62 spec: Object.spec{ 63 template: Object.spec.template{ 64 containers: [ 65 Object.spec.template.containers.item{ 66 name: "nginx", 67 image: "nginx", 68 args: ["-g"] 69 } 70 ] 71 } 72 } 73 }`, 74 expectedValue: map[string]any{ 75 "spec": map[string]any{ 76 "template": map[string]any{ 77 "containers": []any{ 78 map[string]any{ 79 "name": "nginx", 80 "image": "nginx", 81 "args": []any{"-g"}, 82 }, 83 }, 84 }, 85 }, 86 }, 87 }, 88 { 89 name: "list of ints", 90 expression: `Object{ 91 intList: [1, 2, 3] 92 }`, 93 expectedValue: map[string]any{ 94 "intList": []any{int64(1), int64(2), int64(3)}, 95 }, 96 }, 97 { 98 name: "map string-to-string", 99 expression: `Object{ 100 annotations: {"foo": "bar"} 101 }`, 102 expectedValue: map[string]any{ 103 "annotations": map[string]any{ 104 "foo": "bar", 105 }, 106 }, 107 }, 108 { 109 name: "field access", 110 expression: `Object{ 111 intList: [1, 2, 3] 112 }.intList.sum()`, 113 expectedValue: int64(6), 114 }, 115 { 116 name: "equality check", 117 expression: "Object{spec: Object.spec{replicas: 3}} == Object{spec: Object.spec{replicas: 1 + 2}}", 118 expectedValue: true, 119 }, 120 } { 121 t.Run(tc.name, func(t *testing.T) { 122 _, option := mutation.NewTypeProviderAndEnvOption(&TypeResolver{}) 123 env := mustCreateEnv(t, option) 124 ast, issues := env.Compile(tc.expression) 125 if issues != nil { 126 t.Fatalf("unexpected issues during compilation: %v", issues) 127 } 128 program, err := env.Program(ast) 129 if err != nil { 130 t.Fatalf("unexpected error while creating program: %v", err) 131 } 132 r, _, err := program.Eval(map[string]any{}) 133 if err != nil { 134 t.Fatalf("unexpected error during evaluation: %v", err) 135 } 136 if v := r.Value(); !reflect.DeepEqual(v, tc.expectedValue) { 137 t.Errorf("expected %v but got %v", tc.expectedValue, v) 138 } 139 }) 140 } 141 } 142 func mustCreateEnv(t testing.TB, envOptions ...cel.EnvOption) *cel.Env { 143 envSet, err := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true). 144 Extend(environment.VersionedOptions{ 145 IntroducedVersion: version.MajorMinor(1, 30), 146 EnvOptions: envOptions, 147 }) 148 if err != nil { 149 t.Fatalf("fail to create env set: %v", err) 150 } 151 env, err := envSet.Env(environment.StoredExpressions) 152 if err != nil { 153 t.Fatalf("fail to setup env: %v", env) 154 } 155 return env 156 }