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  }