...

Source file src/cuelang.org/go/internal/core/validate/validate_test.go

Documentation: cuelang.org/go/internal/core/validate

     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 validate
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"testing"
    21  
    22  	"cuelang.org/go/cue/errors"
    23  	"cuelang.org/go/cue/parser"
    24  	"cuelang.org/go/internal/core/adt"
    25  	"cuelang.org/go/internal/core/compile"
    26  	"cuelang.org/go/internal/core/eval"
    27  	"cuelang.org/go/internal/core/runtime"
    28  	"cuelang.org/go/internal/cuetest"
    29  	"cuelang.org/go/internal/tdtest"
    30  	"github.com/google/go-cmp/cmp"
    31  )
    32  
    33  func TestValidate(t *testing.T) {
    34  	type testCase struct {
    35  		name   string
    36  		in     string
    37  		out    string
    38  		lookup string
    39  		cfg    *Config
    40  	}
    41  	testCases := []testCase{{
    42  		name: "no error, but not concrete, even with definition label",
    43  		cfg:  &Config{Concrete: true},
    44  		in: `
    45  		#foo: { use: string }
    46  		`,
    47  		lookup: "#foo",
    48  		out:    "incomplete\n#foo.use: incomplete value string:\n    test:2:16",
    49  	}, {
    50  		name: "definitions not considered for completeness",
    51  		cfg:  &Config{Concrete: true},
    52  		in: `
    53  		#foo: { use: string }
    54  		`,
    55  	}, {
    56  		name: "hidden fields not considered for completeness",
    57  		cfg:  &Config{Concrete: true},
    58  		in: `
    59  		_foo: { use: string }
    60  		`,
    61  	}, {
    62  		name: "hidden fields not considered for completeness",
    63  		in: `
    64  		_foo: { use: string }
    65  		`,
    66  	}, {
    67  		name: "evaluation error at top",
    68  		in: `
    69  		1 & 2
    70  		`,
    71  		out: "eval\nconflicting values 2 and 1:\n    test:2:3\n    test:2:7",
    72  	}, {
    73  		name: "evaluation error in field",
    74  		in: `
    75  		x: 1 & 2
    76  		`,
    77  		out: "eval\nx: conflicting values 2 and 1:\n    test:2:6\n    test:2:10",
    78  	}, {
    79  		name: "first error",
    80  		in: `
    81  		x: 1 & 2
    82  		y: 2 & 4
    83  		`,
    84  		out: "eval\nx: conflicting values 2 and 1:\n    test:2:6\n    test:2:10",
    85  	}, {
    86  		name: "all errors",
    87  		cfg:  &Config{AllErrors: true},
    88  		in: `
    89  		x: 1 & 2
    90  		y: 2 & 4
    91  		`,
    92  		out: `eval
    93  x: conflicting values 2 and 1:
    94      test:2:6
    95      test:2:10
    96  y: conflicting values 4 and 2:
    97      test:3:6
    98      test:3:10`,
    99  	}, {
   100  		name: "incomplete",
   101  		cfg:  &Config{Concrete: true},
   102  		in: `
   103  		y: 2 + x
   104  		x: string
   105  		`,
   106  		out: "incomplete\ny: non-concrete value string in operand to +:\n    test:2:6\n    test:3:6",
   107  	}, {
   108  		name: "allowed incomplete cycle",
   109  		in: `
   110  		y: x
   111  		x: y
   112  		`,
   113  	}, {
   114  		name: "allowed incomplete when disallowing cycles",
   115  		cfg:  &Config{DisallowCycles: true},
   116  		in: `
   117  		y: string
   118  		x: y
   119  		`,
   120  	}, {
   121  		name: "disallow cycle",
   122  		cfg:  &Config{DisallowCycles: true},
   123  		in: `
   124  		y: x + 1
   125  		x: y - 1
   126  		`,
   127  		out: "cycle\ncycle error:\n    test:2:6",
   128  	}, {
   129  		name: "disallow cycle",
   130  		cfg:  &Config{DisallowCycles: true},
   131  		in: `
   132  		a: b - 100
   133  		b: a + 100
   134  		c: [c[1], c[0]]		`,
   135  		out: "cycle\ncycle error:\n    test:2:6",
   136  	}, {
   137  		name: "treat cycles as incomplete when not disallowing",
   138  		cfg:  &Config{},
   139  		in: `
   140  		y: x + 1
   141  		x: y - 1
   142  		`,
   143  	}, {
   144  		// Note: this is already handled by evaluation, as terminal errors
   145  		// are percolated up.
   146  		name: "catch most serious error",
   147  		cfg:  &Config{Concrete: true},
   148  		in: `
   149  		y: string
   150  		x: 1 & 2
   151  		`,
   152  		out: "eval\nx: conflicting values 2 and 1:\n    test:3:6\n    test:3:10",
   153  	}, {
   154  		name: "consider defaults for concreteness",
   155  		cfg:  &Config{Concrete: true},
   156  		in: `
   157  		x: *1 | 2
   158  		`,
   159  	}, {
   160  		name: "allow non-concrete in definitions in concrete mode",
   161  		cfg:  &Config{Concrete: true},
   162  		in: `
   163  		x: 2
   164  		#d: {
   165  			b: int
   166  			c: b + b
   167  		}
   168  		`,
   169  	}, {
   170  		name: "pick up non-concrete value in default",
   171  		cfg:  &Config{Concrete: true},
   172  		in: `
   173  		x: null | *{
   174  			a: int
   175  		}
   176  		`,
   177  		out: "incomplete\nx.a: incomplete value int:\n    test:3:7",
   178  	}, {
   179  		name: "pick up non-concrete value in default",
   180  		cfg:  &Config{Concrete: true},
   181  		in: `
   182  			x: null | *{
   183  				a: 1 | 2
   184  			}
   185  			`,
   186  		out: "incomplete\nx.a: incomplete value 1 | 2",
   187  	}, {
   188  		name: "required field not present",
   189  		cfg:  &Config{Final: true},
   190  		in: `
   191  			Person: {
   192  				name!:  string
   193  				age?:   int
   194  				height: 1.80
   195  			}
   196  			`,
   197  		out: "incomplete\nPerson.name: field is required but not present:\n    test:3:5",
   198  	}, {
   199  		name: "allow required fields in definitions",
   200  		cfg:  &Config{Concrete: true},
   201  		in: `
   202  		#Person: {
   203  			name!: string
   204  			age?:  int
   205  		}
   206  		`,
   207  		out: "",
   208  	}, {
   209  		name: "allow required fields when not concrete",
   210  		in: `
   211  		Person: {
   212  			name!: string
   213  			age?:  int
   214  		}
   215  		`,
   216  		out: "",
   217  	}}
   218  
   219  	r := runtime.New()
   220  	ctx := eval.NewContext(r, nil)
   221  
   222  	tdtest.Run(t, testCases, func(t *cuetest.T, tc *testCase) {
   223  		f, err := parser.ParseFile("test", tc.in)
   224  		if err != nil {
   225  			t.Fatal(err)
   226  		}
   227  		v, err := compile.Files(nil, r, "", f)
   228  		if err != nil {
   229  			t.Fatal(err)
   230  		}
   231  		v.Finalize(ctx)
   232  		if tc.lookup != "" {
   233  			v = v.Lookup(adt.MakeIdentLabel(r, tc.lookup, "main"))
   234  		}
   235  
   236  		b := Validate(ctx, v, tc.cfg)
   237  
   238  		w := &strings.Builder{}
   239  		if b != nil {
   240  			fmt.Fprintln(w, b.Code)
   241  			errors.Print(w, b.Err, nil)
   242  		}
   243  
   244  		got := strings.TrimSpace(w.String())
   245  		if tc.out != got {
   246  			t.Error(cmp.Diff(tc.out, got))
   247  		}
   248  	})
   249  }
   250  

View as plain text