1// Copyright 2019 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
15package openapi_test
16
17import (
18 "bytes"
19 "io/fs"
20 "os"
21 "path"
22 "path/filepath"
23 "strings"
24 "testing"
25
26 "github.com/go-quicktest/qt"
27 "github.com/google/go-cmp/cmp"
28 "golang.org/x/tools/txtar"
29
30 "cuelang.org/go/cue"
31 "cuelang.org/go/cue/ast"
32 "cuelang.org/go/cue/cuecontext"
33 "cuelang.org/go/cue/errors"
34 "cuelang.org/go/cue/format"
35 "cuelang.org/go/encoding/json"
36 "cuelang.org/go/encoding/openapi"
37 "cuelang.org/go/encoding/yaml"
38 "cuelang.org/go/internal"
39 "cuelang.org/go/internal/cuetest"
40)
41
42// TestDecode reads the testdata/script/*.txtar files, converts the
43// contained JSON (or YAML) schema to CUE and compares it against the
44// output.
45//
46// The first .json or .yaml file in the txtar archive (with no / in
47// the name) is used as the input. The first .cue file in the archive
48// (with no / in the name) is used as the expected output.
49//
50// Set CUE_UPDATE=1 to update test files with the corresponding output.
51func TestDecode(t *testing.T) {
52 t.Parallel()
53 err := filepath.WalkDir("testdata/script", func(fullpath string, entry fs.DirEntry, err error) error {
54 if err != nil {
55 return err
56 }
57 if !strings.HasSuffix(fullpath, ".txtar") {
58 return nil
59 }
60
61 t.Run(fullpath, func(t *testing.T) {
62 t.Parallel()
63 a, err := txtar.ParseFile(fullpath)
64 if err != nil {
65 t.Fatal(err)
66 }
67
68 cfg := &openapi.Config{PkgName: "foo"}
69
70 var inFile *ast.File
71 var out, errout []byte
72 outIndex := -1
73
74 for i, f := range a.Files {
75 if strings.Contains(f.Name, "/") {
76 continue
77 }
78 switch path.Ext(f.Name) {
79 case ".json":
80 if inFile == nil {
81 var inExpr ast.Expr
82 inExpr, err = json.Extract(f.Name, f.Data)
83 inFile = internal.ToFile(inExpr)
84 }
85 case ".yaml":
86 if inFile == nil {
87 inFile, err = yaml.Extract(f.Name, f.Data)
88 }
89 case ".cue":
90 if out == nil {
91 out = f.Data
92 outIndex = i
93 }
94 case ".err":
95 if errout == nil {
96 errout = f.Data
97 }
98 }
99 }
100 if err != nil {
101 t.Fatal(err)
102 }
103 ctx := cuecontext.New()
104 in := ctx.BuildFile(inFile)
105 if err := in.Err(); err != nil {
106 t.Fatal(err)
107 }
108
109 gotFile, err := openapi.Extract(in, cfg)
110 if err != nil && errout == nil {
111 t.Fatal(errors.Details(err, nil))
112 }
113 got := []byte(nil)
114 if err != nil {
115 got = []byte(err.Error())
116 }
117 if diff := cmp.Diff(errout, got); diff != "" {
118 t.Error(diff)
119 }
120
121 if gotFile != nil {
122 // verify the generated CUE.
123 v := ctx.BuildFile(gotFile, cue.Filename(fullpath))
124 if err := v.Err(); err != nil {
125 t.Fatal(errors.Details(err, nil))
126 }
127
128 b, err := format.Node(gotFile, format.Simplify())
129 if err != nil {
130 t.Fatal(err)
131 }
132
133 b = bytes.TrimSpace(b)
134 out = bytes.TrimSpace(out)
135
136 if diff := cmp.Diff(b, out); diff != "" {
137 if cuetest.UpdateGoldenFiles {
138 a.Files[outIndex].Data = b
139 b = txtar.Format(a)
140 err = os.WriteFile(fullpath, b, 0666)
141 if err != nil {
142 t.Fatal(err)
143 }
144 return
145 }
146 t.Error(cmp.Diff(string(out), string(b)))
147 }
148 }
149 })
150 return nil
151 })
152 qt.Assert(t, qt.IsNil(err))
153}