github.com/solo-io/cue@v0.4.7/internal/core/export/export_test.go (about) 1 // Copyright 2020 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package export_test 16 17 import ( 18 "testing" 19 20 "github.com/rogpeppe/go-internal/txtar" 21 "github.com/solo-io/cue/cue" 22 "github.com/solo-io/cue/cue/ast" 23 "github.com/solo-io/cue/cue/cuecontext" 24 "github.com/solo-io/cue/cue/errors" 25 "github.com/solo-io/cue/cue/format" 26 "github.com/solo-io/cue/cue/parser" 27 "github.com/solo-io/cue/encoding/gocode/gocodec" 28 "github.com/solo-io/cue/internal/astinternal" 29 "github.com/solo-io/cue/internal/core/adt" 30 "github.com/solo-io/cue/internal/core/compile" 31 "github.com/solo-io/cue/internal/core/convert" 32 "github.com/solo-io/cue/internal/core/eval" 33 "github.com/solo-io/cue/internal/core/export" 34 "github.com/solo-io/cue/internal/core/runtime" 35 "github.com/solo-io/cue/internal/cuetest" 36 "github.com/solo-io/cue/internal/cuetxtar" 37 "github.com/solo-io/cue/internal/value" 38 ) 39 40 func TestDefinition(t *testing.T) { 41 test := cuetxtar.TxTarTest{ 42 Root: "./testdata", 43 Name: "definition", 44 Update: cuetest.UpdateGoldenFiles, 45 } 46 47 r := runtime.New() 48 49 test.Run(t, func(t *cuetxtar.Test) { 50 a := t.ValidInstances() 51 52 v, errs := compile.Files(nil, r, "", a[0].Files...) 53 if errs != nil { 54 t.Fatal(errs) 55 } 56 v.Finalize(eval.NewContext(r, v)) 57 58 // TODO: do we need to evaluate v? In principle not necessary. 59 // v.Finalize(eval.NewContext(r, v)) 60 61 file, errs := export.Def(r, "", v) 62 errors.Print(t, errs, nil) 63 _, _ = t.Write(formatNode(t.T, file)) 64 }) 65 } 66 67 func formatNode(t *testing.T, n ast.Node) []byte { 68 t.Helper() 69 70 b, err := format.Node(n) 71 if err != nil { 72 t.Fatal(err) 73 } 74 return b 75 } 76 77 // TestGenerated tests conversions of generated Go structs, which may be 78 // different from parsed or evaluated CUE, such as having Vertex values. 79 func TestGenerated(t *testing.T) { 80 ctx := cuecontext.New() 81 82 testCases := []struct { 83 in func(ctx *adt.OpContext) (adt.Expr, error) 84 out string 85 p *export.Profile 86 }{{ 87 in: func(ctx *adt.OpContext) (adt.Expr, error) { 88 in := &C{ 89 Terminals: []*A{{Name: "Name", Description: "Desc"}}, 90 } 91 return convert.GoValueToValue(ctx, in, false), nil 92 }, 93 out: `Terminals: [{Name: "Name", Description: "Desc"}]`, 94 }, { 95 in: func(ctx *adt.OpContext) (adt.Expr, error) { 96 in := &C{ 97 Terminals: []*A{{Name: "Name", Description: "Desc"}}, 98 } 99 return convert.GoTypeToExpr(ctx, in) 100 }, 101 out: `*null|{Terminals?: *null|[...*null|{Name: string, Description: string}]}`, 102 }, { 103 in: func(ctx *adt.OpContext) (adt.Expr, error) { 104 in := []*A{{Name: "Name", Description: "Desc"}} 105 return convert.GoValueToValue(ctx, in, false), nil 106 }, 107 out: `[{Name: "Name", Description: "Desc"}]`, 108 }, { 109 in: func(ctx *adt.OpContext) (adt.Expr, error) { 110 in := []*A{{Name: "Name", Description: "Desc"}} 111 return convert.GoTypeToExpr(ctx, in) 112 }, 113 out: `*null|[...*null|{Name: string, Description: string}]`, 114 }, { 115 in: func(ctx *adt.OpContext) (adt.Expr, error) { 116 expr, err := parser.ParseExpr("test", `{ 117 x: Guide.#Terminal 118 Guide: {} 119 }`) 120 if err != nil { 121 return nil, err 122 } 123 c, err := compile.Expr(nil, ctx, "_", expr) 124 if err != nil { 125 return nil, err 126 } 127 root := &adt.Vertex{} 128 root.AddConjunct(c) 129 root.Finalize(ctx) 130 131 // Simulate Value.Unify of Lookup("x") and Lookup("Guide"). 132 n := &adt.Vertex{} 133 n.AddConjunct(adt.MakeRootConjunct(nil, root.Arcs[0])) 134 n.AddConjunct(adt.MakeRootConjunct(nil, root.Arcs[1])) 135 n.Finalize(ctx) 136 137 return n, nil 138 }, 139 out: `<[l2// x: undefined field: #Terminal] _|_>`, 140 p: export.Final, 141 }, { 142 in: func(r *adt.OpContext) (adt.Expr, error) { 143 v := ctx.CompileString(` 144 #Provider: { 145 ID: string 146 notConcrete: bool 147 a: int 148 b: a + 1 149 }`) 150 151 spec := v.LookupPath(cue.ParsePath("#Provider")) 152 spec2 := spec.FillPath(cue.ParsePath("ID"), "12345") 153 root := v.FillPath(cue.ParsePath("providers.foo"), spec2) 154 _, n := value.ToInternal(root) 155 156 return n, nil 157 }, 158 out: `#Provider: {ID: string, notConcrete: bool, a: int, b: a+1}, providers: {foo: {ID: "12345", notConcrete: bool, a: int, b: a+1}}`, 159 p: export.All, 160 }, { 161 // Issue #882 162 in: func(r *adt.OpContext) (adt.Expr, error) { 163 valA := ctx.CompileString(` 164 #One: { version: string } 165 `) 166 167 valB := ctx.CompileString(` 168 #One: _ 169 ones: {[string]: #One} 170 `) 171 v := valB.Unify(valA) 172 _, n := value.ToInternal(v) 173 return n, nil 174 }, 175 out: `#One: {version: string}, ones: {[string]: #One}`, 176 p: export.All, 177 }, { 178 // Indicate closedness in an element that is closed and misses parent 179 // context. 180 // Issue #882 181 in: func(r *adt.OpContext) (adt.Expr, error) { 182 v := ctx.CompileString(` 183 #A: b: c: string 184 `) 185 v = v.LookupPath(cue.ParsePath("#A.b")) 186 187 _, n := value.ToInternal(v) 188 return n, nil 189 }, 190 out: `_#def, _#def: {c: string}`, 191 p: export.All, 192 }, { 193 // Don't wrap in def if the if the value is an embedded scalar. 194 // Issue #977 195 in: func(r *adt.OpContext) (adt.Expr, error) { 196 v := ctx.CompileString(` 197 #A: { "foo", #enum: 2 } 198 `) 199 v = v.LookupPath(cue.ParsePath("#A")) 200 201 _, n := value.ToInternal(v) 202 return n, nil 203 }, 204 out: `"foo", #enum: 2`, 205 p: export.All, 206 }} 207 for _, tc := range testCases { 208 t.Run("", func(t *testing.T) { 209 ctx := adt.NewContext((*runtime.Runtime)(ctx), &adt.Vertex{}) 210 211 v, err := tc.in(ctx) 212 if err != nil { 213 t.Fatal("failed test case: ", err) 214 } 215 216 p := tc.p 217 if p == nil { 218 p = export.Simplified 219 } 220 221 var n ast.Node 222 switch x := v.(type) { 223 case *adt.Vertex: 224 n, err = p.Def(ctx, "", x) 225 default: 226 n, err = p.Expr(ctx, "", v) 227 } 228 if err != nil { 229 t.Fatal("failed export: ", err) 230 } 231 got := astinternal.DebugStr(n) 232 if got != tc.out { 233 t.Errorf("got: %s\nwant: %s", got, tc.out) 234 } 235 }) 236 } 237 } 238 239 type A struct { 240 Name string 241 Description string 242 } 243 244 type B struct { 245 Image string 246 } 247 248 type C struct { 249 Terminals []*A 250 } 251 252 // For debugging purposes. Do not delete. 253 func TestX(t *testing.T) { 254 t.Skip() 255 256 in := ` 257 -- in.cue -- 258 package test 259 260 // // Foo 261 // a: [X=string]: [Y=string]: { 262 // name: X+Y 263 // } 264 265 // [Y=string]: [X=string]: name: {Y+X} 266 // { 267 // name: X.other + Y 268 // other: string 269 // } 270 271 // c: [X=string]: X 272 273 // #pkg1: Object 274 275 // "Hello \(#pkg1)!" 276 277 278 // Object: "World" 279 280 // // A Foo fooses stuff. 281 // foos are instances of Foo. 282 // foos: [string]: {} 283 284 // // // My first little foo. 285 // foos: MyFoo: {} 286 ` 287 288 archive := txtar.Parse([]byte(in)) 289 a := cuetxtar.Load(archive, "/tmp/test") 290 if err := a[0].Err; err != nil { 291 t.Fatal(err) 292 } 293 294 // x := a[0].Files[0] 295 // astutil.Sanitize(x) 296 297 r := runtime.New() 298 v, errs := compile.Files(nil, r, "", a[0].Files...) 299 if errs != nil { 300 t.Fatal(errs) 301 } 302 v.Finalize(eval.NewContext(r, v)) 303 304 file, errs := export.Def(r, "main", v) 305 if errs != nil { 306 t.Fatal(errs) 307 } 308 309 t.Error(string(formatNode(t, file))) 310 } 311 312 func TestFromGo(t *testing.T) { 313 type Struct struct { 314 A string 315 B string 316 } 317 318 m := make(map[string]Struct) 319 m["hello"] = Struct{ 320 A: "a", 321 B: "b", 322 } 323 var r cue.Runtime 324 codec := gocodec.New(&r, nil) 325 v, err := codec.Decode(m) 326 if err != nil { 327 panic(err) 328 } 329 330 syn, _ := format.Node(v.Syntax()) 331 if got := string(syn); got != `{ 332 hello: { 333 A: "a" 334 B: "b" 335 } 336 }` { 337 t.Errorf("incorrect ordering: %s\n", got) 338 } 339 }