github.com/emreu/go-swagger@v0.22.1/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, 30)
    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.", prop.Description)
   159  
   160  	assertProperty(t, &mod, "string", "birthday", "date", "Birthday")
   161  	prop, ok = mod.Properties["birthday"]
   162  	assert.True(t, ok)
   163  	assert.Equal(t, "The pet's birthday", prop.Description)
   164  
   165  	assertArrayRef(t, &mod, "tags", "Tags", "#/definitions/tag")
   166  	prop, ok = mod.Properties["tags"]
   167  	assert.True(t, ok)
   168  	assert.Equal(t, "Extra bits of information attached to this pet.", prop.Description)
   169  
   170  	mod, ok = definitions["order"]
   171  	assert.True(t, ok)
   172  	assert.Len(t, mod.Properties, 4)
   173  	assert.Len(t, mod.Required, 3)
   174  
   175  	assertProperty(t, &mod, "integer", "id", "int64", "ID")
   176  	prop, ok = mod.Properties["id"]
   177  	assert.True(t, ok, "should have had an 'id' property")
   178  	assert.Equal(t, "the ID of the order", prop.Description)
   179  
   180  	assertProperty(t, &mod, "integer", "userId", "int64", "UserID")
   181  	prop, ok = mod.Properties["userId"]
   182  	assert.True(t, ok, "should have had an 'userId' property")
   183  	assert.Equal(t, "the id of the user who placed the order.", prop.Description)
   184  
   185  	assertProperty(t, &mod, "string", "orderedAt", "date-time", "OrderedAt")
   186  	prop, ok = mod.Properties["orderedAt"]
   187  	assert.Equal(t, "the time at which this order was made.", prop.Description)
   188  	assert.True(t, ok, "should have an 'orderedAt' property")
   189  
   190  	assertArrayProperty(t, &mod, "object", "items", "", "Items")
   191  	prop, ok = mod.Properties["items"]
   192  	assert.True(t, ok, "should have an 'items' slice")
   193  	assert.NotNil(t, prop.Items, "items should have had an items property")
   194  	assert.NotNil(t, prop.Items.Schema, "items.items should have had a schema property")
   195  
   196  	itprop := prop.Items.Schema
   197  	assert.Len(t, itprop.Properties, 2)
   198  	assert.Len(t, itprop.Required, 2)
   199  
   200  	assertProperty(t, itprop, "integer", "petId", "int64", "PetID")
   201  	iprop, ok := itprop.Properties["petId"]
   202  	assert.True(t, ok, "should have had a 'petId' property")
   203  	assert.Equal(t, "the id of the pet to order", iprop.Description)
   204  
   205  	assertProperty(t, itprop, "integer", "qty", "int32", "Quantity")
   206  	iprop, ok = itprop.Properties["qty"]
   207  	assert.True(t, ok, "should have had a 'qty' property")
   208  	assert.Equal(t, "the quantity of this pet to order", iprop.Description)
   209  	assert.EqualValues(t, 1, *iprop.Minimum)
   210  
   211  	// responses
   212  	resp, ok := doc.Responses["genericError"]
   213  	assert.True(t, ok)
   214  	assert.NotNil(t, resp.Schema)
   215  	assert.Len(t, resp.Schema.Properties, 2)
   216  	assertProperty(t, resp.Schema, "integer", "code", "int32", "Code")
   217  	assertProperty(t, resp.Schema, "string", "message", "", "Message")
   218  
   219  	resp, ok = doc.Responses["validationError"]
   220  	assert.True(t, ok)
   221  	assert.NotNil(t, resp.Schema)
   222  	assert.Len(t, resp.Schema.Properties, 3)
   223  	assertProperty(t, resp.Schema, "integer", "code", "int32", "Code")
   224  	assertProperty(t, resp.Schema, "string", "message", "", "Message")
   225  	assertProperty(t, resp.Schema, "string", "field", "", "Field")
   226  
   227  	resp, ok = doc.Responses["MarkdownRender"]
   228  	assert.True(t, ok)
   229  	assert.NotNil(t, resp.Schema)
   230  	assert.True(t, resp.Schema.Type.Contains("string"))
   231  
   232  	paths := doc.Paths.Paths
   233  
   234  	// path /pets
   235  	op, ok := paths["/pets"]
   236  	assert.True(t, ok)
   237  	assert.NotNil(t, op)
   238  
   239  	// listPets
   240  	assert.NotNil(t, op.Get)
   241  	assert.Equal(t, "Lists the pets known to the store.", op.Get.Summary)
   242  	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)
   243  	assert.Equal(t, "listPets", op.Get.ID)
   244  	assert.EqualValues(t, []string{"pets"}, op.Get.Tags)
   245  	assert.True(t, op.Get.Deprecated)
   246  	var names namedParams
   247  	for i, v := range op.Get.Parameters {
   248  		names = append(names, namedParam{Index: i, Name: v.Name})
   249  	}
   250  	sort.Sort(names)
   251  	sparam := op.Get.Parameters[names[1].Index]
   252  	assert.Equal(t, "Status", sparam.Description)
   253  	assert.Equal(t, "query", sparam.In)
   254  	assert.Equal(t, "string", sparam.Type)
   255  	assert.Equal(t, "", sparam.Format)
   256  	assert.False(t, sparam.Required)
   257  	assert.Equal(t, "Status", sparam.Extensions["x-go-name"])
   258  	assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String())
   259  	assert.Len(t, op.Get.Parameters, 2)
   260  	sparam1 := op.Get.Parameters[names[0].Index]
   261  	assert.Equal(t, "Birthday", sparam1.Description)
   262  	assert.Equal(t, "query", sparam1.In)
   263  	assert.Equal(t, "string", sparam1.Type)
   264  	assert.Equal(t, "date", sparam1.Format)
   265  	assert.False(t, sparam1.Required)
   266  	assert.Equal(t, "Birthday", sparam1.Extensions["x-go-name"])
   267  	rs, ok := op.Get.Responses.StatusCodeResponses[200]
   268  	assert.True(t, ok)
   269  	assert.NotNil(t, rs.Schema)
   270  	aprop := rs.Schema
   271  	assert.Equal(t, "array", aprop.Type[0])
   272  	assert.NotNil(t, aprop.Items)
   273  	assert.NotNil(t, aprop.Items.Schema)
   274  	assert.Equal(t, "#/definitions/pet", aprop.Items.Schema.Ref.String())
   275  
   276  	// createPet
   277  	assert.NotNil(t, op.Post)
   278  	assert.Equal(t, "Creates a new pet in the store.", op.Post.Summary)
   279  	assert.Equal(t, "", op.Post.Description)
   280  	assert.Equal(t, "createPet", op.Post.ID)
   281  	assert.EqualValues(t, []string{"pets"}, op.Post.Tags)
   282  	verifyRefParam(t, op.Post.Parameters[0], "The pet to submit.", "pet")
   283  	assert.Equal(t, "#/responses/genericError", op.Post.Responses.Default.Ref.String())
   284  	rs, ok = op.Post.Responses.StatusCodeResponses[200]
   285  	assert.True(t, ok)
   286  	assert.NotNil(t, rs.Schema)
   287  	aprop = rs.Schema
   288  	assert.Equal(t, "#/definitions/pet", aprop.Ref.String())
   289  
   290  	// path /pets/{id}
   291  	op, ok = paths["/pets/{id}"]
   292  	assert.True(t, ok)
   293  	assert.NotNil(t, op)
   294  
   295  	// getPetById
   296  	assert.NotNil(t, op.Get)
   297  	assert.Equal(t, "Gets the details for a pet.", op.Get.Summary)
   298  	assert.Equal(t, "", op.Get.Description)
   299  	assert.Equal(t, "getPetById", op.Get.ID)
   300  	assert.EqualValues(t, []string{"pets"}, op.Get.Tags)
   301  	verifyIDParam(t, op.Get.Parameters[0], "The ID of the pet")
   302  	assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String())
   303  	rs, ok = op.Get.Responses.StatusCodeResponses[200]
   304  	assert.True(t, ok)
   305  	assert.NotNil(t, rs.Schema)
   306  	aprop = rs.Schema
   307  	assert.Equal(t, "#/definitions/pet", aprop.Ref.String())
   308  
   309  	// updatePet
   310  	assert.NotNil(t, op.Put)
   311  	assert.Equal(t, "Updates the details for a pet.", op.Put.Summary)
   312  	assert.Equal(t, "", op.Put.Description)
   313  	assert.Equal(t, "updatePet", op.Put.ID)
   314  	assert.EqualValues(t, []string{"pets"}, op.Put.Tags)
   315  	verifyIDParam(t, op.Put.Parameters[0], "The ID of the pet")
   316  	verifyRefParam(t, op.Put.Parameters[1], "The pet to submit.", "pet")
   317  	assert.Equal(t, "#/responses/genericError", op.Put.Responses.Default.Ref.String())
   318  	rs, ok = op.Put.Responses.StatusCodeResponses[200]
   319  	assert.True(t, ok)
   320  	assert.NotNil(t, rs.Schema)
   321  	aprop = rs.Schema
   322  	assert.Equal(t, "#/definitions/pet", aprop.Ref.String())
   323  
   324  	// deletePet
   325  	assert.NotNil(t, op.Delete)
   326  	assert.Equal(t, "Deletes a pet from the store.", op.Delete.Summary)
   327  	assert.Equal(t, "", op.Delete.Description)
   328  	assert.Equal(t, "deletePet", op.Delete.ID)
   329  	assert.EqualValues(t, []string{"pets"}, op.Delete.Tags)
   330  	verifyIDParam(t, op.Delete.Parameters[0], "The ID of the pet")
   331  	assert.Equal(t, "#/responses/genericError", op.Delete.Responses.Default.Ref.String())
   332  	_, ok = op.Delete.Responses.StatusCodeResponses[204]
   333  	assert.True(t, ok)
   334  
   335  	// path /orders/{id}
   336  	op, ok = paths["/orders/{id}"]
   337  	assert.True(t, ok)
   338  	assert.NotNil(t, op)
   339  
   340  	// getOrderDetails
   341  	assert.NotNil(t, op.Get)
   342  	assert.Equal(t, "Gets the details for an order.", op.Get.Summary)
   343  	assert.Equal(t, "", op.Get.Description)
   344  	assert.Equal(t, "getOrderDetails", op.Get.ID)
   345  	assert.EqualValues(t, []string{"orders"}, op.Get.Tags)
   346  	verifyIDParam(t, op.Get.Parameters[0], "The ID of the order")
   347  	assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String())
   348  	rs, ok = op.Get.Responses.StatusCodeResponses[200]
   349  	assert.True(t, ok)
   350  	assert.Equal(t, "#/responses/orderResponse", rs.Ref.String())
   351  	rsm := doc.Responses["orderResponse"]
   352  	assert.NotNil(t, rsm.Schema)
   353  	assert.Equal(t, "#/definitions/order", rsm.Schema.Ref.String())
   354  
   355  	// cancelOrder
   356  	assert.NotNil(t, op.Delete)
   357  	assert.Equal(t, "Deletes an order.", op.Delete.Summary)
   358  	assert.Equal(t, "", op.Delete.Description)
   359  	assert.Equal(t, "cancelOrder", op.Delete.ID)
   360  	assert.EqualValues(t, []string{"orders"}, op.Delete.Tags)
   361  	verifyIDParam(t, op.Delete.Parameters[0], "The ID of the order")
   362  	assert.Equal(t, "#/responses/genericError", op.Delete.Responses.Default.Ref.String())
   363  	_, ok = op.Delete.Responses.StatusCodeResponses[204]
   364  	assert.True(t, ok)
   365  
   366  	// updateOrder
   367  	assert.NotNil(t, op.Put)
   368  	assert.Equal(t, "Updates an order.", op.Put.Summary)
   369  	assert.Equal(t, "", op.Put.Description)
   370  	assert.Equal(t, "updateOrder", op.Put.ID)
   371  	assert.EqualValues(t, []string{"orders"}, op.Put.Tags)
   372  	verifyIDParam(t, op.Put.Parameters[0], "The ID of the order")
   373  	verifyRefParam(t, op.Put.Parameters[1], "The order to submit", "order")
   374  	assert.Equal(t, "#/responses/genericError", op.Put.Responses.Default.Ref.String())
   375  	rs, ok = op.Put.Responses.StatusCodeResponses[200]
   376  	assert.True(t, ok)
   377  	assert.NotNil(t, rs.Schema)
   378  	aprop = rs.Schema
   379  	assert.Equal(t, "#/definitions/order", aprop.Ref.String())
   380  
   381  	// path /orders
   382  	op, ok = paths["/orders"]
   383  	assert.True(t, ok)
   384  	assert.NotNil(t, op)
   385  
   386  	// createOrder
   387  	assert.NotNil(t, op.Post)
   388  	assert.Equal(t, "Creates an order.", op.Post.Summary)
   389  	assert.Equal(t, "", op.Post.Description)
   390  	assert.Equal(t, "createOrder", op.Post.ID)
   391  	assert.EqualValues(t, []string{"orders"}, op.Post.Tags)
   392  	verifyRefParam(t, op.Post.Parameters[0], "The order to submit", "order")
   393  	assert.Equal(t, "#/responses/genericError", op.Post.Responses.Default.Ref.String())
   394  	rs, ok = op.Post.Responses.StatusCodeResponses[200]
   395  	assert.True(t, ok)
   396  	assert.Equal(t, "#/responses/orderResponse", rs.Ref.String())
   397  	rsm = doc.Responses["orderResponse"]
   398  	assert.NotNil(t, rsm.Schema)
   399  	assert.Equal(t, "#/definitions/order", rsm.Schema.Ref.String())
   400  }
   401  
   402  func verifyIDParam(t testing.TB, param spec.Parameter, description string) {
   403  	assert.Equal(t, description, param.Description)
   404  	assert.Equal(t, "path", param.In)
   405  	assert.Equal(t, "integer", param.Type)
   406  	assert.Equal(t, "int64", param.Format)
   407  	assert.True(t, param.Required)
   408  	assert.Equal(t, "ID", param.Extensions["x-go-name"])
   409  }
   410  
   411  func verifyRefParam(t testing.TB, param spec.Parameter, description, refed string) {
   412  	assert.Equal(t, description, param.Description)
   413  	assert.Equal(t, "body", param.In)
   414  	// TODO: this may fail sometimes (seen on go1.12 windows test): require pointer to be valid and avoid panicking
   415  	require.NotNil(t, param)
   416  	require.NotNil(t, param.Schema)
   417  	assert.Equal(t, "#/definitions/"+refed, param.Schema.Ref.String())
   418  	assert.True(t, param.Required)
   419  }
   420  
   421  type namedParam struct {
   422  	Index int
   423  	Name  string
   424  }
   425  
   426  type namedParams []namedParam
   427  
   428  func (g namedParams) Len() int           { return len(g) }
   429  func (g namedParams) Swap(i, j int)      { g[i], g[j] = g[j], g[i] }
   430  func (g namedParams) Less(i, j int) bool { return g[i].Name < g[j].Name }