github.com/kaisawind/go-swagger@v0.19.0/scan/scanner_test.go (about)

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package scan
    16  
    17  import (
    18  	"fmt"
    19  	"go/ast"
    20  	goparser "go/parser"
    21  	"io/ioutil"
    22  	"log"
    23  	"os"
    24  	"regexp"
    25  	"sort"
    26  	"strings"
    27  	"testing"
    28  
    29  	"github.com/go-openapi/loads"
    30  	"github.com/go-openapi/spec"
    31  	"github.com/go-openapi/strfmt"
    32  	"github.com/go-openapi/validate"
    33  	"github.com/stretchr/testify/assert"
    34  	"golang.org/x/tools/go/loader"
    35  
    36  	_ "github.com/go-swagger/scan-repo-boundary/makeplans"
    37  )
    38  
    39  var classificationProg *loader.Program
    40  var noModelDefs map[string]spec.Schema
    41  
    42  type namedParam struct {
    43  	Index int
    44  	Name  string
    45  }
    46  
    47  type namedParams []namedParam
    48  
    49  func (g namedParams) Len() int           { return len(g) }
    50  func (g namedParams) Swap(i, j int)      { g[i], g[j] = g[j], g[i] }
    51  func (g namedParams) Less(i, j int) bool { return g[i].Name < g[j].Name }
    52  
    53  func init() {
    54  	classificationProg = classifierProgram()
    55  	docFile := "../fixtures/goparsing/classification/models/nomodel.go"
    56  	fileTree, err := goparser.ParseFile(classificationProg.Fset, docFile, nil, goparser.ParseComments)
    57  	if err != nil {
    58  		log.Fatal(err)
    59  	}
    60  	sp := newSchemaParser(classificationProg)
    61  	noModelDefs = make(map[string]spec.Schema)
    62  	err = sp.Parse(fileTree, noModelDefs)
    63  	if err != nil {
    64  		log.Fatal(err)
    65  	}
    66  }
    67  
    68  // only used within this group of tests but never used within actual code base.
    69  func newSchemaAnnotationParser(goName string) *schemaAnnotationParser {
    70  	return &schemaAnnotationParser{GoName: goName, rx: rxModelOverride}
    71  }
    72  
    73  type schemaAnnotationParser struct {
    74  	GoName string
    75  	Name   string
    76  	rx     *regexp.Regexp
    77  }
    78  
    79  func (sap *schemaAnnotationParser) Matches(line string) bool {
    80  	return sap.rx.MatchString(line)
    81  }
    82  
    83  func (sap *schemaAnnotationParser) Parse(lines []string) error {
    84  	if sap.Name != "" {
    85  		return nil
    86  	}
    87  
    88  	if len(lines) > 0 {
    89  		for _, line := range lines {
    90  			matches := sap.rx.FindStringSubmatch(line)
    91  			if len(matches) > 1 && len(matches[1]) > 0 {
    92  				sap.Name = matches[1]
    93  				return nil
    94  			}
    95  		}
    96  	}
    97  	return nil
    98  }
    99  
   100  func extraModelsClassifier(t testing.TB) (*loader.Program, map[string]spec.Schema) {
   101  	prog := classifierProgram()
   102  	docFile := "../fixtures/goparsing/classification/models/extranomodel.go"
   103  	fileTree, err := goparser.ParseFile(prog.Fset, docFile, nil, goparser.ParseComments)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  	sp := newSchemaParser(prog)
   108  	defs := make(map[string]spec.Schema)
   109  	err = sp.Parse(fileTree, defs)
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  	return prog, defs
   114  }
   115  
   116  func TestAppScanner_NewSpec(t *testing.T) {
   117  	scanner, err := newAppScanner(&Opts{BasePath: "../fixtures/goparsing/petstore/petstore-fixture"})
   118  	assert.NoError(t, err)
   119  	assert.NotNil(t, scanner)
   120  	doc, err := scanner.Parse()
   121  	assert.NoError(t, err)
   122  	if assert.NotNil(t, doc) {
   123  		verifyParsedPetStore(t, doc)
   124  	}
   125  }
   126  
   127  func TestAppScanner_Definitions(t *testing.T) {
   128  	scanner, err := newAppScanner(&Opts{BasePath: "../fixtures/goparsing/bookings"})
   129  	assert.NoError(t, err)
   130  	assert.NotNil(t, scanner)
   131  	doc, err := scanner.Parse()
   132  	assert.NoError(t, err)
   133  	if assert.NotNil(t, doc) {
   134  		_, ok := doc.Definitions["Booking"]
   135  		assert.True(t, ok, "Should include cross repo structs")
   136  		_, ok = doc.Definitions["Customer"]
   137  		assert.True(t, ok, "Should include package structs with swagger:model")
   138  		_, ok = doc.Definitions["DateRange"]
   139  		assert.True(t, ok, "Should include package structs that are used in responses")
   140  		_, ok = doc.Definitions["BookingResponse"]
   141  		assert.False(t, ok, "Should not include responses")
   142  		_, ok = doc.Definitions["IgnoreMe"]
   143  		assert.False(t, ok, "Should not include un-annotated/un-referenced structs")
   144  	}
   145  }
   146  
   147  func verifyParsedPetStore(t testing.TB, doc *spec.Swagger) {
   148  	assert.EqualValues(t, []string{"application/json"}, doc.Consumes)
   149  	assert.EqualValues(t, []string{"application/json"}, doc.Produces)
   150  	assert.EqualValues(t, []string{"http", "https"}, doc.Schemes)
   151  	assert.Equal(t, "localhost", doc.Host)
   152  	assert.Equal(t, "/v2", doc.BasePath)
   153  
   154  	verifyInfo(t, doc.Info)
   155  
   156  	if assert.NotNil(t, doc.Paths) {
   157  		assert.Len(t, doc.Paths.Paths, 4)
   158  	}
   159  	var keys []string
   160  	for k := range doc.Definitions {
   161  		keys = append(keys, k)
   162  	}
   163  	assert.Len(t, keys, 3)
   164  	assert.Len(t, doc.Responses, 3)
   165  
   166  	definitions := doc.Definitions
   167  	mod, ok := definitions["tag"]
   168  	assert.True(t, ok)
   169  	assert.Equal(t, spec.StringOrArray([]string{"object"}), mod.Type)
   170  	assert.Equal(t, "A Tag is an extra piece of data to provide more information about a pet.", mod.Title)
   171  	assert.Equal(t, "It is used to describe the animals available in the store.", mod.Description)
   172  	assert.Len(t, mod.Required, 2)
   173  
   174  	assertProperty(t, &mod, "integer", "id", "int64", "ID")
   175  	prop, ok := mod.Properties["id"]
   176  	assert.True(t, ok, "should have had an 'id' property")
   177  	assert.Equal(t, "The id of the tag.", prop.Description)
   178  
   179  	assertProperty(t, &mod, "string", "value", "", "Value")
   180  	prop, ok = mod.Properties["value"]
   181  	assert.True(t, ok)
   182  	assert.Equal(t, "The value of the tag.", prop.Description)
   183  
   184  	mod, ok = definitions["pet"]
   185  	assert.True(t, ok)
   186  	assert.Equal(t, spec.StringOrArray([]string{"object"}), mod.Type)
   187  	assert.Equal(t, "A Pet is the main product in the store.", mod.Title)
   188  	assert.Equal(t, "It is used to describe the animals available in the store.", mod.Description)
   189  	assert.Len(t, mod.Required, 2)
   190  
   191  	assertProperty(t, &mod, "integer", "id", "int64", "ID")
   192  	prop, ok = mod.Properties["id"]
   193  	assert.True(t, ok, "should have had an 'id' property")
   194  	assert.Equal(t, "The id of the pet.", prop.Description)
   195  
   196  	assertProperty(t, &mod, "string", "name", "", "Name")
   197  	prop, ok = mod.Properties["name"]
   198  	assert.True(t, ok)
   199  	assert.Equal(t, "The name of the pet.", prop.Description)
   200  	assert.EqualValues(t, 3, *prop.MinLength)
   201  	assert.EqualValues(t, 50, *prop.MaxLength)
   202  	assert.Equal(t, "\\w[\\w-]+", prop.Pattern)
   203  
   204  	assertArrayProperty(t, &mod, "string", "photoUrls", "", "PhotoURLs")
   205  	prop, ok = mod.Properties["photoUrls"]
   206  	assert.True(t, ok)
   207  	assert.Equal(t, "The photo urls for the pet.\nThis only accepts jpeg or png images.", prop.Description)
   208  	if assert.NotNil(t, prop.Items) && assert.NotNil(t, prop.Items.Schema) {
   209  		assert.Equal(t, "\\.(jpe?g|png)$", prop.Items.Schema.Pattern)
   210  	}
   211  
   212  	assertProperty(t, &mod, "string", "status", "", "Status")
   213  	prop, ok = mod.Properties["status"]
   214  	assert.True(t, ok)
   215  	assert.Equal(t, "The current status of the pet in the store.", prop.Description)
   216  
   217  	assertProperty(t, &mod, "string", "birthday", "date", "Birthday")
   218  	prop, ok = mod.Properties["birthday"]
   219  	assert.True(t, ok)
   220  	assert.Equal(t, "The pet's birthday", prop.Description)
   221  
   222  	assertArrayRef(t, &mod, "tags", "Tags", "#/definitions/tag")
   223  	prop, ok = mod.Properties["tags"]
   224  	assert.True(t, ok)
   225  	assert.Equal(t, "Extra bits of information attached to this pet.", prop.Description)
   226  
   227  	mod, ok = definitions["order"]
   228  	assert.True(t, ok)
   229  	assert.Len(t, mod.Properties, 4)
   230  	assert.Len(t, mod.Required, 3)
   231  
   232  	assertProperty(t, &mod, "integer", "id", "int64", "ID")
   233  	prop, ok = mod.Properties["id"]
   234  	assert.True(t, ok, "should have had an 'id' property")
   235  	assert.Equal(t, "the ID of the order", prop.Description)
   236  
   237  	assertProperty(t, &mod, "integer", "userId", "int64", "UserID")
   238  	prop, ok = mod.Properties["userId"]
   239  	assert.True(t, ok, "should have had an 'userId' property")
   240  	assert.Equal(t, "the id of the user who placed the order.", prop.Description)
   241  
   242  	assertProperty(t, &mod, "string", "orderedAt", "date-time", "OrderedAt")
   243  	prop, ok = mod.Properties["orderedAt"]
   244  	assert.Equal(t, "the time at which this order was made.", prop.Description)
   245  	assert.True(t, ok, "should have an 'orderedAt' property")
   246  
   247  	assertArrayProperty(t, &mod, "object", "items", "", "Items")
   248  	prop, ok = mod.Properties["items"]
   249  	assert.True(t, ok, "should have an 'items' slice")
   250  	assert.NotNil(t, prop.Items, "items should have had an items property")
   251  	assert.NotNil(t, prop.Items.Schema, "items.items should have had a schema property")
   252  
   253  	itprop := prop.Items.Schema
   254  	assert.Len(t, itprop.Properties, 2)
   255  	assert.Len(t, itprop.Required, 2)
   256  
   257  	assertProperty(t, itprop, "integer", "petId", "int64", "PetID")
   258  	iprop, ok := itprop.Properties["petId"]
   259  	assert.True(t, ok, "should have had a 'petId' property")
   260  	assert.Equal(t, "the id of the pet to order", iprop.Description)
   261  
   262  	assertProperty(t, itprop, "integer", "qty", "int32", "Quantity")
   263  	iprop, ok = itprop.Properties["qty"]
   264  	assert.True(t, ok, "should have had a 'qty' property")
   265  	assert.Equal(t, "the quantity of this pet to order", iprop.Description)
   266  	assert.EqualValues(t, 1, *iprop.Minimum)
   267  
   268  	// responses
   269  	resp, ok := doc.Responses["genericError"]
   270  	assert.True(t, ok)
   271  	assert.NotNil(t, resp.Schema)
   272  	assert.Len(t, resp.Schema.Properties, 2)
   273  	assertProperty(t, resp.Schema, "integer", "code", "int32", "Code")
   274  	assertProperty(t, resp.Schema, "string", "message", "", "Message")
   275  
   276  	resp, ok = doc.Responses["validationError"]
   277  	assert.True(t, ok)
   278  	assert.NotNil(t, resp.Schema)
   279  	assert.Len(t, resp.Schema.Properties, 3)
   280  	assertProperty(t, resp.Schema, "integer", "code", "int32", "Code")
   281  	assertProperty(t, resp.Schema, "string", "message", "", "Message")
   282  	assertProperty(t, resp.Schema, "string", "field", "", "Field")
   283  
   284  	paths := doc.Paths.Paths
   285  
   286  	// path /pets
   287  	op, ok := paths["/pets"]
   288  	assert.True(t, ok)
   289  	assert.NotNil(t, op)
   290  
   291  	// listPets
   292  	assert.NotNil(t, op.Get)
   293  	assert.Equal(t, "Lists the pets known to the store.", op.Get.Summary)
   294  	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)
   295  	assert.Equal(t, "listPets", op.Get.ID)
   296  	assert.EqualValues(t, []string{"pets"}, op.Get.Tags)
   297  	var names namedParams
   298  	for i, v := range op.Get.Parameters {
   299  		names = append(names, namedParam{Index: i, Name: v.Name})
   300  	}
   301  	sort.Sort(names)
   302  	sparam := op.Get.Parameters[names[1].Index]
   303  	assert.Equal(t, "Status", sparam.Description)
   304  	assert.Equal(t, "query", sparam.In)
   305  	assert.Equal(t, "string", sparam.Type)
   306  	assert.Equal(t, "", sparam.Format)
   307  	assert.False(t, sparam.Required)
   308  	assert.Equal(t, "Status", sparam.Extensions["x-go-name"])
   309  	assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String())
   310  	assert.Len(t, op.Get.Parameters, 2)
   311  	sparam1 := op.Get.Parameters[names[0].Index]
   312  	assert.Equal(t, "Birthday", sparam1.Description)
   313  	assert.Equal(t, "query", sparam1.In)
   314  	assert.Equal(t, "string", sparam1.Type)
   315  	assert.Equal(t, "date", sparam1.Format)
   316  	assert.False(t, sparam1.Required)
   317  	assert.Equal(t, "Birthday", sparam1.Extensions["x-go-name"])
   318  	rs, ok := op.Get.Responses.StatusCodeResponses[200]
   319  	assert.True(t, ok)
   320  	assert.NotNil(t, rs.Schema)
   321  	aprop := rs.Schema
   322  	assert.Equal(t, "array", aprop.Type[0])
   323  	assert.NotNil(t, aprop.Items)
   324  	assert.NotNil(t, aprop.Items.Schema)
   325  	assert.Equal(t, "#/definitions/pet", aprop.Items.Schema.Ref.String())
   326  
   327  	// createPet
   328  	assert.NotNil(t, op.Post)
   329  	assert.Equal(t, "Creates a new pet in the store.", op.Post.Summary)
   330  	assert.Equal(t, "", op.Post.Description)
   331  	assert.Equal(t, "createPet", op.Post.ID)
   332  	assert.EqualValues(t, []string{"pets"}, op.Post.Tags)
   333  	verifyRefParam(t, op.Post.Parameters[0], "The pet to submit.", "pet")
   334  	assert.Equal(t, "#/responses/genericError", op.Post.Responses.Default.Ref.String())
   335  	rs, ok = op.Post.Responses.StatusCodeResponses[200]
   336  	assert.True(t, ok)
   337  	assert.NotNil(t, rs.Schema)
   338  	aprop = rs.Schema
   339  	assert.Equal(t, "#/definitions/pet", aprop.Ref.String())
   340  
   341  	// path /pets/{id}
   342  	op, ok = paths["/pets/{id}"]
   343  	assert.True(t, ok)
   344  	assert.NotNil(t, op)
   345  
   346  	// getPetById
   347  	assert.NotNil(t, op.Get)
   348  	assert.Equal(t, "Gets the details for a pet.", op.Get.Summary)
   349  	assert.Equal(t, "", op.Get.Description)
   350  	assert.Equal(t, "getPetById", op.Get.ID)
   351  	assert.EqualValues(t, []string{"pets"}, op.Get.Tags)
   352  	verifyIDParam(t, op.Get.Parameters[0], "The ID of the pet")
   353  	assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String())
   354  	rs, ok = op.Get.Responses.StatusCodeResponses[200]
   355  	assert.True(t, ok)
   356  	assert.NotNil(t, rs.Schema)
   357  	aprop = rs.Schema
   358  	assert.Equal(t, "#/definitions/pet", aprop.Ref.String())
   359  
   360  	// updatePet
   361  	assert.NotNil(t, op.Put)
   362  	assert.Equal(t, "Updates the details for a pet.", op.Put.Summary)
   363  	assert.Equal(t, "", op.Put.Description)
   364  	assert.Equal(t, "updatePet", op.Put.ID)
   365  	assert.EqualValues(t, []string{"pets"}, op.Put.Tags)
   366  	verifyIDParam(t, op.Put.Parameters[0], "The ID of the pet")
   367  	verifyRefParam(t, op.Put.Parameters[1], "The pet to submit.", "pet")
   368  	assert.Equal(t, "#/responses/genericError", op.Put.Responses.Default.Ref.String())
   369  	rs, ok = op.Put.Responses.StatusCodeResponses[200]
   370  	assert.True(t, ok)
   371  	assert.NotNil(t, rs.Schema)
   372  	aprop = rs.Schema
   373  	assert.Equal(t, "#/definitions/pet", aprop.Ref.String())
   374  
   375  	// deletePet
   376  	assert.NotNil(t, op.Delete)
   377  	assert.Equal(t, "Deletes a pet from the store.", op.Delete.Summary)
   378  	assert.Equal(t, "", op.Delete.Description)
   379  	assert.Equal(t, "deletePet", op.Delete.ID)
   380  	assert.EqualValues(t, []string{"pets"}, op.Delete.Tags)
   381  	verifyIDParam(t, op.Delete.Parameters[0], "The ID of the pet")
   382  	assert.Equal(t, "#/responses/genericError", op.Delete.Responses.Default.Ref.String())
   383  	_, ok = op.Delete.Responses.StatusCodeResponses[204]
   384  	assert.True(t, ok)
   385  
   386  	// path /orders/{id}
   387  	op, ok = paths["/orders/{id}"]
   388  	assert.True(t, ok)
   389  	assert.NotNil(t, op)
   390  
   391  	// getOrderDetails
   392  	assert.NotNil(t, op.Get)
   393  	assert.Equal(t, "Gets the details for an order.", op.Get.Summary)
   394  	assert.Equal(t, "", op.Get.Description)
   395  	assert.Equal(t, "getOrderDetails", op.Get.ID)
   396  	assert.EqualValues(t, []string{"orders"}, op.Get.Tags)
   397  	verifyIDParam(t, op.Get.Parameters[0], "The ID of the order")
   398  	assert.Equal(t, "#/responses/genericError", op.Get.Responses.Default.Ref.String())
   399  	rs, ok = op.Get.Responses.StatusCodeResponses[200]
   400  	assert.True(t, ok)
   401  	assert.Equal(t, "#/responses/orderResponse", rs.Ref.String())
   402  	rsm := doc.Responses["orderResponse"]
   403  	assert.NotNil(t, rsm.Schema)
   404  	assert.Equal(t, "#/definitions/order", rsm.Schema.Ref.String())
   405  
   406  	// cancelOrder
   407  	assert.NotNil(t, op.Delete)
   408  	assert.Equal(t, "Deletes an order.", op.Delete.Summary)
   409  	assert.Equal(t, "", op.Delete.Description)
   410  	assert.Equal(t, "cancelOrder", op.Delete.ID)
   411  	assert.EqualValues(t, []string{"orders"}, op.Delete.Tags)
   412  	verifyIDParam(t, op.Delete.Parameters[0], "The ID of the order")
   413  	assert.Equal(t, "#/responses/genericError", op.Delete.Responses.Default.Ref.String())
   414  	_, ok = op.Delete.Responses.StatusCodeResponses[204]
   415  	assert.True(t, ok)
   416  
   417  	// updateOrder
   418  	assert.NotNil(t, op.Put)
   419  	assert.Equal(t, "Updates an order.", op.Put.Summary)
   420  	assert.Equal(t, "", op.Put.Description)
   421  	assert.Equal(t, "updateOrder", op.Put.ID)
   422  	assert.EqualValues(t, []string{"orders"}, op.Put.Tags)
   423  	verifyIDParam(t, op.Put.Parameters[0], "The ID of the order")
   424  	verifyRefParam(t, op.Put.Parameters[1], "The order to submit", "order")
   425  	assert.Equal(t, "#/responses/genericError", op.Put.Responses.Default.Ref.String())
   426  	rs, ok = op.Put.Responses.StatusCodeResponses[200]
   427  	assert.True(t, ok)
   428  	assert.NotNil(t, rs.Schema)
   429  	aprop = rs.Schema
   430  	assert.Equal(t, "#/definitions/order", aprop.Ref.String())
   431  
   432  	// path /orders
   433  	op, ok = paths["/orders"]
   434  	assert.True(t, ok)
   435  	assert.NotNil(t, op)
   436  
   437  	// createOrder
   438  	assert.NotNil(t, op.Post)
   439  	assert.Equal(t, "Creates an order.", op.Post.Summary)
   440  	assert.Equal(t, "", op.Post.Description)
   441  	assert.Equal(t, "createOrder", op.Post.ID)
   442  	assert.EqualValues(t, []string{"orders"}, op.Post.Tags)
   443  	verifyRefParam(t, op.Post.Parameters[0], "The order to submit", "order")
   444  	assert.Equal(t, "#/responses/genericError", op.Post.Responses.Default.Ref.String())
   445  	rs, ok = op.Post.Responses.StatusCodeResponses[200]
   446  	assert.True(t, ok)
   447  	assert.Equal(t, "#/responses/orderResponse", rs.Ref.String())
   448  	rsm = doc.Responses["orderResponse"]
   449  	assert.NotNil(t, rsm.Schema)
   450  	assert.Equal(t, "#/definitions/order", rsm.Schema.Ref.String())
   451  }
   452  
   453  func verifyIDParam(t testing.TB, param spec.Parameter, description string) {
   454  	assert.Equal(t, description, param.Description)
   455  	assert.Equal(t, "path", param.In)
   456  	assert.Equal(t, "integer", param.Type)
   457  	assert.Equal(t, "int64", param.Format)
   458  	assert.True(t, param.Required)
   459  	assert.Equal(t, "ID", param.Extensions["x-go-name"])
   460  }
   461  
   462  func verifyRefParam(t testing.TB, param spec.Parameter, description, refed string) {
   463  	assert.Equal(t, description, param.Description)
   464  	assert.Equal(t, "body", param.In)
   465  	assert.Equal(t, "#/definitions/"+refed, param.Schema.Ref.String())
   466  	assert.True(t, param.Required)
   467  }
   468  
   469  func TestSectionedParser_TitleDescription(t *testing.T) {
   470  	text := `This has a title, separated by a whitespace line
   471  
   472  In this example the punctuation for the title should not matter for swagger.
   473  For go it will still make a difference though.
   474  `
   475  	text2 := `This has a title without whitespace.
   476  The punctuation here does indeed matter. But it won't for go.
   477  `
   478  
   479  	text3 := `This has a title, and markdown in the description
   480  
   481  See how markdown works now, we can have lists:
   482  
   483  + first item
   484  + second item
   485  + third item
   486  
   487  [Links works too](http://localhost)
   488  `
   489  
   490  	text4 := `This has whitespace sensitive markdown in the description
   491  
   492  |+ first item
   493  |    + nested item
   494  |    + also nested item
   495  
   496  Sample code block:
   497  
   498  |    fmt.Println("Hello World!")
   499  
   500  `
   501  
   502  	var err error
   503  
   504  	st := &sectionedParser{}
   505  	st.setTitle = func(lines []string) {}
   506  	err = st.Parse(ascg(text))
   507  	assert.NoError(t, err)
   508  
   509  	assert.EqualValues(t, []string{"This has a title, separated by a whitespace line"}, st.Title())
   510  	assert.EqualValues(t, []string{"In this example the punctuation for the title should not matter for swagger.", "For go it will still make a difference though."}, st.Description())
   511  
   512  	st = &sectionedParser{}
   513  	st.setTitle = func(lines []string) {}
   514  	err = st.Parse(ascg(text2))
   515  	assert.NoError(t, err)
   516  
   517  	assert.EqualValues(t, []string{"This has a title without whitespace."}, st.Title())
   518  	assert.EqualValues(t, []string{"The punctuation here does indeed matter. But it won't for go."}, st.Description())
   519  
   520  	st = &sectionedParser{}
   521  	st.setTitle = func(lines []string) {}
   522  	err = st.Parse(ascg(text3))
   523  	assert.NoError(t, err)
   524  
   525  	assert.EqualValues(t, []string{"This has a title, and markdown in the description"}, st.Title())
   526  	assert.EqualValues(t, []string{"See how markdown works now, we can have lists:", "", "+ first item", "+ second item", "+ third item", "", "[Links works too](http://localhost)"}, st.Description())
   527  
   528  	st = &sectionedParser{}
   529  	st.setTitle = func(lines []string) {}
   530  	err = st.Parse(ascg(text4))
   531  	assert.NoError(t, err)
   532  
   533  	assert.EqualValues(t, []string{"This has whitespace sensitive markdown in the description"}, st.Title())
   534  	assert.EqualValues(t, []string{"+ first item", "    + nested item", "    + also nested item", "", "Sample code block:", "", "    fmt.Println(\"Hello World!\")"}, st.Description())
   535  }
   536  
   537  func dummyBuilder() schemaValidations {
   538  	return schemaValidations{new(spec.Schema)}
   539  }
   540  
   541  func TestSectionedParser_TagsDescription(t *testing.T) {
   542  	block := `This has a title without whitespace.
   543  The punctuation here does indeed matter. But it won't for go.
   544  minimum: 10
   545  maximum: 20
   546  `
   547  	block2 := `This has a title without whitespace.
   548  The punctuation here does indeed matter. But it won't for go.
   549  
   550  minimum: 10
   551  maximum: 20
   552  `
   553  
   554  	var err error
   555  
   556  	st := &sectionedParser{}
   557  	st.setTitle = func(lines []string) {}
   558  	st.taggers = []tagParser{
   559  		{"Maximum", false, false, nil, &setMaximum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMaximumFmt, ""))}},
   560  		{"Minimum", false, false, nil, &setMinimum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMinimumFmt, ""))}},
   561  		{"MultipleOf", false, false, nil, &setMultipleOf{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMultipleOfFmt, ""))}},
   562  	}
   563  
   564  	err = st.Parse(ascg(block))
   565  	assert.NoError(t, err)
   566  	assert.EqualValues(t, []string{"This has a title without whitespace."}, st.Title())
   567  	assert.EqualValues(t, []string{"The punctuation here does indeed matter. But it won't for go."}, st.Description())
   568  	assert.Len(t, st.matched, 2)
   569  	_, ok := st.matched["Maximum"]
   570  	assert.True(t, ok)
   571  	_, ok = st.matched["Minimum"]
   572  	assert.True(t, ok)
   573  
   574  	st = &sectionedParser{}
   575  	st.setTitle = func(lines []string) {}
   576  	st.taggers = []tagParser{
   577  		{"Maximum", false, false, nil, &setMaximum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMaximumFmt, ""))}},
   578  		{"Minimum", false, false, nil, &setMinimum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMinimumFmt, ""))}},
   579  		{"MultipleOf", false, false, nil, &setMultipleOf{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMultipleOfFmt, ""))}},
   580  	}
   581  
   582  	err = st.Parse(ascg(block2))
   583  	assert.NoError(t, err)
   584  	assert.EqualValues(t, []string{"This has a title without whitespace."}, st.Title())
   585  	assert.EqualValues(t, []string{"The punctuation here does indeed matter. But it won't for go."}, st.Description())
   586  	assert.Len(t, st.matched, 2)
   587  	_, ok = st.matched["Maximum"]
   588  	assert.True(t, ok)
   589  	_, ok = st.matched["Minimum"]
   590  	assert.True(t, ok)
   591  }
   592  
   593  func TestSectionedParser_Empty(t *testing.T) {
   594  	block := `swagger:response someResponse`
   595  
   596  	var err error
   597  
   598  	st := &sectionedParser{}
   599  	st.setTitle = func(lines []string) {}
   600  	ap := newSchemaAnnotationParser("SomeResponse")
   601  	ap.rx = rxResponseOverride
   602  	st.annotation = ap
   603  
   604  	err = st.Parse(ascg(block))
   605  	assert.NoError(t, err)
   606  	assert.Empty(t, st.Title())
   607  	assert.Empty(t, st.Description())
   608  	assert.Empty(t, st.taggers)
   609  	assert.Equal(t, "SomeResponse", ap.GoName)
   610  	assert.Equal(t, "someResponse", ap.Name)
   611  }
   612  
   613  func TestSectionedParser_SkipSectionAnnotation(t *testing.T) {
   614  	block := `swagger:model someModel
   615  
   616  This has a title without whitespace.
   617  The punctuation here does indeed matter. But it won't for go.
   618  
   619  minimum: 10
   620  maximum: 20
   621  `
   622  	var err error
   623  
   624  	st := &sectionedParser{}
   625  	st.setTitle = func(lines []string) {}
   626  	ap := newSchemaAnnotationParser("SomeModel")
   627  	st.annotation = ap
   628  	st.taggers = []tagParser{
   629  		{"Maximum", false, false, nil, &setMaximum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMaximumFmt, ""))}},
   630  		{"Minimum", false, false, nil, &setMinimum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMinimumFmt, ""))}},
   631  		{"MultipleOf", false, false, nil, &setMultipleOf{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMultipleOfFmt, ""))}},
   632  	}
   633  
   634  	err = st.Parse(ascg(block))
   635  	assert.NoError(t, err)
   636  	assert.EqualValues(t, []string{"This has a title without whitespace."}, st.Title())
   637  	assert.EqualValues(t, []string{"The punctuation here does indeed matter. But it won't for go."}, st.Description())
   638  	assert.Len(t, st.matched, 2)
   639  	_, ok := st.matched["Maximum"]
   640  	assert.True(t, ok)
   641  	_, ok = st.matched["Minimum"]
   642  	assert.True(t, ok)
   643  	assert.Equal(t, "SomeModel", ap.GoName)
   644  	assert.Equal(t, "someModel", ap.Name)
   645  }
   646  
   647  func TestSectionedParser_TerminateOnNewAnnotation(t *testing.T) {
   648  	block := `swagger:model someModel
   649  
   650  This has a title without whitespace.
   651  The punctuation here does indeed matter. But it won't for go.
   652  
   653  minimum: 10
   654  swagger:meta
   655  maximum: 20
   656  `
   657  	var err error
   658  
   659  	st := &sectionedParser{}
   660  	st.setTitle = func(lines []string) {}
   661  	ap := newSchemaAnnotationParser("SomeModel")
   662  	st.annotation = ap
   663  	st.taggers = []tagParser{
   664  		{"Maximum", false, false, nil, &setMaximum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMaximumFmt, ""))}},
   665  		{"Minimum", false, false, nil, &setMinimum{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMinimumFmt, ""))}},
   666  		{"MultipleOf", false, false, nil, &setMultipleOf{dummyBuilder(), regexp.MustCompile(fmt.Sprintf(rxMultipleOfFmt, ""))}},
   667  	}
   668  
   669  	err = st.Parse(ascg(block))
   670  	assert.NoError(t, err)
   671  	assert.EqualValues(t, []string{"This has a title without whitespace."}, st.Title())
   672  	assert.EqualValues(t, []string{"The punctuation here does indeed matter. But it won't for go."}, st.Description())
   673  	assert.Len(t, st.matched, 1)
   674  	_, ok := st.matched["Maximum"]
   675  	assert.False(t, ok)
   676  	_, ok = st.matched["Minimum"]
   677  	assert.True(t, ok)
   678  	assert.Equal(t, "SomeModel", ap.GoName)
   679  	assert.Equal(t, "someModel", ap.Name)
   680  }
   681  
   682  func ascg(txt string) *ast.CommentGroup {
   683  	var cg ast.CommentGroup
   684  	for _, line := range strings.Split(txt, "\n") {
   685  		var cmt ast.Comment
   686  		cmt.Text = "// " + line
   687  		cg.List = append(cg.List, &cmt)
   688  	}
   689  	return &cg
   690  }
   691  
   692  func TestSchemaValueExtractors(t *testing.T) {
   693  	strfmts := []string{
   694  		"// swagger:strfmt ",
   695  		"* swagger:strfmt ",
   696  		"* swagger:strfmt ",
   697  		" swagger:strfmt ",
   698  		"swagger:strfmt ",
   699  		"// swagger:strfmt    ",
   700  		"* swagger:strfmt     ",
   701  		"* swagger:strfmt    ",
   702  		" swagger:strfmt     ",
   703  		"swagger:strfmt      ",
   704  	}
   705  	models := []string{
   706  		"// swagger:model ",
   707  		"* swagger:model ",
   708  		"* swagger:model ",
   709  		" swagger:model ",
   710  		"swagger:model ",
   711  		"// swagger:model    ",
   712  		"* swagger:model     ",
   713  		"* swagger:model    ",
   714  		" swagger:model     ",
   715  		"swagger:model      ",
   716  	}
   717  
   718  	allOf := []string{
   719  		"// swagger:allOf ",
   720  		"* swagger:allOf ",
   721  		"* swagger:allOf ",
   722  		" swagger:allOf ",
   723  		"swagger:allOf ",
   724  		"// swagger:allOf    ",
   725  		"* swagger:allOf     ",
   726  		"* swagger:allOf    ",
   727  		" swagger:allOf     ",
   728  		"swagger:allOf      ",
   729  	}
   730  
   731  	parameters := []string{
   732  		"// swagger:parameters ",
   733  		"* swagger:parameters ",
   734  		"* swagger:parameters ",
   735  		" swagger:parameters ",
   736  		"swagger:parameters ",
   737  		"// swagger:parameters    ",
   738  		"* swagger:parameters     ",
   739  		"* swagger:parameters    ",
   740  		" swagger:parameters     ",
   741  		"swagger:parameters      ",
   742  	}
   743  
   744  	validParams := []string{
   745  		"yada123",
   746  		"date",
   747  		"date-time",
   748  		"long-combo-1-with-combo-2-and-a-3rd-one-too",
   749  	}
   750  	invalidParams := []string{
   751  		"1-yada-3",
   752  		"1-2-3",
   753  		"-yada-3",
   754  		"-2-3",
   755  		"*blah",
   756  		"blah*",
   757  	}
   758  
   759  	verifySwaggerOneArgSwaggerTag(t, rxStrFmt, strfmts, validParams, append(invalidParams, "", "  ", " "))
   760  	verifySwaggerOneArgSwaggerTag(t, rxModelOverride, models, append(validParams, "", "  ", " "), invalidParams)
   761  
   762  	verifySwaggerOneArgSwaggerTag(t, rxAllOf, allOf, append(validParams, "", "  ", " "), invalidParams)
   763  
   764  	verifySwaggerMultiArgSwaggerTag(t, rxParametersOverride, parameters, validParams, invalidParams)
   765  
   766  	verifyMinMax(t, rxf(rxMinimumFmt, ""), "min", []string{"", ">", "="})
   767  	verifyMinMax(t, rxf(rxMinimumFmt, fmt.Sprintf(rxItemsPrefixFmt, 1)), "items.min", []string{"", ">", "="})
   768  	verifyMinMax(t, rxf(rxMaximumFmt, ""), "max", []string{"", "<", "="})
   769  	verifyMinMax(t, rxf(rxMaximumFmt, fmt.Sprintf(rxItemsPrefixFmt, 1)), "items.max", []string{"", "<", "="})
   770  	verifyNumeric2Words(t, rxf(rxMultipleOfFmt, ""), "multiple", "of")
   771  	verifyNumeric2Words(t, rxf(rxMultipleOfFmt, fmt.Sprintf(rxItemsPrefixFmt, 1)), "items.multiple", "of")
   772  
   773  	verifyIntegerMinMaxManyWords(t, rxf(rxMinLengthFmt, ""), "min", []string{"len", "length"})
   774  	// pattern
   775  	extraSpaces := []string{"", " ", "  ", "     "}
   776  	prefixes := []string{"//", "*", ""}
   777  	patArgs := []string{"^\\w+$", "[A-Za-z0-9-.]*"}
   778  	patNames := []string{"pattern", "Pattern"}
   779  	for _, pref := range prefixes {
   780  		for _, es1 := range extraSpaces {
   781  			for _, nm := range patNames {
   782  				for _, es2 := range extraSpaces {
   783  					for _, es3 := range extraSpaces {
   784  						for _, arg := range patArgs {
   785  							line := strings.Join([]string{pref, es1, nm, es2, ":", es3, arg}, "")
   786  							matches := rxf(rxPatternFmt, "").FindStringSubmatch(line)
   787  							assert.Len(t, matches, 2)
   788  							assert.Equal(t, arg, matches[1])
   789  						}
   790  					}
   791  				}
   792  			}
   793  		}
   794  	}
   795  
   796  	verifyIntegerMinMaxManyWords(t, rxf(rxMinItemsFmt, ""), "min", []string{"items"})
   797  	verifyBoolean(t, rxf(rxUniqueFmt, ""), []string{"unique"}, nil)
   798  
   799  	verifyBoolean(t, rxReadOnly, []string{"read"}, []string{"only"})
   800  	verifyBoolean(t, rxRequired, []string{"required"}, nil)
   801  }
   802  
   803  func makeMinMax(lower string) (res []string) {
   804  	for _, a := range []string{"", "imum"} {
   805  		res = append(res, lower+a, strings.Title(lower)+a)
   806  	}
   807  	return
   808  }
   809  
   810  func verifyBoolean(t *testing.T, matcher *regexp.Regexp, names, names2 []string) {
   811  	extraSpaces := []string{"", " ", "  ", "     "}
   812  	prefixes := []string{"//", "*", ""}
   813  	validArgs := []string{"true", "false"}
   814  	invalidArgs := []string{"TRUE", "FALSE", "t", "f", "1", "0", "True", "False", "true*", "false*"}
   815  	var nms []string
   816  	for _, nm := range names {
   817  		nms = append(nms, nm, strings.Title(nm))
   818  	}
   819  
   820  	var nms2 []string
   821  	for _, nm := range names2 {
   822  		nms2 = append(nms2, nm, strings.Title(nm))
   823  	}
   824  
   825  	var rnms []string
   826  	if len(nms2) > 0 {
   827  		for _, nm := range nms {
   828  			for _, es := range append(extraSpaces, "-") {
   829  				for _, nm2 := range nms2 {
   830  					rnms = append(rnms, strings.Join([]string{nm, es, nm2}, ""))
   831  				}
   832  			}
   833  		}
   834  	} else {
   835  		rnms = nms
   836  	}
   837  
   838  	var cnt int
   839  	for _, pref := range prefixes {
   840  		for _, es1 := range extraSpaces {
   841  			for _, nm := range rnms {
   842  				for _, es2 := range extraSpaces {
   843  					for _, es3 := range extraSpaces {
   844  						for _, vv := range validArgs {
   845  							line := strings.Join([]string{pref, es1, nm, es2, ":", es3, vv}, "")
   846  							matches := matcher.FindStringSubmatch(line)
   847  							assert.Len(t, matches, 2)
   848  							assert.Equal(t, vv, matches[1])
   849  							cnt++
   850  						}
   851  						for _, iv := range invalidArgs {
   852  							line := strings.Join([]string{pref, es1, nm, es2, ":", es3, iv}, "")
   853  							matches := matcher.FindStringSubmatch(line)
   854  							assert.Empty(t, matches)
   855  							cnt++
   856  						}
   857  					}
   858  				}
   859  			}
   860  		}
   861  	}
   862  	var nm2 string
   863  	if len(names2) > 0 {
   864  		nm2 = " " + names2[0]
   865  	}
   866  	var Debug = os.Getenv("DEBUG") != "" || os.Getenv("SWAGGER_DEBUG") != ""
   867  	if Debug {
   868  		fmt.Printf("tested %d %s%s combinations\n", cnt, names[0], nm2)
   869  	}
   870  }
   871  
   872  func verifyIntegerMinMaxManyWords(t *testing.T, matcher *regexp.Regexp, name1 string, words []string) {
   873  	extraSpaces := []string{"", " ", "  ", "     "}
   874  	prefixes := []string{"//", "*", ""}
   875  	validNumericArgs := []string{"0", "1234"}
   876  	invalidNumericArgs := []string{"1A3F", "2e10", "*12", "12*", "-1235", "0.0", "1234.0394", "-2948.484"}
   877  
   878  	var names []string
   879  	for _, w := range words {
   880  		names = append(names, w, strings.Title(w))
   881  	}
   882  
   883  	var cnt int
   884  	for _, pref := range prefixes {
   885  		for _, es1 := range extraSpaces {
   886  			for _, nm1 := range makeMinMax(name1) {
   887  				for _, es2 := range append(extraSpaces, "-") {
   888  					for _, nm2 := range names {
   889  						for _, es3 := range extraSpaces {
   890  							for _, es4 := range extraSpaces {
   891  								for _, vv := range validNumericArgs {
   892  									line := strings.Join([]string{pref, es1, nm1, es2, nm2, es3, ":", es4, vv}, "")
   893  									matches := matcher.FindStringSubmatch(line)
   894  									//fmt.Printf("matching %q, matches (%d): %v\n", line, len(matches), matches)
   895  									assert.Len(t, matches, 2)
   896  									assert.Equal(t, vv, matches[1])
   897  									cnt++
   898  								}
   899  								for _, iv := range invalidNumericArgs {
   900  									line := strings.Join([]string{pref, es1, nm1, es2, nm2, es3, ":", es4, iv}, "")
   901  									matches := matcher.FindStringSubmatch(line)
   902  									assert.Empty(t, matches)
   903  									cnt++
   904  								}
   905  							}
   906  						}
   907  					}
   908  				}
   909  			}
   910  		}
   911  	}
   912  	var nm2 string
   913  	if len(words) > 0 {
   914  		nm2 = " " + words[0]
   915  	}
   916  	var Debug = os.Getenv("DEBUG") != "" || os.Getenv("SWAGGER_DEBUG") != ""
   917  	if Debug {
   918  		fmt.Printf("tested %d %s%s combinations\n", cnt, name1, nm2)
   919  
   920  	}
   921  }
   922  
   923  func verifyNumeric2Words(t *testing.T, matcher *regexp.Regexp, name1, name2 string) {
   924  	extraSpaces := []string{"", " ", "  ", "     "}
   925  	prefixes := []string{"//", "*", ""}
   926  	validNumericArgs := []string{"0", "1234", "-1235", "0.0", "1234.0394", "-2948.484"}
   927  	invalidNumericArgs := []string{"1A3F", "2e10", "*12", "12*"}
   928  
   929  	var cnt int
   930  	for _, pref := range prefixes {
   931  		for _, es1 := range extraSpaces {
   932  			for _, es2 := range extraSpaces {
   933  				for _, es3 := range extraSpaces {
   934  					for _, es4 := range extraSpaces {
   935  						for _, vv := range validNumericArgs {
   936  							lines := []string{
   937  								strings.Join([]string{pref, es1, name1, es2, name2, es3, ":", es4, vv}, ""),
   938  								strings.Join([]string{pref, es1, strings.Title(name1), es2, strings.Title(name2), es3, ":", es4, vv}, ""),
   939  								strings.Join([]string{pref, es1, strings.Title(name1), es2, name2, es3, ":", es4, vv}, ""),
   940  								strings.Join([]string{pref, es1, name1, es2, strings.Title(name2), es3, ":", es4, vv}, ""),
   941  							}
   942  							for _, line := range lines {
   943  								matches := matcher.FindStringSubmatch(line)
   944  								//fmt.Printf("matching %q, matches (%d): %v\n", line, len(matches), matches)
   945  								assert.Len(t, matches, 2)
   946  								assert.Equal(t, vv, matches[1])
   947  								cnt++
   948  							}
   949  						}
   950  						for _, iv := range invalidNumericArgs {
   951  							lines := []string{
   952  								strings.Join([]string{pref, es1, name1, es2, name2, es3, ":", es4, iv}, ""),
   953  								strings.Join([]string{pref, es1, strings.Title(name1), es2, strings.Title(name2), es3, ":", es4, iv}, ""),
   954  								strings.Join([]string{pref, es1, strings.Title(name1), es2, name2, es3, ":", es4, iv}, ""),
   955  								strings.Join([]string{pref, es1, name1, es2, strings.Title(name2), es3, ":", es4, iv}, ""),
   956  							}
   957  							for _, line := range lines {
   958  								matches := matcher.FindStringSubmatch(line)
   959  								//fmt.Printf("matching %q, matches (%d): %v\n", line, len(matches), matches)
   960  								assert.Empty(t, matches)
   961  								cnt++
   962  							}
   963  						}
   964  					}
   965  				}
   966  			}
   967  		}
   968  	}
   969  	var Debug = os.Getenv("DEBUG") != "" || os.Getenv("SWAGGER_DEBUG") != ""
   970  	if Debug {
   971  		fmt.Printf("tested %d %s %s combinations\n", cnt, name1, name2)
   972  	}
   973  }
   974  
   975  func verifyMinMax(t *testing.T, matcher *regexp.Regexp, name string, operators []string) {
   976  	extraSpaces := []string{"", " ", "  ", "     "}
   977  	prefixes := []string{"//", "*", ""}
   978  	validNumericArgs := []string{"0", "1234", "-1235", "0.0", "1234.0394", "-2948.484"}
   979  	invalidNumericArgs := []string{"1A3F", "2e10", "*12", "12*"}
   980  
   981  	var cnt int
   982  	for _, pref := range prefixes {
   983  		for _, es1 := range extraSpaces {
   984  			for _, wrd := range makeMinMax(name) {
   985  				for _, es2 := range extraSpaces {
   986  					for _, es3 := range extraSpaces {
   987  						for _, op := range operators {
   988  							for _, es4 := range extraSpaces {
   989  								for _, vv := range validNumericArgs {
   990  									line := strings.Join([]string{pref, es1, wrd, es2, ":", es3, op, es4, vv}, "")
   991  									matches := matcher.FindStringSubmatch(line)
   992  									// fmt.Printf("matching %q with %q, matches (%d): %v\n", line, matcher, len(matches), matches)
   993  									assert.Len(t, matches, 3)
   994  									assert.Equal(t, vv, matches[2])
   995  									cnt++
   996  								}
   997  								for _, iv := range invalidNumericArgs {
   998  									line := strings.Join([]string{pref, es1, wrd, es2, ":", es3, op, es4, iv}, "")
   999  									matches := matcher.FindStringSubmatch(line)
  1000  									assert.Empty(t, matches)
  1001  									cnt++
  1002  								}
  1003  							}
  1004  						}
  1005  					}
  1006  				}
  1007  			}
  1008  		}
  1009  	}
  1010  	var Debug = os.Getenv("DEBUG") != "" || os.Getenv("SWAGGER_DEBUG") != ""
  1011  	if Debug {
  1012  		fmt.Printf("tested %d %s combinations\n", cnt, name)
  1013  	}
  1014  }
  1015  
  1016  func verifySwaggerOneArgSwaggerTag(t *testing.T, matcher *regexp.Regexp, prefixes, validParams, invalidParams []string) {
  1017  	for _, pref := range prefixes {
  1018  		for _, param := range validParams {
  1019  			line := pref + param
  1020  			matches := matcher.FindStringSubmatch(line)
  1021  			if assert.Len(t, matches, 2) {
  1022  				assert.Equal(t, strings.TrimSpace(param), matches[1])
  1023  			}
  1024  		}
  1025  	}
  1026  
  1027  	for _, pref := range prefixes {
  1028  		for _, param := range invalidParams {
  1029  			line := pref + param
  1030  			matches := matcher.FindStringSubmatch(line)
  1031  			assert.Empty(t, matches)
  1032  		}
  1033  	}
  1034  }
  1035  
  1036  func verifySwaggerMultiArgSwaggerTag(t *testing.T, matcher *regexp.Regexp, prefixes, validParams, invalidParams []string) {
  1037  	var actualParams []string
  1038  	for i := 0; i < len(validParams); i++ {
  1039  		var vp []string
  1040  		for j := 0; j < (i + 1); j++ {
  1041  			vp = append(vp, validParams[j])
  1042  		}
  1043  		actualParams = append(actualParams, strings.Join(vp, " "))
  1044  	}
  1045  	for _, pref := range prefixes {
  1046  		for _, param := range actualParams {
  1047  			line := pref + param
  1048  			matches := matcher.FindStringSubmatch(line)
  1049  			// fmt.Printf("matching %q with %q, matches (%d): %v\n", line, matcher, len(matches), matches)
  1050  			assert.Len(t, matches, 2)
  1051  			assert.Equal(t, strings.TrimSpace(param), matches[1])
  1052  		}
  1053  	}
  1054  
  1055  	for _, pref := range prefixes {
  1056  		for _, param := range invalidParams {
  1057  			line := pref + param
  1058  			matches := matcher.FindStringSubmatch(line)
  1059  			assert.Empty(t, matches)
  1060  		}
  1061  	}
  1062  }
  1063  
  1064  func TestEnhancement793(t *testing.T) {
  1065  	var err error
  1066  	scanner, err := newAppScanner(&Opts{
  1067  		BasePath:   "../fixtures/enhancements/793",
  1068  		ScanModels: true,
  1069  	})
  1070  	assert.NoError(t, err)
  1071  	assert.NotNil(t, scanner)
  1072  	doc, err := scanner.Parse()
  1073  	assert.NoError(t, err)
  1074  	if assert.NotNil(t, doc) {
  1075  		bytes, err := doc.MarshalJSON()
  1076  		assert.NoError(t, err)
  1077  		assert.NotEmpty(t, bytes)
  1078  
  1079  		file, _ := ioutil.TempFile(os.TempDir(), "scanner")
  1080  		file.Write(bytes)
  1081  		file.Close()
  1082  
  1083  		doc, err := loads.Spec(file.Name())
  1084  		if assert.NoError(t, err) {
  1085  			validator := validate.NewSpecValidator(doc.Schema(), strfmt.Default)
  1086  			res, _ := validator.Validate(doc)
  1087  			assert.Empty(t, res.Errors)
  1088  			assert.True(t, res.IsValid())
  1089  		}
  1090  	}
  1091  }