github.com/solo-io/cue@v0.4.7/encoding/openapi/openapi_test.go (about) 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 15 package openapi_test 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "io/ioutil" 21 "path/filepath" 22 "strings" 23 "testing" 24 25 "github.com/kylelemons/godebug/diff" 26 27 "github.com/solo-io/cue/cue" 28 "github.com/solo-io/cue/cue/ast" 29 "github.com/solo-io/cue/cue/errors" 30 "github.com/solo-io/cue/cue/load" 31 "github.com/solo-io/cue/encoding/openapi" 32 "github.com/solo-io/cue/internal/cuetest" 33 ) 34 35 func TestParseDefinitions(t *testing.T) { 36 info := *(*openapi.OrderedMap)(ast.NewStruct( 37 "title", ast.NewString("test"), 38 "version", ast.NewString("v1"), 39 )) 40 defaultConfig := &openapi.Config{} 41 resolveRefs := &openapi.Config{Info: info, ExpandReferences: true} 42 43 testCases := []struct { 44 in, out string 45 config *openapi.Config 46 err string 47 }{{ 48 in: "structural.cue", 49 out: "structural.json", 50 config: resolveRefs, 51 }, { 52 in: "nested.cue", 53 out: "nested.json", 54 config: defaultConfig, 55 }, { 56 in: "simple.cue", 57 out: "simple.json", 58 config: resolveRefs, 59 }, { 60 in: "simple.cue", 61 out: "simple-filter.json", 62 config: &openapi.Config{Info: info, FieldFilter: "min.*|max.*"}, 63 }, { 64 in: "array.cue", 65 out: "array.json", 66 config: defaultConfig, 67 }, { 68 in: "enum.cue", 69 out: "enum.json", 70 config: defaultConfig, 71 }, { 72 in: "struct.cue", 73 out: "struct.json", 74 config: defaultConfig, 75 }, { 76 in: "strings.cue", 77 out: "strings.json", 78 config: defaultConfig, 79 }, { 80 in: "nums.cue", 81 out: "nums.json", 82 config: defaultConfig, 83 }, { 84 in: "nums.cue", 85 out: "nums-v3.1.0.json", 86 config: &openapi.Config{Info: info, Version: "3.1.0"}, 87 }, { 88 in: "builtins.cue", 89 out: "builtins.json", 90 config: defaultConfig, 91 }, { 92 in: "oneof.cue", 93 out: "oneof.json", 94 config: defaultConfig, 95 }, { 96 in: "oneof.cue", 97 out: "oneof-resolve.json", 98 config: resolveRefs, 99 }, { 100 in: "openapi.cue", 101 out: "openapi.json", 102 config: defaultConfig, 103 }, { 104 in: "openapi.cue", 105 out: "openapi-norefs.json", 106 config: resolveRefs, 107 }, { 108 in: "embed.cue", 109 out: "embed.json", 110 config: defaultConfig, 111 }, { 112 in: "embed.cue", 113 out: "embed-norefs.json", 114 config: resolveRefs, 115 }, { 116 in: "oneof.cue", 117 out: "oneof-funcs.json", 118 config: &openapi.Config{ 119 Info: info, 120 ReferenceFunc: func(inst *cue.Instance, path []string) string { 121 return strings.ToUpper(strings.Join(path, "_")) 122 }, 123 DescriptionFunc: func(v cue.Value) string { 124 return "Randomly picked description from a set of size one." 125 }, 126 }, 127 }, { 128 in: "refs.cue", 129 out: "refs.json", 130 config: &openapi.Config{ 131 Info: info, 132 ReferenceFunc: func(inst *cue.Instance, path []string) string { 133 switch { 134 case strings.HasPrefix(path[0], "Excluded"): 135 return "" 136 } 137 return strings.Join(path, ".") 138 }, 139 }, 140 }, { 141 in: "issue131.cue", 142 out: "issue131.json", 143 config: &openapi.Config{Info: info, SelfContained: true}, 144 }, { 145 // Issue #915 146 in: "cycle.cue", 147 out: "cycle.json", 148 config: &openapi.Config{Info: info}, 149 }, { 150 // Issue #915 151 in: "cycle.cue", 152 config: &openapi.Config{Info: info, ExpandReferences: true}, 153 err: "cycle", 154 }, { 155 in: "nested-in-oneof.cue", 156 config: &openapi.Config{ 157 ExpandReferences: true, 158 }, 159 out: "nested-in-oneof.json", 160 }} 161 for _, tc := range testCases { 162 t.Run(tc.out, func(t *testing.T) { 163 filename := filepath.FromSlash(tc.in) 164 165 inst := cue.Build(load.Instances([]string{filename}, &load.Config{ 166 Dir: "./testdata", 167 }))[0] 168 if inst.Err != nil { 169 t.Fatal(errors.Details(inst.Err, nil)) 170 } 171 172 b, err := openapi.Gen(inst, tc.config) 173 if err != nil { 174 if tc.err == "" { 175 t.Fatal("unexpected error:", errors.Details(inst.Err, nil)) 176 } 177 return 178 } 179 180 if tc.err != "" { 181 t.Fatal("unexpected success:", tc.err) 182 } else { 183 all, err := tc.config.All(inst) 184 if err != nil { 185 t.Fatal(err) 186 } 187 walk(all) 188 } 189 190 var out = &bytes.Buffer{} 191 _ = json.Indent(out, b, "", " ") 192 193 wantFile := filepath.Join("testdata", tc.out) 194 if cuetest.UpdateGoldenFiles { 195 _ = ioutil.WriteFile(wantFile, out.Bytes(), 0644) 196 return 197 } 198 199 b, err = ioutil.ReadFile(wantFile) 200 if err != nil { 201 t.Fatal(err) 202 } 203 204 if d := diff.Diff(string(b), out.String()); d != "" { 205 t.Errorf("files differ:\n%v", d) 206 } 207 }) 208 } 209 } 210 211 // walk traverses an openapi.OrderedMap. This is a helper function 212 // used to ensure that a generated OpenAPI value is well-formed. 213 func walk(om *openapi.OrderedMap) { 214 for _, p := range om.Pairs() { 215 switch p := p.Value.(type) { 216 case *openapi.OrderedMap: 217 walk(p) 218 case []*openapi.OrderedMap: 219 for _, om := range p { 220 walk(om) 221 } 222 } 223 } 224 } 225 226 // This is for debugging purposes. Do not remove. 227 func TestX(t *testing.T) { 228 t.Skip() 229 230 var r cue.Runtime 231 inst, err := r.Compile("test", ` 232 AnyField: "any value" 233 `) 234 if err != nil { 235 t.Fatal(err) 236 } 237 238 b, err := openapi.Gen(inst, &openapi.Config{ 239 ExpandReferences: true, 240 }) 241 if err != nil { 242 t.Fatal(errors.Details(err, nil)) 243 } 244 245 var out = &bytes.Buffer{} 246 _ = json.Indent(out, b, "", " ") 247 t.Error(out.String()) 248 }