github.com/jamescostian/go-swagger@v0.30.4-0.20221130163922-68364d6b567b/scan/routes_test.go (about)

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