...

Source file src/github.com/go-openapi/analysis/schema_test.go

Documentation: github.com/go-openapi/analysis

     1  package analysis
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"path"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/go-openapi/analysis/internal/antest"
    13  	"github.com/go-openapi/spec"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  var knownSchemas = []*spec.Schema{
    19  	spec.BoolProperty(),                  // 0
    20  	spec.StringProperty(),                // 1
    21  	spec.Int8Property(),                  // 2
    22  	spec.Int16Property(),                 // 3
    23  	spec.Int32Property(),                 // 4
    24  	spec.Int64Property(),                 // 5
    25  	spec.Float32Property(),               // 6
    26  	spec.Float64Property(),               // 7
    27  	spec.DateProperty(),                  // 8
    28  	spec.DateTimeProperty(),              // 9
    29  	(&spec.Schema{}),                     // 10
    30  	(&spec.Schema{}).Typed("object", ""), // 11
    31  	(&spec.Schema{}).Typed("", ""),       // 12
    32  	(&spec.Schema{}).Typed("", "uuid"),   // 13
    33  }
    34  
    35  func TestSchemaAnalysis_KnownTypes(t *testing.T) {
    36  	for i, v := range knownSchemas {
    37  		sch, err := Schema(SchemaOpts{Schema: v})
    38  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    39  		assert.Truef(t, sch.IsKnownType, "item at %d should be a known type", i)
    40  	}
    41  
    42  	for i, v := range complexSchemas {
    43  		sch, err := Schema(SchemaOpts{Schema: v})
    44  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    45  		assert.Falsef(t, sch.IsKnownType, "item at %d should not be a known type", i)
    46  	}
    47  
    48  	serv := refServer()
    49  	defer serv.Close()
    50  
    51  	for i, ref := range knownRefs(serv.URL) {
    52  		sch, err := Schema(SchemaOpts{Schema: refSchema(ref)})
    53  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    54  		assert.Truef(t, sch.IsKnownType, "item at %d should be a known type", i)
    55  	}
    56  
    57  	for i, ref := range complexRefs(serv.URL) {
    58  		sch, err := Schema(SchemaOpts{Schema: refSchema(ref)})
    59  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    60  		assert.Falsef(t, sch.IsKnownType, "item at %d should not be a known type", i)
    61  	}
    62  }
    63  
    64  func TestSchemaAnalysis_Array(t *testing.T) {
    65  	for i, v := range append(knownSchemas, (&spec.Schema{}).Typed("array", "")) {
    66  		sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)})
    67  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    68  		assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
    69  		assert.Truef(t, sch.IsSimpleArray, "item at %d should be a simple array type", i)
    70  	}
    71  
    72  	for i, v := range complexSchemas {
    73  		sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)})
    74  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    75  		assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
    76  		assert.Falsef(t, sch.IsSimpleArray, "item at %d should not be a simple array type", i)
    77  	}
    78  
    79  	serv := refServer()
    80  	defer serv.Close()
    81  
    82  	for i, ref := range knownRefs(serv.URL) {
    83  		sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(refSchema(ref))})
    84  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    85  		assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
    86  		assert.Truef(t, sch.IsSimpleArray, "item at %d should be a simple array type", i)
    87  	}
    88  
    89  	for i, ref := range complexRefs(serv.URL) {
    90  		sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(refSchema(ref))})
    91  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
    92  		assert.Falsef(t, sch.IsKnownType, "item at %d should not be a known type", i)
    93  		assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
    94  		assert.Falsef(t, sch.IsSimpleArray, "item at %d should not be a simple array type", i)
    95  	}
    96  
    97  	// edge case: unrestricted array (beyond Swagger)
    98  	at := spec.ArrayProperty(nil)
    99  	at.Items = nil
   100  	sch, err := Schema(SchemaOpts{Schema: at})
   101  	require.NoError(t, err)
   102  	assert.True(t, sch.IsArray)
   103  	assert.False(t, sch.IsTuple)
   104  	assert.False(t, sch.IsKnownType)
   105  	assert.True(t, sch.IsSimpleSchema)
   106  
   107  	// unrestricted array with explicit empty schema
   108  	at = spec.ArrayProperty(nil)
   109  	at.Items = &spec.SchemaOrArray{}
   110  	sch, err = Schema(SchemaOpts{Schema: at})
   111  	require.NoError(t, err)
   112  	assert.True(t, sch.IsArray)
   113  	assert.False(t, sch.IsTuple)
   114  	assert.False(t, sch.IsKnownType)
   115  	assert.True(t, sch.IsSimpleSchema)
   116  }
   117  
   118  func TestSchemaAnalysis_Map(t *testing.T) {
   119  	for i, v := range append(knownSchemas, spec.MapProperty(nil)) {
   120  		sch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)})
   121  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
   122  		assert.Truef(t, sch.IsMap, "item at %d should be a map type", i)
   123  		assert.Truef(t, sch.IsSimpleMap, "item at %d should be a simple map type", i)
   124  	}
   125  
   126  	for i, v := range complexSchemas {
   127  		sch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)})
   128  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
   129  		assert.Truef(t, sch.IsMap, "item at %d should be a map type", i)
   130  		assert.Falsef(t, sch.IsSimpleMap, "item at %d should not be a simple map type", i)
   131  	}
   132  }
   133  
   134  func TestSchemaAnalysis_ExtendedObject(t *testing.T) {
   135  	for i, v := range knownSchemas {
   136  		wex := spec.MapProperty(v).SetProperty("name", *spec.StringProperty())
   137  		sch, err := Schema(SchemaOpts{Schema: wex})
   138  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
   139  		assert.Truef(t, sch.IsExtendedObject, "item at %d should be an extended map object type", i)
   140  		assert.Falsef(t, sch.IsMap, "item at %d should not be a map type", i)
   141  		assert.Falsef(t, sch.IsSimpleMap, "item at %d should not be a simple map type", i)
   142  	}
   143  }
   144  
   145  func TestSchemaAnalysis_Tuple(t *testing.T) {
   146  	at := spec.ArrayProperty(nil)
   147  	at.Items = &spec.SchemaOrArray{}
   148  	at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty(), *spec.Int64Property())
   149  
   150  	sch, err := Schema(SchemaOpts{Schema: at})
   151  	require.NoError(t, err)
   152  	assert.True(t, sch.IsTuple)
   153  	assert.False(t, sch.IsTupleWithExtra)
   154  	assert.False(t, sch.IsKnownType)
   155  	assert.False(t, sch.IsSimpleSchema)
   156  
   157  	// edge case: tuple with a single element
   158  	at.Items = &spec.SchemaOrArray{}
   159  	at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty())
   160  	sch, err = Schema(SchemaOpts{Schema: at})
   161  	require.NoError(t, err)
   162  	assert.True(t, sch.IsTuple)
   163  	assert.False(t, sch.IsTupleWithExtra)
   164  	assert.False(t, sch.IsKnownType)
   165  	assert.False(t, sch.IsSimpleSchema)
   166  }
   167  
   168  func TestSchemaAnalysis_TupleWithExtra(t *testing.T) {
   169  	at := spec.ArrayProperty(nil)
   170  	at.Items = &spec.SchemaOrArray{}
   171  	at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty(), *spec.Int64Property())
   172  	at.AdditionalItems = &spec.SchemaOrBool{Allows: true}
   173  	at.AdditionalItems.Schema = spec.Int32Property()
   174  
   175  	sch, err := Schema(SchemaOpts{Schema: at})
   176  	require.NoError(t, err)
   177  	assert.False(t, sch.IsTuple)
   178  	assert.True(t, sch.IsTupleWithExtra)
   179  	assert.False(t, sch.IsKnownType)
   180  	assert.False(t, sch.IsSimpleSchema)
   181  }
   182  
   183  func TestSchemaAnalysis_BaseType(t *testing.T) {
   184  	cl := (&spec.Schema{}).Typed("object", "").SetProperty("type", *spec.StringProperty()).WithDiscriminator("type")
   185  
   186  	sch, err := Schema(SchemaOpts{Schema: cl})
   187  	require.NoError(t, err)
   188  	assert.True(t, sch.IsBaseType)
   189  	assert.False(t, sch.IsKnownType)
   190  	assert.False(t, sch.IsSimpleSchema)
   191  }
   192  
   193  func TestSchemaAnalysis_SimpleSchema(t *testing.T) {
   194  	for i, v := range append(knownSchemas, spec.ArrayProperty(nil), spec.MapProperty(nil)) {
   195  		sch, err := Schema(SchemaOpts{Schema: v})
   196  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
   197  		assert.Truef(t, sch.IsSimpleSchema, "item at %d should be a simple schema", i)
   198  
   199  		asch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)})
   200  		require.NoErrorf(t, err, "failed to analyze array schema at %d: %v", i, err)
   201  		assert.Truef(t, asch.IsSimpleSchema, "array item at %d should be a simple schema", i)
   202  
   203  		msch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)})
   204  		require.NoErrorf(t, err, "failed to analyze map schema at %d: %v", i, err)
   205  		assert.Truef(t, msch.IsSimpleSchema, "map item at %d should be a simple schema", i)
   206  	}
   207  
   208  	for i, v := range complexSchemas {
   209  		sch, err := Schema(SchemaOpts{Schema: v})
   210  		require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
   211  		assert.Falsef(t, sch.IsSimpleSchema, "item at %d should not be a simple schema", i)
   212  	}
   213  }
   214  
   215  func TestSchemaAnalys_InvalidSchema(t *testing.T) {
   216  	// explore error cases in schema analysis:
   217  	// the only cause for failure is a wrong $ref at some place
   218  	bp := filepath.Join("fixtures", "bugs", "1602", "other-invalid-pointers.yaml")
   219  	sp := antest.LoadOrFail(t, bp)
   220  
   221  	// invalid ref not detected (no digging further)
   222  	def := sp.Definitions["invalidRefInObject"]
   223  	_, err := Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
   224  	require.NoError(t, err, "did not expect an error here, in spite of the underlying invalid $ref")
   225  
   226  	def = sp.Definitions["invalidRefInTuple"]
   227  	_, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
   228  	require.NoError(t, err, "did not expect an error here, in spite of the underlying invalid $ref")
   229  
   230  	// invalid ref detected (digging)
   231  	schema := refSchema(spec.MustCreateRef("#/definitions/noWhere"))
   232  	_, err = Schema(SchemaOpts{Schema: schema, Root: sp, BasePath: bp})
   233  	require.Error(t, err, "expected an error here")
   234  
   235  	def = sp.Definitions["invalidRefInMap"]
   236  	_, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
   237  	require.Error(t, err, "expected an error here")
   238  
   239  	def = sp.Definitions["invalidRefInArray"]
   240  	_, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
   241  	require.Error(t, err, "expected an error here")
   242  
   243  	def = sp.Definitions["indirectToInvalidRef"]
   244  	_, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
   245  	require.Error(t, err, "expected an error here")
   246  }
   247  
   248  func TestSchemaAnalysis_EdgeCases(t *testing.T) {
   249  	t.Parallel()
   250  
   251  	_, err := Schema(SchemaOpts{Schema: nil})
   252  	require.Error(t, err)
   253  }
   254  
   255  /* helpers for the Schema test suite */
   256  
   257  func newCObj() *spec.Schema {
   258  	return (&spec.Schema{}).Typed("object", "").SetProperty("id", *spec.Int64Property())
   259  }
   260  
   261  var complexObject = newCObj()
   262  
   263  var complexSchemas = []*spec.Schema{
   264  	complexObject,
   265  	spec.ArrayProperty(complexObject),
   266  	spec.MapProperty(complexObject),
   267  }
   268  
   269  func knownRefs(base string) []spec.Ref {
   270  	urls := []string{"bool", "string", "integer", "float", "date", "object", "format"}
   271  
   272  	result := make([]spec.Ref, 0, len(urls))
   273  	for _, u := range urls {
   274  		result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("known", u))))
   275  	}
   276  
   277  	return result
   278  }
   279  
   280  func complexRefs(base string) []spec.Ref {
   281  	urls := []string{"object", "array", "map"}
   282  
   283  	result := make([]spec.Ref, 0, len(urls))
   284  	for _, u := range urls {
   285  		result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("complex", u))))
   286  	}
   287  
   288  	return result
   289  }
   290  
   291  func refServer() *httptest.Server {
   292  	mux := http.NewServeMux()
   293  	mux.Handle("/known/bool", schemaHandler(knownSchemas[0]))
   294  	mux.Handle("/known/string", schemaHandler(knownSchemas[1]))
   295  	mux.Handle("/known/integer", schemaHandler(knownSchemas[5]))
   296  	mux.Handle("/known/float", schemaHandler(knownSchemas[6]))
   297  	mux.Handle("/known/date", schemaHandler(knownSchemas[8]))
   298  	mux.Handle("/known/object", schemaHandler(knownSchemas[11]))
   299  	mux.Handle("/known/format", schemaHandler(knownSchemas[13]))
   300  
   301  	mux.Handle("/complex/object", schemaHandler(complexSchemas[0]))
   302  	mux.Handle("/complex/array", schemaHandler(complexSchemas[1]))
   303  	mux.Handle("/complex/map", schemaHandler(complexSchemas[2]))
   304  
   305  	return httptest.NewServer(mux)
   306  }
   307  
   308  func refSchema(ref spec.Ref) *spec.Schema {
   309  	return &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
   310  }
   311  
   312  func schemaHandler(schema *spec.Schema) http.Handler {
   313  	return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
   314  		writeJSON(w, schema)
   315  	})
   316  }
   317  
   318  func writeJSON(w http.ResponseWriter, data interface{}) {
   319  	w.Header().Add("Content-Type", "application/json")
   320  	w.WriteHeader(http.StatusOK)
   321  	enc := json.NewEncoder(w)
   322  
   323  	if err := enc.Encode(data); err != nil {
   324  		panic(err)
   325  	}
   326  }
   327  

View as plain text