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