github.com/oam-dev/kubevela@v1.9.11/pkg/cue/script/template_test.go (about) 1 /* 2 Copyright 2022 The KubeVela 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 script 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 ) 27 28 var templateScript = ` 29 metadata: { 30 name: "helm-repository" 31 alias: "Helm Repository" 32 scope: "system" 33 sensitive: false 34 } 35 template: { 36 output: { 37 url: parameter.url 38 } 39 parameter: { 40 // +usage=The public url of the helm chart repository. 41 url: string 42 // +usage=The username of basic auth repo. 43 username: string 44 // +usage=The password of basic auth repo. 45 password?: string 46 // +usage=The ca certificate of helm repository. Please encode this data with base64. 47 caFile?: string 48 49 options: "o1" | "o2" 50 } 51 } 52 ` 53 54 var templateWithContextScript = ` 55 import ( 56 "strconv" 57 ) 58 metadata: { 59 name: "helm-repository" 60 alias: "Helm Repository" 61 scope: "system" 62 sensitive: false 63 } 64 template: { 65 output: { 66 url: parameter.url 67 name: context.name 68 namespace: context.namespace 69 sensitive: strconv.FormatBool(metadata.sensitive) 70 } 71 parameter: { 72 // +usage=The public url of the helm chart repository. 73 url: string 74 // +usage=The username of basic auth repo. 75 username: string 76 // +usage=The password of basic auth repo. 77 password?: string 78 // +usage=The ca certificate of helm repository. Please encode this data with base64. 79 caFile?: string 80 } 81 } 82 ` 83 84 var withPackage = ` 85 86 package main 87 88 const: { 89 // +usage=The name of the addon application 90 name: "addon-loki" 91 } 92 93 outputs: { 94 a: context.appName 95 } 96 97 parameter: { 98 99 // global parameters 100 101 // +usage=The namespace of the loki to be installed 102 namespace: *"o11y-system" | string 103 // +usage=The clusters to install 104 clusters?: [...string] 105 106 // loki parameters 107 108 // +usage=Specify the image of loki 109 image: *"grafana/loki" | string 110 // +usage=Specify the imagePullPolicy of the image 111 imagePullPolicy: *"IfNotPresent" | "Never" | "Always" 112 // +usage=Specify the service type for expose loki. If empty, it will be not exposed. 113 serviceType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "" 114 // +usage=Specify the storage size to use. If empty, emptyDir will be used. Otherwise pvc will be used. 115 storage?: =~"^([1-9][0-9]{0,63})(E|P|T|G|M|K|Ei|Pi|Ti|Gi|Mi|Ki)$" 116 // +usage=Specify the storage class to use. 117 storageClassName?: string 118 119 // agent parameters 120 121 // +usage=Specify the type of log agents, if empty, no agent will be installed 122 agent: *"" | "vector" | "promtail" 123 // +usage=Specify the image of promtail 124 promtailImage: *"grafana/promtail" | string 125 // +usage=Specify the image of vector 126 vectorImage: *"timberio/vector:0.24.0-distroless-libc" | string 127 } 128 ` 129 130 var withImport = ` 131 import ( 132 "vela/op" 133 ) 134 135 apply: op.#Apply & { 136 value: parameter.value 137 cluster: parameter.cluster 138 } 139 parameter: { 140 // +usage=Specify the value of the object 141 value: {...} 142 // +usage=Specify the cluster of the object 143 cluster: *"" | string 144 }` 145 146 var withTemplate = ` 147 metadata: { 148 name: "xxx" 149 } 150 template: { 151 output: { 152 name: metadata.name 153 appName: context.appName 154 value: parameter.value 155 } 156 parameter: { 157 // +usage=Specify the value of the object 158 value: {...} 159 // +usage=Specify the cluster of the object 160 cluster: *"" | string 161 } 162 } 163 ` 164 165 func TestMergeValues(t *testing.T) { 166 var cueScript = CUE(templateScript) 167 value, err := cueScript.MergeValues(nil, map[string]interface{}{ 168 "url": "hub.docker.com", 169 "username": "name", 170 }) 171 assert.Equal(t, err, nil) 172 output, err := value.LookupValue("template", "output") 173 assert.Equal(t, err, nil) 174 var data = map[string]interface{}{} 175 err = output.UnmarshalTo(&data) 176 assert.Equal(t, err, nil) 177 assert.Equal(t, data["url"], "hub.docker.com") 178 } 179 180 func TestRunAndOutput(t *testing.T) { 181 var cueScript = BuildCUEScriptWithDefaultContext([]byte("context:{namespace:string \n name:string}"), []byte(templateWithContextScript)) 182 output, err := cueScript.RunAndOutput(map[string]interface{}{ 183 "name": "nnn", 184 "namespace": "ns", 185 }, map[string]interface{}{ 186 "url": "hub.docker.com", 187 "username": "test", 188 "password": "test", 189 "caFile": "test ca", 190 }) 191 assert.Equal(t, err, nil) 192 var data = map[string]interface{}{} 193 err = output.UnmarshalTo(&data) 194 assert.Equal(t, err, nil) 195 assert.Equal(t, data["name"], "nnn") 196 assert.Equal(t, data["namespace"], "ns") 197 assert.Equal(t, data["url"], "hub.docker.com") 198 } 199 200 func TestRunAndOutputWithCueX(t *testing.T) { 201 var cueScript = BuildCUEScriptWithDefaultContext([]byte("context:{namespace:string \n name:string}"), []byte(templateWithContextScript)) 202 output, err := cueScript.RunAndOutputWithCueX(context.Background(), map[string]interface{}{ 203 "name": "nnn", 204 "namespace": "ns", 205 }, map[string]interface{}{ 206 "url": "hub.docker.com", 207 "username": "test", 208 "password": "test", 209 "caFile": "test ca", 210 }, "template", "output") 211 assert.Equal(t, err, nil) 212 var data = map[string]interface{}{} 213 err = output.Decode(&data) 214 assert.Equal(t, err, nil) 215 assert.Equal(t, data["name"], "nnn") 216 assert.Equal(t, data["namespace"], "ns") 217 assert.Equal(t, data["url"], "hub.docker.com") 218 } 219 220 func TestValidateProperties(t *testing.T) { 221 var cueScript = CUE(templateScript) 222 // miss the required parameter 223 err := cueScript.ValidateProperties(map[string]interface{}{ 224 "url": "hub.docker.com", 225 }) 226 assert.Equal(t, err.(*ParameterError).Message, "This parameter is required") 227 228 // wrong the parameter value type 229 err = cueScript.ValidateProperties(map[string]interface{}{ 230 "url": 1, 231 "username": "ddd", 232 }) 233 assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "conflicting values"), true) 234 assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "url"), true) 235 236 // wrong the parameter value 237 err = cueScript.ValidateProperties(map[string]interface{}{ 238 "url": "ddd", 239 "username": "ddd", 240 }) 241 assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "This parameter is required"), true) 242 assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true) 243 244 // wrong the parameter value and no required value 245 err = cueScript.ValidateProperties(map[string]interface{}{ 246 "url": "ddd", 247 "username": "ddd", 248 "options": "o3", 249 }) 250 fmt.Println(err.(*ParameterError).Message) 251 assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true) 252 assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "2 errors in empty disjunction"), true) 253 } 254 255 func TestValidatePropertiesWithCueX(t *testing.T) { 256 var cueScript = CUE(templateScript) 257 // miss the required parameter 258 err := cueScript.ValidatePropertiesWithCueX(map[string]interface{}{ 259 "url": "hub.docker.com", 260 }) 261 assert.Equal(t, err.(*ParameterError).Message, "This parameter is required") 262 263 // wrong the parameter value type 264 err = cueScript.ValidatePropertiesWithCueX(map[string]interface{}{ 265 "url": 1, 266 "username": "ddd", 267 }) 268 assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "conflicting values"), true) 269 assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "url"), true) 270 271 // wrong the parameter value 272 err = cueScript.ValidatePropertiesWithCueX(map[string]interface{}{ 273 "url": "ddd", 274 "username": "ddd", 275 }) 276 assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "This parameter is required"), true) 277 assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true) 278 279 // wrong the parameter value and no required value 280 err = cueScript.ValidatePropertiesWithCueX(map[string]interface{}{ 281 "url": "ddd", 282 "username": "ddd", 283 "options": "o3", 284 }) 285 fmt.Println(err.(*ParameterError).Message) 286 assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true) 287 assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "2 errors in empty disjunction"), true) 288 } 289 290 func TestParsePropertiesToSchema(t *testing.T) { 291 cue := CUE(withPackage) 292 schema, err := cue.ParsePropertiesToSchema() 293 assert.Equal(t, err, nil) 294 assert.Equal(t, len(schema.Properties), 10) 295 296 cue = CUE(withImport) 297 schema, err = cue.ParsePropertiesToSchema() 298 assert.Equal(t, err, nil) 299 assert.Equal(t, len(schema.Properties), 2) 300 301 cue = CUE([]byte(withTemplate)) 302 schema, err = cue.ParsePropertiesToSchema("template") 303 assert.Equal(t, err, nil) 304 assert.Equal(t, len(schema.Properties), 2) 305 } 306 307 func TestParsePropertiesToSchemaWithCueX(t *testing.T) { 308 cue := CUE([]byte(withPackage)) 309 schema, err := cue.ParsePropertiesToSchemaWithCueX("") 310 assert.Equal(t, err, nil) 311 assert.Equal(t, len(schema.Properties), 10) 312 313 cue = CUE([]byte(withTemplate)) 314 schema, err = cue.ParsePropertiesToSchemaWithCueX("template") 315 assert.Equal(t, err, nil) 316 assert.Equal(t, len(schema.Properties), 2) 317 }