1
2
3
4
5
6
7
8
9
10
11
12
13
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
145
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