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 }