...

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

Documentation: github.com/go-openapi/analysis

     1  package analysis
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/go-openapi/analysis/internal/antest"
     9  	"github.com/go-openapi/analysis/internal/flatten/operations"
    10  	"github.com/go-openapi/analysis/internal/flatten/sortref"
    11  	"github.com/go-openapi/jsonpointer"
    12  	"github.com/go-openapi/spec"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestName_FromRef(t *testing.T) {
    18  	t.Parallel()
    19  
    20  	values := []struct{ Source, Expected string }{
    21  		{"#/definitions/errorModel", "errorModel"},
    22  		{"http://somewhere.com/definitions/errorModel", "errorModel"},
    23  		{"http://somewhere.com/definitions/errorModel.json", "errorModel"},
    24  		{"/definitions/errorModel", "errorModel"},
    25  		{"/definitions/errorModel.json", "errorModel"},
    26  		{"http://somewhere.com", "somewhereCom"},
    27  		{"#", ""},
    28  	}
    29  
    30  	for _, v := range values {
    31  		assert.Equal(t, v.Expected, nameFromRef(spec.MustCreateRef(v.Source), &FlattenOpts{}))
    32  	}
    33  }
    34  
    35  func TestName_FromRefMangle(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	values := []struct{ Source, Expected, ExpectedKeepName string }{
    39  		{"#/definitions/ErrorModel", "errorModel", "ErrorModel"},
    40  		{"#/definitions/Error_Model", "errorModel", "Error_Model"},
    41  		{"http://somewhere.com/definitions/errorModel", "errorModel", "errorModel"},
    42  		{"http://somewhere.com/definitions/ErrorModel.json", "errorModel", "ErrorModel"},
    43  		{"/definitions/ErrorModel", "errorModel", "ErrorModel"},
    44  		{"/definitions/ErrorModel.json", "errorModel", "ErrorModel"},
    45  		{"http://somewhere.com", "somewhereCom", "somewhere com"},
    46  		{"#", "", ""},
    47  	}
    48  
    49  	for _, v := range values {
    50  		assert.Equal(t, v.Expected, nameFromRef(spec.MustCreateRef(v.Source), &FlattenOpts{}))
    51  		assert.Equal(t, v.ExpectedKeepName, nameFromRef(spec.MustCreateRef(v.Source), &FlattenOpts{KeepNames: true}))
    52  	}
    53  }
    54  
    55  func TestName_Definition(t *testing.T) {
    56  	values := []struct {
    57  		Source, Expected string
    58  		Definitions      spec.Definitions
    59  	}{
    60  		{"#/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)},
    61  		{"http://somewhere.com/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)},
    62  		{"#/definitions/errorModel", "errorModel", map[string]spec.Schema{"apples": *spec.StringProperty()}},
    63  		{"#/definitions/errorModel", "errorModelOAIGen", map[string]spec.Schema{"errorModel": *spec.StringProperty()}},
    64  		{"#/definitions/errorModel", "errorModelOAIGen1",
    65  			map[string]spec.Schema{"errorModel": *spec.StringProperty(), "errorModelOAIGen": *spec.StringProperty()}},
    66  		{"#", "oaiGen", nil},
    67  	}
    68  
    69  	for _, v := range values {
    70  		u, _ := uniqifyName(v.Definitions, nameFromRef(spec.MustCreateRef(v.Source), &FlattenOpts{}))
    71  		assert.Equal(t, v.Expected, u)
    72  	}
    73  }
    74  
    75  func TestName_SplitKey(t *testing.T) {
    76  	type KeyFlag uint64
    77  
    78  	const (
    79  		isOperation KeyFlag = 1 << iota
    80  		isDefinition
    81  		isSharedOperationParam
    82  		isOperationParam
    83  		isOperationResponse
    84  		isDefaultResponse
    85  		isStatusCodeResponse
    86  	)
    87  
    88  	values := []struct {
    89  		Key         string
    90  		Flags       KeyFlag
    91  		PathItemRef spec.Ref
    92  		PathRef     spec.Ref
    93  		Name        string
    94  	}{
    95  		{
    96  			"#/paths/~1some~1where~1{id}/parameters/1/schema",
    97  			isOperation | isSharedOperationParam,
    98  			spec.Ref{},
    99  			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
   100  			"",
   101  		},
   102  		{
   103  			"#/paths/~1some~1where~1{id}/get/parameters/2/schema",
   104  			isOperation | isOperationParam,
   105  			spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"),
   106  			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
   107  			"",
   108  		},
   109  		{
   110  			"#/paths/~1some~1where~1{id}/get/responses/default/schema",
   111  			isOperation | isOperationResponse | isDefaultResponse,
   112  			spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"),
   113  			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
   114  			"Default",
   115  		},
   116  		{
   117  			"#/paths/~1some~1where~1{id}/get/responses/200/schema",
   118  			isOperation | isOperationResponse | isStatusCodeResponse,
   119  			spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"),
   120  			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
   121  			"OK",
   122  		},
   123  		{
   124  			"#/definitions/namedAgain",
   125  			isDefinition,
   126  			spec.Ref{},
   127  			spec.Ref{},
   128  			"namedAgain",
   129  		},
   130  		{
   131  			"#/definitions/datedRecords/items/1",
   132  			isDefinition,
   133  			spec.Ref{},
   134  			spec.Ref{},
   135  			"datedRecords",
   136  		},
   137  		{
   138  			"#/definitions/datedRecords/items/1",
   139  			isDefinition,
   140  			spec.Ref{},
   141  			spec.Ref{},
   142  			"datedRecords",
   143  		},
   144  		{
   145  			"#/definitions/datedTaggedRecords/items/1",
   146  			isDefinition,
   147  			spec.Ref{},
   148  			spec.Ref{},
   149  			"datedTaggedRecords",
   150  		},
   151  		{
   152  			"#/definitions/datedTaggedRecords/additionalItems",
   153  			isDefinition,
   154  			spec.Ref{},
   155  			spec.Ref{},
   156  			"datedTaggedRecords",
   157  		},
   158  		{
   159  			"#/definitions/otherRecords/items",
   160  			isDefinition,
   161  			spec.Ref{},
   162  			spec.Ref{},
   163  			"otherRecords",
   164  		},
   165  		{
   166  			"#/definitions/tags/additionalProperties",
   167  			isDefinition,
   168  			spec.Ref{},
   169  			spec.Ref{},
   170  			"tags",
   171  		},
   172  		{
   173  			"#/definitions/namedThing/properties/name",
   174  			isDefinition,
   175  			spec.Ref{},
   176  			spec.Ref{},
   177  			"namedThing",
   178  		},
   179  	}
   180  
   181  	for i, v := range values {
   182  		parts := sortref.KeyParts(v.Key)
   183  		pref := parts.PathRef()
   184  		piref := parts.PathItemRef()
   185  		assert.Equal(t, v.PathRef.String(), pref.String(), "pathRef: %s at %d", v.Key, i)
   186  		assert.Equal(t, v.PathItemRef.String(), piref.String(), "pathItemRef: %s at %d", v.Key, i)
   187  
   188  		if v.Flags&isOperation != 0 {
   189  			assert.True(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i)
   190  		} else {
   191  			assert.False(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i)
   192  		}
   193  
   194  		if v.Flags&isDefinition != 0 {
   195  			assert.True(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i)
   196  			assert.Equal(t, v.Name, parts.DefinitionName(), "definition name: %s at %d", v.Key, i)
   197  		} else {
   198  			assert.False(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i)
   199  			if v.Name != "" {
   200  				assert.Equal(t, v.Name, parts.ResponseName(), "response name: %s at %d", v.Key, i)
   201  			}
   202  		}
   203  
   204  		if v.Flags&isOperationParam != 0 {
   205  			assert.True(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i)
   206  		} else {
   207  			assert.False(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i)
   208  		}
   209  
   210  		if v.Flags&isSharedOperationParam != 0 {
   211  			assert.True(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i)
   212  		} else {
   213  			assert.False(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i)
   214  		}
   215  
   216  		if v.Flags&isOperationResponse != 0 {
   217  			assert.True(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i)
   218  		} else {
   219  			assert.False(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i)
   220  		}
   221  
   222  		if v.Flags&isDefaultResponse != 0 {
   223  			assert.True(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i)
   224  		} else {
   225  			assert.False(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i)
   226  		}
   227  
   228  		if v.Flags&isStatusCodeResponse != 0 {
   229  			assert.True(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i)
   230  		} else {
   231  			assert.False(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i)
   232  		}
   233  	}
   234  }
   235  
   236  func TestName_NamesFromKey(t *testing.T) {
   237  	bp := filepath.Join("fixtures", "inline_schemas.yml")
   238  	sp := antest.LoadOrFail(t, bp)
   239  
   240  	values := []struct {
   241  		Key   string
   242  		Names []string
   243  	}{
   244  		{"#/paths/~1some~1where~1{id}/parameters/1/schema",
   245  			[]string{"GetSomeWhereID params body", "PostSomeWhereID params body"}},
   246  		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema", []string{"GetSomeWhereID params body"}},
   247  		{"#/paths/~1some~1where~1{id}/get/responses/default/schema", []string{"GetSomeWhereID Default body"}},
   248  		{"#/paths/~1some~1where~1{id}/get/responses/200/schema", []string{"GetSomeWhereID OK body"}},
   249  		{"#/definitions/namedAgain", []string{"namedAgain"}},
   250  		{"#/definitions/datedTag/allOf/1", []string{"datedTag allOf 1"}},
   251  		{"#/definitions/datedRecords/items/1", []string{"datedRecords tuple 1"}},
   252  		{"#/definitions/datedTaggedRecords/items/1", []string{"datedTaggedRecords tuple 1"}},
   253  		{"#/definitions/datedTaggedRecords/additionalItems", []string{"datedTaggedRecords tuple additionalItems"}},
   254  		{"#/definitions/otherRecords/items", []string{"otherRecords items"}},
   255  		{"#/definitions/tags/additionalProperties", []string{"tags additionalProperties"}},
   256  		{"#/definitions/namedThing/properties/name", []string{"namedThing name"}},
   257  	}
   258  
   259  	for i, v := range values {
   260  		ptr, err := jsonpointer.New(definitionPtr(v.Key)[1:])
   261  		require.NoError(t, err)
   262  
   263  		vv, _, err := ptr.Get(sp)
   264  		require.NoError(t, err)
   265  
   266  		switch tv := vv.(type) {
   267  		case *spec.Schema:
   268  			aschema, err := Schema(SchemaOpts{Schema: tv, Root: sp, BasePath: bp})
   269  			require.NoError(t, err)
   270  			names := namesFromKey(sortref.KeyParts(v.Key), aschema, operations.AllOpRefsByRef(New(sp), nil))
   271  			assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i)
   272  		case spec.Schema:
   273  			aschema, err := Schema(SchemaOpts{Schema: &tv, Root: sp, BasePath: bp})
   274  			require.NoError(t, err)
   275  			names := namesFromKey(sortref.KeyParts(v.Key), aschema, operations.AllOpRefsByRef(New(sp), nil))
   276  			assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i)
   277  		default:
   278  			assert.Fail(t, "unknown type", "got %T", vv)
   279  		}
   280  	}
   281  }
   282  
   283  func TestName_BuildNameWithReservedKeyWord(t *testing.T) {
   284  	s := sortref.SplitKey([]string{"definitions", "fullview", "properties", "properties"})
   285  	startIdx := 2
   286  	segments := []string{"fullview"}
   287  	newName := s.BuildName(segments, startIdx, partAdder(nil))
   288  	assert.Equal(t, "fullview properties", newName)
   289  
   290  	s = sortref.SplitKey([]string{"definitions", "fullview",
   291  		"properties", "properties", "properties", "properties", "properties", "properties"})
   292  	newName = s.BuildName(segments, startIdx, partAdder(nil))
   293  	assert.Equal(t, "fullview"+strings.Repeat(" properties", 3), newName)
   294  }
   295  
   296  func TestName_InlinedSchemas(t *testing.T) {
   297  	values := []struct {
   298  		Key      string
   299  		Location string
   300  		Ref      spec.Ref
   301  	}{
   302  		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2/properties/name",
   303  			"#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name",
   304  			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"),
   305  		},
   306  		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/1",
   307  			"#/definitions/getSomeWhereIdParamsBodyRecord/items/1",
   308  			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"),
   309  		},
   310  
   311  		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2",
   312  			"#/definitions/getSomeWhereIdParamsBodyRecord/items/2",
   313  			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"),
   314  		},
   315  
   316  		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2/properties/name",
   317  			"#/definitions/getSomeWhereIdOKBodyRecordItems2/properties/name",
   318  			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2Name"),
   319  		},
   320  
   321  		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/1",
   322  			"#/definitions/getSomeWhereIdOKBodyRecord/items/1",
   323  			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems1"),
   324  		},
   325  
   326  		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2",
   327  			"#/definitions/getSomeWhereIdOKBodyRecord/items/2",
   328  			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2"),
   329  		},
   330  
   331  		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record",
   332  			"#/definitions/getSomeWhereIdOKBody/properties/record",
   333  			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecord"),
   334  		},
   335  
   336  		{"#/paths/~1some~1where~1{id}/get/responses/200/schema",
   337  			"#/paths/~1some~1where~1{id}/get/responses/200/schema",
   338  			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"),
   339  		},
   340  
   341  		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2/properties/name",
   342  			"#/definitions/getSomeWhereIdDefaultBodyRecordItems2/properties/name",
   343  			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2Name"),
   344  		},
   345  
   346  		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/1",
   347  			"#/definitions/getSomeWhereIdDefaultBodyRecord/items/1",
   348  			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems1"),
   349  		},
   350  
   351  		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2",
   352  			"#/definitions/getSomeWhereIdDefaultBodyRecord/items/2",
   353  			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2"),
   354  		},
   355  
   356  		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record",
   357  			"#/definitions/getSomeWhereIdDefaultBody/properties/record",
   358  			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecord"),
   359  		},
   360  
   361  		{"#/paths/~1some~1where~1{id}/get/responses/default/schema",
   362  			"#/paths/~1some~1where~1{id}/get/responses/default/schema",
   363  			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBody"),
   364  		},
   365  		// maps:
   366  		// {"#/definitions/nestedThing/properties/record/items/2/allOf/1/additionalProperties",
   367  		// "#/definitions/nestedThingRecordItems2AllOf1/additionalProperties",
   368  		// spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1AdditionalProperties"),
   369  		// },
   370  
   371  		// {"#/definitions/nestedThing/properties/record/items/2/allOf/1",
   372  		// "#/definitions/nestedThingRecordItems2/allOf/1",
   373  		// spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1"),
   374  		// },
   375  		{"#/definitions/nestedThing/properties/record/items/2/properties/name",
   376  			"#/definitions/nestedThingRecordItems2/properties/name",
   377  			spec.MustCreateRef("#/definitions/nestedThingRecordItems2Name"),
   378  		},
   379  
   380  		{"#/definitions/nestedThing/properties/record/items/1",
   381  			"#/definitions/nestedThingRecord/items/1",
   382  			spec.MustCreateRef("#/definitions/nestedThingRecordItems1"),
   383  		},
   384  
   385  		{"#/definitions/nestedThing/properties/record/items/2",
   386  			"#/definitions/nestedThingRecord/items/2",
   387  			spec.MustCreateRef("#/definitions/nestedThingRecordItems2"),
   388  		},
   389  
   390  		{"#/definitions/datedRecords/items/1",
   391  			"#/definitions/datedRecords/items/1",
   392  			spec.MustCreateRef("#/definitions/datedRecordsItems1"),
   393  		},
   394  
   395  		{"#/definitions/datedTaggedRecords/items/1",
   396  			"#/definitions/datedTaggedRecords/items/1",
   397  			spec.MustCreateRef("#/definitions/datedTaggedRecordsItems1"),
   398  		},
   399  
   400  		{"#/definitions/namedThing/properties/name",
   401  			"#/definitions/namedThing/properties/name",
   402  			spec.MustCreateRef("#/definitions/namedThingName"),
   403  		},
   404  
   405  		{"#/definitions/nestedThing/properties/record",
   406  			"#/definitions/nestedThing/properties/record",
   407  			spec.MustCreateRef("#/definitions/nestedThingRecord"),
   408  		},
   409  
   410  		{"#/definitions/records/items/0",
   411  			"#/definitions/records/items/0",
   412  			spec.MustCreateRef("#/definitions/recordsItems0"),
   413  		},
   414  
   415  		{"#/definitions/datedTaggedRecords/additionalItems",
   416  			"#/definitions/datedTaggedRecords/additionalItems",
   417  			spec.MustCreateRef("#/definitions/datedTaggedRecordsItemsAdditionalItems"),
   418  		},
   419  
   420  		{"#/definitions/otherRecords/items",
   421  			"#/definitions/otherRecords/items",
   422  			spec.MustCreateRef("#/definitions/otherRecordsItems"),
   423  		},
   424  
   425  		{"#/definitions/tags/additionalProperties",
   426  			"#/definitions/tags/additionalProperties",
   427  			spec.MustCreateRef("#/definitions/tagsAdditionalProperties"),
   428  		},
   429  	}
   430  
   431  	bp := filepath.Join("fixtures", "nested_inline_schemas.yml")
   432  	sp := antest.LoadOrFail(t, bp)
   433  
   434  	require.NoError(t, spec.ExpandSpec(sp, &spec.ExpandOptions{
   435  		RelativeBase: bp,
   436  		SkipSchemas:  true,
   437  	}))
   438  
   439  	require.NoError(t, nameInlinedSchemas(&FlattenOpts{
   440  		Spec:     New(sp),
   441  		BasePath: bp,
   442  	}))
   443  
   444  	for i, v := range values {
   445  		ptr, err := jsonpointer.New(v.Location[1:])
   446  		require.NoErrorf(t, err, "at %d for %s", i, v.Key)
   447  
   448  		vv, _, err := ptr.Get(sp)
   449  		require.NoErrorf(t, err, "at %d for %s", i, v.Key)
   450  
   451  		switch tv := vv.(type) {
   452  		case *spec.Schema:
   453  			assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key)
   454  		case spec.Schema:
   455  			assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key)
   456  		case *spec.SchemaOrBool:
   457  			var sRef spec.Ref
   458  			if tv != nil && tv.Schema != nil {
   459  				sRef = tv.Schema.Ref
   460  			}
   461  			assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
   462  		case *spec.SchemaOrArray:
   463  			var sRef spec.Ref
   464  			if tv != nil && tv.Schema != nil {
   465  				sRef = tv.Schema.Ref
   466  			}
   467  			assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
   468  		default:
   469  			assert.Fail(t, "unknown type", "got %T", vv)
   470  		}
   471  	}
   472  
   473  	for k, rr := range New(sp).allSchemas {
   474  		if strings.HasPrefix(k, "#/responses") || strings.HasPrefix(k, "#/parameters") {
   475  			continue
   476  		}
   477  		if rr.Schema == nil || rr.Schema.Ref.String() != "" || rr.TopLevel {
   478  			continue
   479  		}
   480  		asch, err := Schema(SchemaOpts{Schema: rr.Schema, Root: sp, BasePath: bp})
   481  		require.NoErrorf(t, err, "for key: %s", k)
   482  
   483  		if !asch.IsSimpleSchema && !asch.IsArray && !asch.IsMap {
   484  			assert.Fail(t, "not a top level schema", "for key: %s", k)
   485  		}
   486  	}
   487  }
   488  
   489  func TestFlattenSchema_UnitGuards(t *testing.T) {
   490  	t.Parallel()
   491  
   492  	parts := sortref.KeyParts("#/nowhere/arbitrary/pointer")
   493  	res := GenLocation(parts)
   494  	assert.Equal(t, "", res)
   495  }
   496  
   497  func definitionPtr(key string) string {
   498  	if !strings.HasPrefix(key, "#/definitions") {
   499  		return key
   500  	}
   501  
   502  	return strings.Join(strings.Split(key, "/")[:3], "/")
   503  }
   504  

View as plain text