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 }