github.com/josephspurrier/go-swagger@v0.2.1-0.20221129144919-1f672a142a00/codescan/application_test.go (about) 1 package codescan 2 3 import ( 4 "sort" 5 "testing" 6 7 "github.com/go-openapi/spec" 8 9 "github.com/stretchr/testify/assert" 10 11 "github.com/stretchr/testify/require" 12 ) 13 14 var ( 15 petstoreCtx *scanCtx 16 classificationCtx *scanCtx 17 ) 18 19 func loadPetstorePkgsCtx(t testing.TB) *scanCtx { 20 if petstoreCtx != nil { 21 return petstoreCtx 22 } 23 sctx, err := newScanCtx(&Options{ 24 Packages: []string{"github.com/go-swagger/go-swagger/fixtures/goparsing/petstore/..."}, 25 }) 26 require.NoError(t, err) 27 petstoreCtx = sctx 28 return petstoreCtx 29 } 30 31 func loadClassificationPkgsCtx(t testing.TB, extra ...string) *scanCtx { 32 if classificationCtx != nil { 33 return classificationCtx 34 } 35 sctx, err := newScanCtx(&Options{ 36 Packages: append([]string{ 37 "github.com/go-swagger/go-swagger/fixtures/goparsing/classification", 38 "github.com/go-swagger/go-swagger/fixtures/goparsing/classification/models", 39 "github.com/go-swagger/go-swagger/fixtures/goparsing/classification/operations", 40 }, extra...), 41 }) 42 require.NoError(t, err) 43 classificationCtx = sctx 44 return classificationCtx 45 } 46 47 func TestApplication_LoadCode(t *testing.T) { 48 sctx := loadClassificationPkgsCtx(t) 49 require.NotNil(t, sctx) 50 require.Len(t, sctx.app.Models, 32) 51 require.Len(t, sctx.app.Meta, 1) 52 require.Len(t, sctx.app.Routes, 7) 53 require.Empty(t, sctx.app.Operations) 54 require.Len(t, sctx.app.Parameters, 10) 55 require.Len(t, sctx.app.Responses, 11) 56 } 57 58 func TestAppScanner_NewSpec(t *testing.T) { 59 doc, err := Run(&Options{ 60 Packages: []string{"github.com/go-swagger/go-swagger/fixtures/goparsing/petstore/..."}, 61 }) 62 require.NoError(t, err) 63 if assert.NotNil(t, doc) { 64 // b, _ := json.MarshalIndent(doc.Responses, "", " ") 65 // log.Println(string(b)) 66 verifyParsedPetStore(t, doc) 67 } 68 } 69 70 func TestAppScanner_Definitions(t *testing.T) { 71 doc, err := Run(&Options{ 72 Packages: []string{"github.com/go-swagger/go-swagger/fixtures/goparsing/bookings/..."}, 73 ScanModels: true, 74 }) 75 require.NoError(t, err) 76 if assert.NotNil(t, doc) { 77 _, ok := doc.Definitions["Booking"] 78 assert.True(t, ok, "Should include cross repo structs") 79 _, ok = doc.Definitions["Customer"] 80 assert.True(t, ok, "Should include package structs with swagger:model") 81 _, ok = doc.Definitions["DateRange"] 82 assert.True(t, ok, "Should include package structs that are used in responses") 83 _, ok = doc.Definitions["BookingResponse"] 84 assert.False(t, ok, "Should not include responses") 85 _, ok = doc.Definitions["IgnoreMe"] 86 assert.False(t, ok, "Should not include un-annotated/un-referenced structs") 87 } 88 } 89 90 func verifyParsedPetStore(t testing.TB, doc *spec.Swagger) { 91 assert.EqualValues(t, []string{"application/json"}, doc.Consumes) 92 assert.EqualValues(t, []string{"application/json"}, doc.Produces) 93 assert.EqualValues(t, []string{"http", "https"}, doc.Schemes) 94 assert.Equal(t, "localhost", doc.Host) 95 assert.Equal(t, "/v2", doc.BasePath) 96 97 verifyInfo(t, doc.Info) 98 99 if assert.NotNil(t, doc.Paths) { 100 assert.Len(t, doc.Paths.Paths, 5) 101 } 102 var keys []string 103 for k := range doc.Definitions { 104 keys = append(keys, k) 105 } 106 assert.Len(t, keys, 3) 107 assert.Len(t, doc.Responses, 4) 108 109 definitions := doc.Definitions 110 mod, ok := definitions["tag"] 111 assert.True(t, ok) 112 assert.Equal(t, spec.StringOrArray([]string{"object"}), mod.Type) 113 assert.Equal(t, "A Tag is an extra piece of data to provide more information about a pet.", mod.Title) 114 assert.Equal(t, "It is used to describe the animals available in the store.", mod.Description) 115 assert.Len(t, mod.Required, 2) 116 117 assertProperty(t, &mod, "integer", "id", "int64", "ID") 118 prop, ok := mod.Properties["id"] 119 assert.True(t, ok, "should have had an 'id' property") 120 assert.Equal(t, "The id of the tag.", prop.Description) 121 122 assertProperty(t, &mod, "string", "value", "", "Value") 123 prop, ok = mod.Properties["value"] 124 assert.True(t, ok) 125 assert.Equal(t, "The value of the tag.", prop.Description) 126 127 mod, ok = definitions["pet"] 128 assert.True(t, ok) 129 assert.Equal(t, spec.StringOrArray([]string{"object"}), mod.Type) 130 assert.Equal(t, "A Pet is the main product in the store.", mod.Title) 131 assert.Equal(t, "It is used to describe the animals available in the store.", mod.Description) 132 assert.Len(t, mod.Required, 2) 133 134 assertProperty(t, &mod, "integer", "id", "int64", "ID") 135 prop, ok = mod.Properties["id"] 136 assert.True(t, ok, "should have had an 'id' property") 137 assert.Equal(t, "The id of the pet.", prop.Description) 138 139 assertProperty(t, &mod, "string", "name", "", "Name") 140 prop, ok = mod.Properties["name"] 141 assert.True(t, ok) 142 assert.Equal(t, "The name of the pet.", prop.Description) 143 assert.EqualValues(t, 3, *prop.MinLength) 144 assert.EqualValues(t, 50, *prop.MaxLength) 145 assert.Equal(t, "\\w[\\w-]+", prop.Pattern) 146 147 assertArrayProperty(t, &mod, "string", "photoUrls", "", "PhotoURLs") 148 prop, ok = mod.Properties["photoUrls"] 149 assert.True(t, ok) 150 assert.Equal(t, "The photo urls for the pet.\nThis only accepts jpeg or png images.", prop.Description) 151 if assert.NotNil(t, prop.Items) && assert.NotNil(t, prop.Items.Schema) { 152 assert.Equal(t, "\\.(jpe?g|png)$", prop.Items.Schema.Pattern) 153 } 154 155 assertProperty(t, &mod, "string", "status", "", "Status") 156 prop, ok = mod.Properties["status"] 157 assert.True(t, ok) 158 assert.Equal(t, "The current status of the pet in the store.\navailable STATUS_AVAILABLE\npending STATUS_PENDING\nsold STATUS_SOLD", prop.Description) 159 assert.Equal(t, []interface{}{"available", "pending", "sold"}, prop.Enum) 160 161 assertProperty(t, &mod, "string", "birthday", "date", "Birthday") 162 prop, ok = mod.Properties["birthday"] 163 assert.True(t, ok) 164 assert.Equal(t, "The pet's birthday", prop.Description) 165 166 assertArrayRef(t, &mod, "tags", "Tags", "#/definitions/tag") 167 prop, ok = mod.Properties["tags"] 168 assert.True(t, ok) 169 assert.Equal(t, "Extra bits of information attached to this pet.", prop.Description) 170 171 mod, ok = definitions["order"] 172 assert.True(t, ok) 173 assert.Len(t, mod.Properties, 4) 174 assert.Len(t, mod.Required, 3) 175 176 assertProperty(t, &mod, "integer", "id", "int64", "ID") 177 prop, ok = mod.Properties["id"] 178 assert.True(t, ok, "should have had an 'id' property") 179 assert.Equal(t, "the ID of the order", prop.Description) 180 181 assertProperty(t, &mod, "integer", "userId", "int64", "UserID") 182 prop, ok = mod.Properties["userId"] 183 assert.True(t, ok, "should have had an 'userId' property") 184 assert.Equal(t, "the id of the user who placed the order.", prop.Description) 185 186 assertProperty(t, &mod, "string", "orderedAt", "date-time", "OrderedAt") 187 prop, ok = mod.Properties["orderedAt"] 188 assert.Equal(t, "the time at which this order was made.", prop.Description) 189 assert.True(t, ok, "should have an 'orderedAt' property") 190 191 assertArrayProperty(t, &mod, "object", "items", "", "Items") 192 prop, ok = mod.Properties["items"] 193 assert.True(t, ok, "should have an 'items' slice") 194 assert.NotNil(t, prop.Items, "items should have had an items property") 195 assert.NotNil(t, prop.Items.Schema, "items.items should have had a schema property") 196 197 itprop := prop.Items.Schema 198 assert.Len(t, itprop.Properties, 2) 199 assert.Len(t, itprop.Required, 2) 200 201 assertProperty(t, itprop, "integer", "petId", "int64", "PetID") 202 iprop, ok := itprop.Properties["petId"] 203 assert.True(t, ok, "should have had a 'petId' property") 204 assert.Equal(t, "the id of the pet to order", iprop.Description) 205 206 assertProperty(t, itprop, "integer", "qty", "int32", "Quantity") 207 iprop, ok = itprop.Properties["qty"] 208 assert.True(t, ok, "should have had a 'qty' property") 209 assert.Equal(t, "the quantity of this pet to order", iprop.Description) 210 assert.EqualValues(t, 1, *iprop.Minimum) 211 212 // responses 213 resp, ok := doc.Responses["genericError"] 214 assert.True(t, ok) 215 assert.NotNil(t, resp.Schema) 216 assert.Len(t, resp.Schema.Properties, 2) 217 assertProperty(t, resp.Schema, "integer", "code", "int32", "Code") 218 assertProperty(t, resp.Schema, "string", "message", "", "Message") 219 220 resp, ok = doc.Responses["validationError"] 221 assert.True(t, ok) 222 assert.NotNil(t, resp.Schema) 223 assert.Len(t, resp.Schema.Properties, 3) 224 assertProperty(t, resp.Schema, "integer", "code", "int32", "Code") 225 assertProperty(t, resp.Schema, "string", "message", "", "Message") 226 assertProperty(t, resp.Schema, "string", "field", "", "Field") 227 228 resp, ok = doc.Responses["MarkdownRender"] 229 assert.True(t, ok) 230 assert.NotNil(t, resp.Schema) 231 assert.True(t, resp.Schema.Type.Contains("string")) 232 233 paths := doc.Paths.Paths 234 235 // path /pets 236 op, ok := paths["/pets"] 237 assert.True(t, ok) 238 assert.NotNil(t, op) 239 240 // listPets 241 assert.NotNil(t, op.Get) 242 assert.Equal(t, "Lists the pets known to the store.", op.Get.Summary) 243 assert.Equal(t, "By default it will only lists pets that are available for sale.\nThis can be changed with the status flag.", op.Get.Description) 244 assert.Equal(t, "listPets", op.Get.ID) 245 assert.EqualValues(t, []string{"pets"}, op.Get.Tags) 246 assert.True(t, op.Get.Deprecated) 247 var names namedParams 248 for i, v := range op.Get.Parameters { 249 names = append(names, namedParam{Index: i, Name: v.Name}) 250 } 251 sort.Sort(names) 252 sparam := op.Get.Parameters[names[1].Index] 253 assert.Equal(t, "Status\navailable STATUS_AVAILABLE\npending STATUS_PENDING\nsold STATUS_SOLD", sparam.Description) 254 assert.Equal(t, "query", sparam.In) 255 assert.Equal(t, "string", sparam.Type) 256 assert.Equal(t, "", sparam.Format) 257 assert.Equal(t, []interface{}{"available", "pending", "sold"}, sparam.Enum) 258 assert.False(t, sparam.Required) 259 assert.Equal(t, "Status", sparam.Extensions["x-go-name"]) 260 assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String()) 261 assert.Len(t, op.Get.Parameters, 2) 262 sparam1 := op.Get.Parameters[names[0].Index] 263 assert.Equal(t, "Birthday", sparam1.Description) 264 assert.Equal(t, "query", sparam1.In) 265 assert.Equal(t, "string", sparam1.Type) 266 assert.Equal(t, "date", sparam1.Format) 267 assert.False(t, sparam1.Required) 268 assert.Equal(t, "Birthday", sparam1.Extensions["x-go-name"]) 269 rs, ok := op.Get.Responses.StatusCodeResponses[200] 270 assert.True(t, ok) 271 assert.NotNil(t, rs.Schema) 272 aprop := rs.Schema 273 assert.Equal(t, "array", aprop.Type[0]) 274 assert.NotNil(t, aprop.Items) 275 assert.NotNil(t, aprop.Items.Schema) 276 assert.Equal(t, "#/definitions/pet", aprop.Items.Schema.Ref.String()) 277 278 // createPet 279 assert.NotNil(t, op.Post) 280 assert.Equal(t, "Creates a new pet in the store.", op.Post.Summary) 281 assert.Equal(t, "", op.Post.Description) 282 assert.Equal(t, "createPet", op.Post.ID) 283 assert.EqualValues(t, []string{"pets"}, op.Post.Tags) 284 verifyRefParam(t, op.Post.Parameters[0], "The pet to submit.", "pet") 285 assert.Equal(t, "#/responses/genericError", op.Post.Responses.Default.Ref.String()) 286 rs, ok = op.Post.Responses.StatusCodeResponses[200] 287 assert.True(t, ok) 288 assert.NotNil(t, rs.Schema) 289 aprop = rs.Schema 290 assert.Equal(t, "#/definitions/pet", aprop.Ref.String()) 291 292 // path /pets/{id} 293 op, ok = paths["/pets/{id}"] 294 assert.True(t, ok) 295 assert.NotNil(t, op) 296 297 // getPetById 298 assert.NotNil(t, op.Get) 299 assert.Equal(t, "Gets the details for a pet.", op.Get.Summary) 300 assert.Equal(t, "", op.Get.Description) 301 assert.Equal(t, "getPetById", op.Get.ID) 302 assert.EqualValues(t, []string{"pets"}, op.Get.Tags) 303 verifyIDParam(t, op.Get.Parameters[0], "The ID of the pet") 304 assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String()) 305 rs, ok = op.Get.Responses.StatusCodeResponses[200] 306 assert.True(t, ok) 307 assert.NotNil(t, rs.Schema) 308 aprop = rs.Schema 309 assert.Equal(t, "#/definitions/pet", aprop.Ref.String()) 310 311 // updatePet 312 assert.NotNil(t, op.Put) 313 assert.Equal(t, "Updates the details for a pet.", op.Put.Summary) 314 assert.Equal(t, "", op.Put.Description) 315 assert.Equal(t, "updatePet", op.Put.ID) 316 assert.EqualValues(t, []string{"pets"}, op.Put.Tags) 317 verifyIDParam(t, op.Put.Parameters[0], "The ID of the pet") 318 verifyRefParam(t, op.Put.Parameters[1], "The pet to submit.", "pet") 319 assert.Equal(t, "#/responses/genericError", op.Put.Responses.Default.Ref.String()) 320 rs, ok = op.Put.Responses.StatusCodeResponses[200] 321 assert.True(t, ok) 322 assert.NotNil(t, rs.Schema) 323 aprop = rs.Schema 324 assert.Equal(t, "#/definitions/pet", aprop.Ref.String()) 325 326 // deletePet 327 assert.NotNil(t, op.Delete) 328 assert.Equal(t, "Deletes a pet from the store.", op.Delete.Summary) 329 assert.Equal(t, "", op.Delete.Description) 330 assert.Equal(t, "deletePet", op.Delete.ID) 331 assert.EqualValues(t, []string{"pets"}, op.Delete.Tags) 332 verifyIDParam(t, op.Delete.Parameters[0], "The ID of the pet") 333 assert.Equal(t, "#/responses/genericError", op.Delete.Responses.Default.Ref.String()) 334 _, ok = op.Delete.Responses.StatusCodeResponses[204] 335 assert.True(t, ok) 336 337 // path /orders/{id} 338 op, ok = paths["/orders/{id}"] 339 assert.True(t, ok) 340 assert.NotNil(t, op) 341 342 // getOrderDetails 343 assert.NotNil(t, op.Get) 344 assert.Equal(t, "Gets the details for an order.", op.Get.Summary) 345 assert.Equal(t, "", op.Get.Description) 346 assert.Equal(t, "getOrderDetails", op.Get.ID) 347 assert.EqualValues(t, []string{"orders"}, op.Get.Tags) 348 verifyIDParam(t, op.Get.Parameters[0], "The ID of the order") 349 assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String()) 350 rs, ok = op.Get.Responses.StatusCodeResponses[200] 351 assert.True(t, ok) 352 assert.Equal(t, "#/responses/orderResponse", rs.Ref.String()) 353 rsm := doc.Responses["orderResponse"] 354 assert.NotNil(t, rsm.Schema) 355 assert.Equal(t, "#/definitions/order", rsm.Schema.Ref.String()) 356 357 // cancelOrder 358 assert.NotNil(t, op.Delete) 359 assert.Equal(t, "Deletes an order.", op.Delete.Summary) 360 assert.Equal(t, "", op.Delete.Description) 361 assert.Equal(t, "cancelOrder", op.Delete.ID) 362 assert.EqualValues(t, []string{"orders"}, op.Delete.Tags) 363 verifyIDParam(t, op.Delete.Parameters[0], "The ID of the order") 364 assert.Equal(t, "#/responses/genericError", op.Delete.Responses.Default.Ref.String()) 365 _, ok = op.Delete.Responses.StatusCodeResponses[204] 366 assert.True(t, ok) 367 368 // updateOrder 369 assert.NotNil(t, op.Put) 370 assert.Equal(t, "Updates an order.", op.Put.Summary) 371 assert.Equal(t, "", op.Put.Description) 372 assert.Equal(t, "updateOrder", op.Put.ID) 373 assert.EqualValues(t, []string{"orders"}, op.Put.Tags) 374 verifyIDParam(t, op.Put.Parameters[0], "The ID of the order") 375 verifyRefParam(t, op.Put.Parameters[1], "The order to submit", "order") 376 assert.Equal(t, "#/responses/genericError", op.Put.Responses.Default.Ref.String()) 377 rs, ok = op.Put.Responses.StatusCodeResponses[200] 378 assert.True(t, ok) 379 assert.NotNil(t, rs.Schema) 380 aprop = rs.Schema 381 assert.Equal(t, "#/definitions/order", aprop.Ref.String()) 382 383 // path /orders 384 op, ok = paths["/orders"] 385 assert.True(t, ok) 386 assert.NotNil(t, op) 387 388 // createOrder 389 assert.NotNil(t, op.Post) 390 assert.Equal(t, "Creates an order.", op.Post.Summary) 391 assert.Equal(t, "", op.Post.Description) 392 assert.Equal(t, "createOrder", op.Post.ID) 393 assert.EqualValues(t, []string{"orders"}, op.Post.Tags) 394 verifyRefParam(t, op.Post.Parameters[0], "The order to submit", "order") 395 assert.Equal(t, "#/responses/genericError", op.Post.Responses.Default.Ref.String()) 396 rs, ok = op.Post.Responses.StatusCodeResponses[200] 397 assert.True(t, ok) 398 assert.Equal(t, "#/responses/orderResponse", rs.Ref.String()) 399 rsm = doc.Responses["orderResponse"] 400 assert.NotNil(t, rsm.Schema) 401 assert.Equal(t, "#/definitions/order", rsm.Schema.Ref.String()) 402 } 403 404 func verifyIDParam(t testing.TB, param spec.Parameter, description string) { 405 assert.Equal(t, description, param.Description) 406 assert.Equal(t, "path", param.In) 407 assert.Equal(t, "integer", param.Type) 408 assert.Equal(t, "int64", param.Format) 409 assert.True(t, param.Required) 410 assert.Equal(t, "ID", param.Extensions["x-go-name"]) 411 } 412 413 func verifyRefParam(t testing.TB, param spec.Parameter, description, refed string) { 414 assert.Equal(t, description, param.Description) 415 assert.Equal(t, "body", param.In) 416 // TODO: this may fail sometimes (seen on go1.12 windows test): require pointer to be valid and avoid panicking 417 require.NotNil(t, param) 418 require.NotNil(t, param.Schema) 419 assert.Equal(t, "#/definitions/"+refed, param.Schema.Ref.String()) 420 assert.True(t, param.Required) 421 } 422 423 type namedParam struct { 424 Index int 425 Name string 426 } 427 428 type namedParams []namedParam 429 430 func (g namedParams) Len() int { return len(g) } 431 func (g namedParams) Swap(i, j int) { g[i], g[j] = g[j], g[i] } 432 func (g namedParams) Less(i, j int) bool { return g[i].Name < g[j].Name }