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 }