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