github.com/go-swagger/go-swagger@v0.31.0/codescan/routes_test.go (about)

     1  package codescan
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	"github.com/go-openapi/spec"
     9  	"github.com/stretchr/testify/assert"
    10  )
    11  
    12  func TestRouteExpression(t *testing.T) {
    13  	assert.Regexp(t, rxRoute, "swagger:route DELETE /orders/{id} deleteOrder")
    14  	assert.Regexp(t, rxRoute, "swagger:route GET /v1.2/something deleteOrder")
    15  }
    16  
    17  func TestRoutesParser(t *testing.T) {
    18  	sctx := loadClassificationPkgsCtx(t)
    19  	var ops spec.Paths
    20  	for _, apiPath := range sctx.app.Routes {
    21  		prs := &routesBuilder{
    22  			ctx:        sctx,
    23  			route:      apiPath,
    24  			operations: make(map[string]*spec.Operation),
    25  		}
    26  		require.NoError(t, prs.Build(&ops))
    27  	}
    28  
    29  	assert.Len(t, ops.Paths, 3)
    30  
    31  	po, ok := ops.Paths["/pets"]
    32  	ext := make(spec.Extensions)
    33  	ext.Add("x-some-flag", "true")
    34  	assert.True(t, ok)
    35  	assert.NotNil(t, po.Get)
    36  	assertOperation(t,
    37  		po.Get,
    38  		"listPets",
    39  		"Lists pets filtered by some parameters.",
    40  		"This will show all available pets by default.\nYou can get the pets that are out of stock",
    41  		[]string{"pets", "users"},
    42  		[]string{"read", "write"},
    43  		ext,
    44  	)
    45  	assertOperation(t,
    46  		po.Post,
    47  		"createPet",
    48  		"Create a pet based on the parameters.",
    49  		"",
    50  		[]string{"pets", "users"},
    51  		[]string{"read", "write"},
    52  		nil,
    53  	)
    54  
    55  	po, ok = ops.Paths["/orders"]
    56  	ext = make(spec.Extensions)
    57  	ext.Add("x-some-flag", "false")
    58  	ext.Add("x-some-list", []string{"item1", "item2", "item3"})
    59  	ext.Add("x-some-object", map[string]interface{}{
    60  		"key1": "value1",
    61  		"key2": "value2",
    62  		"subobject": map[string]interface{}{
    63  			"subkey1": "subvalue1",
    64  			"subkey2": "subvalue2",
    65  		},
    66  		"key3": "value3",
    67  	})
    68  	assert.True(t, ok)
    69  	assert.NotNil(t, po.Get)
    70  	assertOperation(t,
    71  		po.Get,
    72  		"listOrders",
    73  		"lists orders filtered by some parameters.",
    74  		"",
    75  		[]string{"orders"},
    76  		[]string{"orders:read", "https://www.googleapis.com/auth/userinfo.email"},
    77  		ext,
    78  	)
    79  	assertOperation(t,
    80  		po.Post,
    81  		"createOrder",
    82  		"create an order based on the parameters.",
    83  		"",
    84  		[]string{"orders"},
    85  		[]string{"read", "write"},
    86  		nil,
    87  	)
    88  
    89  	po, ok = ops.Paths["/orders/{id}"]
    90  	assert.True(t, ok)
    91  	assert.NotNil(t, po.Get)
    92  	assertOperation(t,
    93  		po.Get,
    94  		"orderDetails",
    95  		"gets the details for an order.",
    96  		"",
    97  		[]string{"orders"},
    98  		[]string{"read", "write"},
    99  		nil,
   100  	)
   101  
   102  	assertOperation(t,
   103  		po.Put,
   104  		"updateOrder",
   105  		"Update the details for an order.",
   106  		"When the order doesn't exist this will return an error.",
   107  		[]string{"orders"},
   108  		[]string{"read", "write"},
   109  		nil,
   110  	)
   111  
   112  	assertOperation(t,
   113  		po.Delete,
   114  		"deleteOrder",
   115  		"delete a particular order.",
   116  		"",
   117  		nil,
   118  		[]string{"read", "write"},
   119  		nil,
   120  	)
   121  
   122  	// additional check description tag at Responses
   123  	rsp, ok := po.Delete.Responses.StatusCodeResponses[202]
   124  	assert.True(t, ok)
   125  	assert.Equal(t, "Some description", rsp.Description)
   126  	assert.Equal(t, "", rsp.Ref.String())
   127  }
   128  
   129  func TestRoutesParserBody(t *testing.T) {
   130  	sctx, err := newScanCtx(&Options{
   131  		Packages: []string{
   132  			"github.com/go-swagger/go-swagger/fixtures/goparsing/classification",
   133  			"github.com/go-swagger/go-swagger/fixtures/goparsing/classification/models",
   134  			"github.com/go-swagger/go-swagger/fixtures/goparsing/classification/operations",
   135  			"github.com/go-swagger/go-swagger/fixtures/goparsing/classification/operations_body",
   136  		},
   137  	})
   138  	require.NoError(t, err)
   139  	var ops spec.Paths
   140  	for _, apiPath := range sctx.app.Routes {
   141  		prs := &routesBuilder{
   142  			ctx:        sctx,
   143  			route:      apiPath,
   144  			operations: make(map[string]*spec.Operation),
   145  		}
   146  		require.NoError(t, prs.Build(&ops))
   147  	}
   148  
   149  	assert.Len(t, ops.Paths, 4)
   150  
   151  	po, ok := ops.Paths["/pets"]
   152  	assert.True(t, ok)
   153  	assert.NotNil(t, po.Get)
   154  	assertOperationBody(t,
   155  		po.Get,
   156  		"listPets",
   157  		"Lists pets filtered by some parameters.",
   158  		"This will show all available pets by default.\nYou can get the pets that are out of stock",
   159  		[]string{"pets", "users"},
   160  		[]string{"read", "write"},
   161  	)
   162  	assert.NotNil(t, po.Post)
   163  
   164  	assertOperationBody(t,
   165  		po.Post,
   166  		"createPet",
   167  		"Create a pet based on the parameters.",
   168  		"",
   169  		[]string{"pets", "users"},
   170  		[]string{"read", "write"},
   171  	)
   172  
   173  	po, ok = ops.Paths["/orders"]
   174  	assert.True(t, ok)
   175  	assert.NotNil(t, po.Get)
   176  	assertOperationBody(t,
   177  		po.Get,
   178  		"listOrders",
   179  		"lists orders filtered by some parameters.",
   180  		"",
   181  		[]string{"orders"},
   182  		[]string{"orders:read", "https://www.googleapis.com/auth/userinfo.email"},
   183  	)
   184  	assert.NotNil(t, po.Post)
   185  
   186  	assertOperationBody(t,
   187  		po.Post,
   188  		"createOrder",
   189  		"create an order based on the parameters.",
   190  		"",
   191  		[]string{"orders"},
   192  		[]string{"read", "write"},
   193  	)
   194  
   195  	po, ok = ops.Paths["/orders/{id}"]
   196  	assert.True(t, ok)
   197  	assert.NotNil(t, po.Get)
   198  	assertOperationBody(t,
   199  		po.Get,
   200  		"orderDetails",
   201  		"gets the details for an order.",
   202  		"",
   203  		[]string{"orders"},
   204  		[]string{"read", "write"},
   205  	)
   206  
   207  	assertOperationBody(t,
   208  		po.Put,
   209  		"updateOrder",
   210  		"Update the details for an order.",
   211  		"When the order doesn't exist this will return an error.",
   212  		[]string{"orders"},
   213  		[]string{"read", "write"},
   214  	)
   215  
   216  	assertOperationBody(t,
   217  		po.Delete,
   218  		"deleteOrder",
   219  		"delete a particular order.",
   220  		"",
   221  		nil,
   222  		[]string{"read", "write"},
   223  	)
   224  
   225  	validateRoutesParameters(t, ops)
   226  }
   227  
   228  func validateRoutesParameters(t *testing.T, ops spec.Paths) {
   229  	po := ops.Paths["/pets"]
   230  	assert.Len(t, po.Post.Parameters, 2)
   231  
   232  	// Testing standard param properties
   233  	p := po.Post.Parameters[0]
   234  	assert.Equal(t, "request", p.Name)
   235  	assert.Equal(t, "body", p.In)
   236  	assert.Equal(t, "The request model.", p.Description)
   237  
   238  	// Testing "required" and "allowEmpty"
   239  	p = po.Post.Parameters[1]
   240  	assert.Equal(t, "id", p.Name)
   241  	assert.Equal(t, "The pet id", p.Description)
   242  	assert.Equal(t, "path", p.In)
   243  	assert.True(t, p.Required)
   244  	assert.False(t, p.AllowEmptyValue)
   245  
   246  	po = ops.Paths["/orders"]
   247  	assert.Len(t, po.Post.Parameters, 2)
   248  
   249  	// Testing invalid value for "in"
   250  	p = po.Post.Parameters[0]
   251  	assert.Equal(t, "id", p.Name)
   252  	assert.Equal(t, "The order id", p.Description)
   253  	assert.Equal(t, "", p.In) // Invalid value should not be set
   254  	assert.False(t, p.Required)
   255  	assert.True(t, p.AllowEmptyValue)
   256  
   257  	p = po.Post.Parameters[1]
   258  	assert.Equal(t, "request", p.Name)
   259  	assert.Equal(t, "body", p.In)
   260  	assert.Equal(t, "The request model.", p.Description)
   261  
   262  	po = ops.Paths["/param-test"]
   263  	assert.Len(t, po.Post.Parameters, 6)
   264  
   265  	// Testing number param with "max" and "min" constraints
   266  	p = po.Post.Parameters[0]
   267  	assert.Equal(t, "someNumber", p.Name)
   268  	assert.Equal(t, "some number", p.Description)
   269  	assert.Equal(t, "path", p.In)
   270  	assert.True(t, p.Required)
   271  	assert.Equal(t, "number", p.Type)
   272  	min, max, def := float64(10), float64(20), float64(15)
   273  	assert.Equal(t, &max, p.Maximum)
   274  	assert.Equal(t, &min, p.Minimum)
   275  	assert.Equal(t, def, p.Default)
   276  	assert.Nil(t, p.Schema)
   277  
   278  	// Testing array param provided as query string. Testing "minLength" and "maxLength" constraints for "array" types
   279  	p = po.Post.Parameters[1]
   280  	assert.Equal(t, "someQuery", p.Name)
   281  	assert.Equal(t, "some query values", p.Description)
   282  	assert.Equal(t, "query", p.In)
   283  	assert.False(t, p.Required)
   284  	assert.Equal(t, "array", p.Type)
   285  	minLen, maxLen := int64(5), int64(20)
   286  	assert.Equal(t, &maxLen, p.MaxLength)
   287  	assert.Equal(t, &minLen, p.MinLength)
   288  	assert.Nil(t, p.Schema)
   289  
   290  	// Testing boolean param with default value
   291  	p = po.Post.Parameters[2]
   292  	assert.Equal(t, "someBoolean", p.Name)
   293  	assert.Equal(t, "some boolean", p.Description)
   294  	assert.Equal(t, "path", p.In)
   295  	assert.False(t, p.Required)
   296  	assert.Equal(t, "boolean", p.Type)
   297  	someBoolean, ok := p.Default.(bool)
   298  	assert.True(t, ok)
   299  	assert.True(t, someBoolean)
   300  	assert.Nil(t, p.Schema)
   301  
   302  	// Testing that "min", "max", "minLength" and "maxLength" constraints will only be considered if the right type is provided
   303  	p = po.Post.Parameters[3]
   304  	assert.Equal(t, "constraintsOnInvalidType", p.Name)
   305  	assert.Equal(t, "test constraints on invalid types", p.Description)
   306  	assert.Equal(t, "query", p.In)
   307  	assert.Equal(t, "boolean", p.Type)
   308  	assert.Nil(t, p.Maximum)
   309  	assert.Nil(t, p.Minimum)
   310  	assert.Nil(t, p.MaxLength)
   311  	assert.Nil(t, p.MinLength)
   312  	assert.Equal(t, "abcde", p.Format)
   313  	constraintsOnInvalidType, ok2 := p.Default.(bool)
   314  	assert.True(t, ok2)
   315  	assert.False(t, constraintsOnInvalidType)
   316  	assert.Nil(t, p.Schema)
   317  
   318  	// Testing that when "type" is not provided, a schema will not be created
   319  	p = po.Post.Parameters[4]
   320  	assert.Equal(t, "noType", p.Name)
   321  	assert.Equal(t, "test no type", p.Description)
   322  	assert.Equal(t, "", p.Type)
   323  	assert.Nil(t, p.Schema)
   324  
   325  	// Testing a request body that takes a string value defined by a list of possible values in "enum"
   326  	p = po.Post.Parameters[5]
   327  	assert.Equal(t, "request", p.Name)
   328  	assert.Equal(t, "The request model.", p.Description)
   329  	assert.Equal(t, "body", p.In)
   330  	assert.Equal(t, "string", p.Schema.Type[0])
   331  	assert.Equal(t, "orange", p.Schema.Default)
   332  	assert.Equal(t, []interface{}{"apple", "orange", "pineapple", "peach", "plum"}, p.Schema.Enum)
   333  	assert.Empty(t, "", p.Type)
   334  }
   335  
   336  func assertOperation(t *testing.T, op *spec.Operation, id, summary, description string, tags, scopes []string, extensions spec.Extensions) {
   337  	assert.NotNil(t, op)
   338  	assert.Equal(t, summary, op.Summary)
   339  	assert.Equal(t, description, op.Description)
   340  	assert.Equal(t, id, op.ID)
   341  	assert.EqualValues(t, tags, op.Tags)
   342  	assert.EqualValues(t, []string{"application/json", "application/x-protobuf"}, op.Consumes)
   343  	assert.EqualValues(t, []string{"application/json", "application/x-protobuf"}, op.Produces)
   344  	assert.EqualValues(t, []string{"http", "https", "ws", "wss"}, op.Schemes)
   345  	assert.Len(t, op.Security, 2)
   346  	akv, ok := op.Security[0]["api_key"]
   347  	assert.True(t, ok)
   348  	// akv must be defined & not empty
   349  	assert.NotNil(t, akv)
   350  	assert.Empty(t, akv)
   351  
   352  	vv, ok := op.Security[1]["oauth"]
   353  	assert.True(t, ok)
   354  	assert.EqualValues(t, scopes, vv)
   355  
   356  	assert.NotNil(t, op.Responses.Default)
   357  	assert.Equal(t, "#/responses/genericError", op.Responses.Default.Ref.String())
   358  
   359  	rsp, ok := op.Responses.StatusCodeResponses[200]
   360  	assert.True(t, ok)
   361  	assert.Equal(t, "#/responses/someResponse", rsp.Ref.String())
   362  	rsp, ok = op.Responses.StatusCodeResponses[422]
   363  	assert.True(t, ok)
   364  	assert.Equal(t, "#/responses/validationError", rsp.Ref.String())
   365  
   366  	ext := op.Extensions
   367  	assert.Equal(t, extensions, ext)
   368  }
   369  
   370  func assertOperationBody(t *testing.T, op *spec.Operation, id, summary, description string, tags, scopes []string) {
   371  	assert.NotNil(t, op)
   372  	assert.Equal(t, summary, op.Summary)
   373  	assert.Equal(t, description, op.Description)
   374  	assert.Equal(t, id, op.ID)
   375  	assert.EqualValues(t, tags, op.Tags)
   376  	assert.EqualValues(t, []string{"application/json", "application/x-protobuf"}, op.Consumes)
   377  	assert.EqualValues(t, []string{"application/json", "application/x-protobuf"}, op.Produces)
   378  	assert.EqualValues(t, []string{"http", "https", "ws", "wss"}, op.Schemes)
   379  	assert.Len(t, op.Security, 2)
   380  	akv, ok := op.Security[0]["api_key"]
   381  	assert.True(t, ok)
   382  	// akv must be defined & not empty
   383  	assert.NotNil(t, akv)
   384  	assert.Empty(t, akv)
   385  
   386  	vv, ok := op.Security[1]["oauth"]
   387  	assert.True(t, ok)
   388  	assert.EqualValues(t, scopes, vv)
   389  
   390  	assert.NotNil(t, op.Responses.Default)
   391  	assert.Equal(t, "", op.Responses.Default.Ref.String())
   392  	assert.Equal(t, "#/definitions/genericError", op.Responses.Default.Schema.Ref.String())
   393  
   394  	rsp, ok := op.Responses.StatusCodeResponses[200]
   395  	assert.True(t, ok)
   396  	assert.Equal(t, "", rsp.Ref.String())
   397  	assert.Equal(t, "#/definitions/someResponse", rsp.Schema.Ref.String())
   398  	rsp, ok = op.Responses.StatusCodeResponses[422]
   399  	assert.True(t, ok)
   400  	assert.Equal(t, "", rsp.Ref.String())
   401  	assert.Equal(t, "#/definitions/validationError", rsp.Schema.Ref.String())
   402  }