1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package openapi_test
16
17 import (
18 "bytes"
19 "encoding/json"
20 "os"
21 "path/filepath"
22 "strings"
23 "testing"
24
25 "github.com/google/go-cmp/cmp"
26
27 "cuelang.org/go/cue"
28 "cuelang.org/go/cue/build"
29 "cuelang.org/go/cue/cuecontext"
30 "cuelang.org/go/cue/errors"
31 "cuelang.org/go/cue/load"
32 "cuelang.org/go/encoding/openapi"
33 "cuelang.org/go/internal/cuetest"
34 )
35
36 func TestParseDefinitions(t *testing.T) {
37 info := struct {
38 Title string `json:"title"`
39 Version string `json:"version"`
40 }{"test", "v1"}
41 defaultConfig := &openapi.Config{}
42 resolveRefs := &openapi.Config{Info: info, ExpandReferences: true}
43
44 testCases := []struct {
45 in, out string
46 variant string
47 instanceOnly bool
48 valueOnly bool
49 config *openapi.Config
50 err string
51 }{{
52 in: "structural.cue",
53 out: "structural.json",
54 config: resolveRefs,
55 }, {
56 in: "structural.cue",
57 variant: "+ReferenceFunc",
58 out: "structural.json",
59 instanceOnly: true,
60 config: &openapi.Config{
61 Info: info,
62 ExpandReferences: true,
63 ReferenceFunc: func(v *cue.Instance, path []string) string {
64 return strings.Join(path, "_")
65 },
66 },
67 }, {
68 in: "protobuf.cue",
69 out: "protobuf.json",
70 instanceOnly: true,
71 config: &openapi.Config{
72 Info: info,
73 ExpandReferences: true,
74 ReferenceFunc: func(p *cue.Instance, path []string) string {
75 return strings.Join(path, ".")
76 },
77 },
78 }, {
79 in: "nested.cue",
80 out: "nested.json",
81 config: defaultConfig,
82 }, {
83 in: "simple.cue",
84 out: "simple.json",
85 config: resolveRefs,
86 }, {
87 in: "simple.cue",
88 out: "simple-filter.json",
89 config: &openapi.Config{Info: info, FieldFilter: "min.*|max.*"},
90 }, {
91 in: "array.cue",
92 out: "array.json",
93 config: defaultConfig,
94 }, {
95 in: "enum.cue",
96 out: "enum.json",
97 config: defaultConfig,
98 }, {
99 in: "struct.cue",
100 out: "struct.json",
101 config: defaultConfig,
102 }, {
103 in: "strings.cue",
104 out: "strings.json",
105 config: defaultConfig,
106 }, {
107 in: "nums.cue",
108 out: "nums.json",
109 config: defaultConfig,
110 }, {
111 in: "nums.cue",
112 out: "nums-v3.1.0.json",
113 config: &openapi.Config{Info: info, Version: "3.1.0"},
114 }, {
115 in: "builtins.cue",
116 out: "builtins.json",
117 config: defaultConfig,
118 }, {
119 in: "oneof.cue",
120 out: "oneof.json",
121 config: defaultConfig,
122 }, {
123 in: "oneof.cue",
124 out: "oneof-resolve.json",
125 config: resolveRefs,
126 }, {
127 in: "openapi.cue",
128 out: "openapi.json",
129 config: defaultConfig,
130 }, {
131 in: "openapi.cue",
132 out: "openapi-norefs.json",
133 config: resolveRefs,
134 }, {
135 in: "embed.cue",
136 out: "embed.json",
137 config: defaultConfig,
138 }, {
139 in: "embed.cue",
140 out: "embed-norefs.json",
141 config: resolveRefs,
142 }, {
143 in: "oneof.cue",
144 out: "oneof-funcs.json",
145 config: &openapi.Config{
146 Info: info,
147 NameFunc: func(v cue.Value, path cue.Path) string {
148 var buf strings.Builder
149 for i, sel := range path.Selectors() {
150 if i > 0 {
151 buf.WriteByte('_')
152 }
153 s := sel.String()
154 s = strings.TrimPrefix(s, "#")
155 buf.WriteString(strings.ToUpper(s))
156 }
157 return buf.String()
158 },
159 DescriptionFunc: func(v cue.Value) string {
160 return "Randomly picked description from a set of size one."
161 },
162 },
163 }, {
164 in: "oneof.cue",
165 out: "oneof-funcs.json",
166 variant: "+ReferenceFunc",
167 instanceOnly: true,
168 config: &openapi.Config{
169 Info: info,
170 ReferenceFunc: func(v *cue.Instance, path []string) string {
171 return strings.ToUpper(strings.Join(path, "_"))
172 },
173 DescriptionFunc: func(v cue.Value) string {
174 return "Randomly picked description from a set of size one."
175 },
176 },
177 }, {
178 in: "refs.cue",
179 out: "refs.json",
180 config: &openapi.Config{
181 Info: info,
182 NameFunc: func(v cue.Value, path cue.Path) string {
183 switch {
184 case strings.HasPrefix(path.Selectors()[0].String(), "#Excluded"):
185 return ""
186 }
187 return strings.TrimPrefix(path.String(), "#")
188 },
189 },
190 }, {
191 in: "refs.cue",
192 out: "refs.json",
193 variant: "+ReferenceFunc",
194 instanceOnly: true,
195 config: &openapi.Config{
196 Info: info,
197 ReferenceFunc: func(v *cue.Instance, path []string) string {
198 switch {
199 case strings.HasPrefix(path[0], "Excluded"):
200 return ""
201 }
202 return strings.Join(path, ".")
203 },
204 },
205 }, {
206 in: "issue131.cue",
207 out: "issue131.json",
208 config: &openapi.Config{Info: info, SelfContained: true},
209 }, {
210
211 in: "cycle.cue",
212 out: "cycle.json",
213 config: &openapi.Config{Info: info},
214 }, {
215
216 in: "cycle.cue",
217 config: &openapi.Config{Info: info, ExpandReferences: true},
218 err: "cycle",
219 }, {
220 in: "quotedfield.cue",
221 out: "quotedfield.json",
222 config: defaultConfig,
223 }, {
224 in: "omitvalue.cue",
225 out: "omitvalue.json",
226 config: defaultConfig,
227 }}
228 for _, tc := range testCases {
229 t.Run(tc.out+tc.variant, func(t *testing.T) {
230 run := func(t *testing.T, inst cue.InstanceOrValue) {
231 b, err := openapi.Gen(inst, tc.config)
232 if err != nil {
233 if tc.err == "" {
234 t.Fatal("unexpected error:", errors.Details(err, nil))
235 }
236 return
237 }
238
239 if tc.err != "" {
240 t.Fatal("unexpected success:", tc.err)
241 } else {
242 all, err := tc.config.All(inst)
243 if err != nil {
244 t.Fatal(err)
245 }
246 walk(all)
247 }
248
249 var out = &bytes.Buffer{}
250 _ = json.Indent(out, b, "", " ")
251
252 wantFile := filepath.Join("testdata", tc.out)
253 if cuetest.UpdateGoldenFiles {
254 _ = os.WriteFile(wantFile, out.Bytes(), 0644)
255 return
256 }
257
258 b, err = os.ReadFile(wantFile)
259 if err != nil {
260 t.Fatal(err)
261 }
262
263 if d := cmp.Diff(string(b), out.String()); d != "" {
264 t.Errorf("files differ:\n%v", d)
265 }
266 }
267 filename := filepath.FromSlash(tc.in)
268 inst := load.Instances([]string{filename}, &load.Config{
269 Dir: "./testdata",
270 })[0]
271 if !tc.valueOnly {
272 t.Run("Instance", func(t *testing.T) {
273
274 inst := cue.Build([]*build.Instance{inst})[0]
275 if inst.Err != nil {
276 t.Fatal(errors.Details(inst.Err, nil))
277 }
278 run(t, inst)
279 })
280 }
281 if !tc.instanceOnly {
282 t.Run("Value", func(t *testing.T) {
283
284 ctx := cuecontext.New()
285 v := ctx.BuildInstance(inst)
286 if err := v.Err(); err != nil {
287 t.Fatal(errors.Details(err, nil))
288 }
289 run(t, v)
290 })
291 }
292 })
293 }
294 }
295
296
297
298 func walk(om *openapi.OrderedMap) {
299 for _, p := range om.Pairs() {
300 switch p := p.Value.(type) {
301 case *openapi.OrderedMap:
302 walk(p)
303 case []*openapi.OrderedMap:
304 for _, om := range p {
305 walk(om)
306 }
307 }
308 }
309 }
310
311
312 func TestIssue1234(t *testing.T) {
313 var r cue.Runtime
314 inst, err := r.Compile("test", `
315 #Test: or([])
316
317 `)
318 if err != nil {
319 t.Fatal(err)
320 }
321
322 _, err = openapi.Gen(inst, &openapi.Config{})
323 if err == nil {
324 t.Fatal("expected error")
325 }
326 }
327
328
329 func TestX(t *testing.T) {
330 t.Skip()
331
332 var r cue.Runtime
333 inst, err := r.Compile("test", `
334 `)
335 if err != nil {
336 t.Fatal(err)
337 }
338
339 b, err := openapi.Gen(inst, &openapi.Config{
340
341 })
342 if err != nil {
343 t.Fatal(errors.Details(err, nil))
344 }
345
346 var out = &bytes.Buffer{}
347 _ = json.Indent(out, b, "", " ")
348 t.Error(out.String())
349 }
350
View as plain text