cuelang.org/go@v0.13.0/cue/syntax_test.go (about)

     1  // Copyright 2021 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 cue_test
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"cuelang.org/go/cue"
    22  	"cuelang.org/go/cue/ast"
    23  	"cuelang.org/go/cue/cuecontext"
    24  	"cuelang.org/go/cue/format"
    25  	"cuelang.org/go/internal"
    26  	"cuelang.org/go/internal/core/runtime"
    27  )
    28  
    29  func TestSyntax(t *testing.T) {
    30  	o := func(opts ...cue.Option) []cue.Option {
    31  		return opts
    32  	}
    33  	_ = o
    34  	testCases := []struct {
    35  		name    string
    36  		in      string
    37  		path    string
    38  		options []cue.Option
    39  		out     string
    40  
    41  		todoV3 bool
    42  	}{{
    43  		name: "preseve docs",
    44  		in: `
    45  		// Aloha
    46  		hello: "world"
    47  		// Aloha2
    48  		if true {
    49  			// Aloha3
    50  			if true {
    51  				// Aloha4
    52  				hello2: "world"
    53  			}
    54  		}
    55  		`,
    56  		options: o(cue.Docs(true)),
    57  		out: `
    58  {
    59  	// Aloha
    60  	hello: "world"
    61  	// Aloha2
    62  	if true {
    63  		// Aloha3
    64  		if true {
    65  			// Aloha4
    66  			hello2: "world"
    67  		}
    68  	}
    69  }`,
    70  	}, {
    71  		name: "partially resolvable",
    72  		in: `
    73  		x: {}
    74  		t: {name: string}
    75  		output: [ ... {t & x.value}]
    76  		`,
    77  		options: o(cue.ResolveReferences(true)),
    78  		out: `
    79  {
    80  	x: {}
    81  	t: {
    82  		name: string
    83  	}
    84  	output: [...t & x.value]
    85  }`,
    86  	}, {
    87  		name: "issue867",
    88  		path: "output",
    89  		in: `
    90  	x: {}
    91  	t: {name: string}
    92  	output: [ ... {t & x.value}]
    93  	`,
    94  		out: `
    95  {
    96  	[...T & {}.value]
    97  
    98  	//cue:path: t
    99  	let T = {
   100  		name: string
   101  	}
   102  }`}, {
   103  		// Structural errors (and worse) are reported as is.
   104  		name: "structural error",
   105  		in: `
   106  			#List: {
   107  				value: _
   108  				next: #List
   109  			}
   110  			a: b: #List
   111  			`,
   112  		path:    "a",
   113  		options: o(cue.ResolveReferences(true)),
   114  		out: `
   115  {
   116  	b: _|_ // #List.next: structural cycle (and 1 more errors)
   117  }`,
   118  		// evalv3 seems to result in `b: {}`.
   119  		todoV3: true,
   120  	}, {
   121  		name: "resolveReferences",
   122  		path: "resource",
   123  		in: `
   124  		// User 1
   125  		v1: #Deployment: {
   126  			spec: {
   127  				replicas: int
   128  				containers: [...]
   129  				other: option: int
   130  			}
   131  
   132  			incomplete: {
   133  				// NOTE: the definition of "a" will be out of scope so this
   134  				// reference will not be resolvable.
   135  				// TODO: hoist the definition of "a" into a let expression.
   136  				x: a.x
   137  				y: 1 | 2
   138  				z: [1, 2][a.x]
   139  			}
   140  
   141  			// NOTE: structural cycles are eliminated from disjunctions. This
   142  			// means the semantics of the type is not preserved.
   143  			// TODO: should we change this?
   144  			recursive: #List
   145  		}
   146  
   147  		a: {}
   148  		#D: {}
   149  
   150  		#List: {
   151  			Value: _
   152  			Next: #List | *null
   153  		}
   154  
   155  		parameter: {
   156  			image: string
   157  			replicas: int
   158  		}
   159  
   160  		_mystring: string
   161  
   162  		resource: v1.#Deployment & {
   163  			spec: {
   164  			   replicas: parameter.replicas
   165  			   containers: [{
   166  						image: parameter.image
   167  						name: "main"
   168  						envs: [..._mystring]
   169  				}]
   170  			}
   171  		}
   172  
   173  		parameter: image: *"myimage" | string
   174  		parameter: replicas: *2 | >=1 & <5
   175  
   176  		// User 2
   177  		parameter: replicas: int
   178  
   179  		resource: spec: replicas: parameter.replicas
   180  
   181  		parameter: replicas: 3
   182  		`,
   183  		options: o(cue.ResolveReferences(true)),
   184  		out: `
   185  {
   186  	spec: {
   187  		replicas: 3
   188  		containers: [{
   189  			image: *"myimage" | string
   190  			name:  "main"
   191  			envs: [...string]
   192  		}]
   193  		other: {
   194  			option: int
   195  		}
   196  	}
   197  	incomplete: {
   198  		x: {}.x
   199  		y: 1 | 2
   200  		z: [1, 2][{}.x]
   201  	}
   202  	recursive: {
   203  		Value: _
   204  		Next:  null
   205  	}
   206  }
   207  		`,
   208  	}, {
   209  		name: "issue2339",
   210  		in: `
   211  s: string
   212  if true {
   213  	out: "\(s)": 3
   214  }
   215  	`,
   216  		options: o(cue.ResolveReferences(true)),
   217  		out: `
   218  {
   219  	s: string
   220  	out: {
   221  		"\(s)": 3
   222  	}
   223  }
   224  	`,
   225  	}, {
   226  		name: "fragments",
   227  		in: `
   228  		// #person is a real person
   229  		#person: {
   230  			children: [...#person]
   231  			name: =~"^[A-Za-z0-9]+$"
   232  			address: string
   233  		}
   234  		`,
   235  		path:    "#person.children",
   236  		options: o(cue.Schema(), cue.Raw()),
   237  		out:     `[...#person]`,
   238  	}}
   239  	for _, tc := range testCases {
   240  		t.Run(tc.name, func(t *testing.T) {
   241  			ctx := cuecontext.New()
   242  			if version, _ := (*runtime.Runtime)(ctx).Settings(); version == internal.EvalV3 && tc.todoV3 {
   243  				t.Skip("TODO: fix these tests on evalv3")
   244  			}
   245  
   246  			v := ctx.CompileString(tc.in)
   247  			v = v.LookupPath(cue.ParsePath(tc.path))
   248  
   249  			syntax := v.Syntax(tc.options...)
   250  			b, err := format.Node(syntax)
   251  			if err != nil {
   252  				t.Fatal(err)
   253  			}
   254  			got := strings.TrimSpace(string(b))
   255  			want := strings.TrimSpace(tc.out)
   256  			if got != want {
   257  				t.Errorf("got: %v; want %v", got, want)
   258  			}
   259  		})
   260  	}
   261  }
   262  
   263  func TestFragment(t *testing.T) {
   264  	in := `
   265  	#person: {
   266  		children: [...#person]
   267  	}`
   268  
   269  	ctx := cuecontext.New()
   270  	if version, _ := (*runtime.Runtime)(ctx).Settings(); version == internal.EvalV3 {
   271  		t.Skip("TODO: fix these tests on evalv3")
   272  	}
   273  
   274  	v := ctx.CompileString(in)
   275  	v = v.LookupPath(cue.ParsePath("#person.children"))
   276  
   277  	syntax := v.Syntax(cue.Schema(), cue.Raw()).(ast.Expr)
   278  
   279  	// Compile the fragment from within the scope it was derived.
   280  	v = ctx.BuildExpr(syntax, cue.Scope(v))
   281  
   282  	// Generate the syntax, this time as self-contained.
   283  	syntax = v.Syntax(cue.Schema()).(ast.Expr)
   284  	b, err := format.Node(syntax)
   285  	if err != nil {
   286  		t.Fatal(err)
   287  	}
   288  	out := `{
   289  	[...PERSON.#x]
   290  
   291  	//cue:path: #person
   292  	let PERSON = {
   293  		#x: {
   294  			children: [...#person]
   295  		}
   296  	}
   297  }`
   298  	got := strings.TrimSpace(string(b))
   299  	want := strings.TrimSpace(out)
   300  	if got != want {
   301  		t.Errorf("got: %v; want %v", got, want)
   302  	}
   303  }