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  }