github.com/go-swagger/go-swagger@v0.31.0/generator/operation_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 generator
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"os"
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/go-openapi/analysis"
    25  	"github.com/go-openapi/loads"
    26  	"github.com/go-openapi/spec"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestUniqueOperationNameMangling(t *testing.T) {
    32  	doc, err := loads.Spec("../fixtures/bugs/2213/fixture-2213.yaml")
    33  	require.NoError(t, err)
    34  	analyzed := analysis.New(doc.Spec())
    35  	ops := gatherOperations(analyzed, nil)
    36  	assert.Contains(t, ops, "GetFoo")
    37  	assert.Contains(t, ops, "GetAFoo")
    38  }
    39  
    40  func TestUniqueOperationNames(t *testing.T) {
    41  	doc, err := loads.Spec("../fixtures/codegen/todolist.simple.yml")
    42  	require.NoError(t, err)
    43  
    44  	sp := doc.Spec()
    45  	sp.Paths.Paths["/tasks"].Post.ID = "saveTask"
    46  	sp.Paths.Paths["/tasks"].Post.AddExtension("origName", "createTask")
    47  	sp.Paths.Paths["/tasks/{id}"].Put.ID = "saveTask"
    48  	sp.Paths.Paths["/tasks/{id}"].Put.AddExtension("origName", "updateTask")
    49  	analyzed := analysis.New(sp)
    50  
    51  	ops := gatherOperations(analyzed, nil)
    52  	assert.Len(t, ops, 6)
    53  	_, exists := ops["saveTask"]
    54  	assert.True(t, exists)
    55  	_, exists = ops["PutTasksID"]
    56  	assert.True(t, exists)
    57  }
    58  
    59  func TestEmptyOperationNames(t *testing.T) {
    60  	doc, err := loads.Spec("../fixtures/codegen/todolist.simple.yml")
    61  	require.NoError(t, err)
    62  
    63  	sp := doc.Spec()
    64  	sp.Paths.Paths["/tasks"].Post.ID = ""
    65  	sp.Paths.Paths["/tasks"].Post.AddExtension("origName", "createTask")
    66  	sp.Paths.Paths["/tasks/{id}"].Put.ID = ""
    67  	sp.Paths.Paths["/tasks/{id}"].Put.AddExtension("origName", "updateTask")
    68  	analyzed := analysis.New(sp)
    69  
    70  	ops := gatherOperations(analyzed, nil)
    71  	assert.Len(t, ops, 6)
    72  	_, exists := ops["PostTasks"]
    73  	assert.True(t, exists)
    74  	_, exists = ops["PutTasksID"]
    75  	assert.True(t, exists)
    76  }
    77  
    78  func TestMakeResponseHeader(t *testing.T) {
    79  	b, err := opBuilder("getTasks", "")
    80  	require.NoError(t, err)
    81  
    82  	hdr := findResponseHeader(&b.Operation, 200, "X-Rate-Limit")
    83  	gh, er := b.MakeHeader("a", "X-Rate-Limit", *hdr)
    84  	require.NoError(t, er)
    85  
    86  	assert.True(t, gh.IsPrimitive)
    87  	assert.Equal(t, "int32", gh.GoType)
    88  	assert.Equal(t, "X-Rate-Limit", gh.Name)
    89  }
    90  
    91  func TestMakeResponseHeaderDefaultValues(t *testing.T) {
    92  	b, err := opBuilder("getTasks", "")
    93  	require.NoError(t, err)
    94  
    95  	testCases := []struct {
    96  		name         string      // input
    97  		typeStr      string      // expected type
    98  		defaultValue interface{} // expected result
    99  	}{
   100  		{"Access-Control-Allow-Origin", "string", "*"},
   101  		{"X-Rate-Limit", "int32", nil},
   102  		{"X-Rate-Limit-Remaining", "int32", float64(42)},
   103  		{"X-Rate-Limit-Reset", "int32", "1449875311"},
   104  		{"X-Rate-Limit-Reset-Human", "string", "3 days"},
   105  		{"X-Rate-Limit-Reset-Human-Number", "string", float64(3)},
   106  	}
   107  
   108  	for _, tc := range testCases {
   109  		hdr := findResponseHeader(&b.Operation, 200, tc.name)
   110  		require.NotNil(t, hdr)
   111  
   112  		gh, er := b.MakeHeader("a", tc.name, *hdr)
   113  		require.NoError(t, er)
   114  
   115  		assert.True(t, gh.IsPrimitive)
   116  		assert.Equal(t, tc.typeStr, gh.GoType)
   117  		assert.Equal(t, tc.name, gh.Name)
   118  		assert.Exactly(t, tc.defaultValue, gh.Default)
   119  	}
   120  }
   121  
   122  func TestMakeResponse(t *testing.T) {
   123  	b, err := opBuilder("getTasks", "")
   124  	require.NoError(t, err)
   125  
   126  	resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc}
   127  	resolver.KnownDefs = make(map[string]struct{})
   128  	for k := range b.Doc.Spec().Definitions {
   129  		resolver.KnownDefs[k] = struct{}{}
   130  	}
   131  	gO, err := b.MakeResponse("a", "getTasksSuccess", true, resolver, 200, b.Operation.Responses.StatusCodeResponses[200])
   132  	require.NoError(t, err)
   133  
   134  	assert.Len(t, gO.Headers, 6)
   135  	assert.NotNil(t, gO.Schema)
   136  	assert.True(t, gO.Schema.IsArray)
   137  	assert.NotNil(t, gO.Schema.Items)
   138  	assert.False(t, gO.Schema.IsAnonymous)
   139  	assert.Equal(t, "[]*models.Task", gO.Schema.GoType)
   140  }
   141  
   142  func TestMakeResponse_WithAllOfSchema(t *testing.T) {
   143  	b, err := methodPathOpBuilder("get", "/media/search", "../fixtures/codegen/instagram.yml")
   144  	require.NoError(t, err)
   145  
   146  	resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc}
   147  	resolver.KnownDefs = make(map[string]struct{})
   148  	for k := range b.Doc.Spec().Definitions {
   149  		resolver.KnownDefs[k] = struct{}{}
   150  	}
   151  	gO, err := b.MakeResponse("a", "get /media/search", true, resolver, 200, b.Operation.Responses.StatusCodeResponses[200])
   152  	require.NoError(t, err)
   153  
   154  	require.NotNil(t, gO.Schema)
   155  	assert.Equal(t, "GetMediaSearchBody", gO.Schema.GoType)
   156  
   157  	require.NotEmpty(t, b.ExtraSchemas)
   158  	body := b.ExtraSchemas["GetMediaSearchBody"]
   159  	require.NotEmpty(t, body.Properties)
   160  
   161  	prop := body.Properties[0]
   162  	assert.Equal(t, "data", prop.Name)
   163  	// is in models only when definition is flattened: otherwise, ExtraSchema is rendered in operations package
   164  	assert.Equal(t, "[]*GetMediaSearchBodyDataItems0", prop.GoType)
   165  
   166  	items := b.ExtraSchemas["GetMediaSearchBodyDataItems0"]
   167  	require.NotEmpty(t, items.AllOf)
   168  
   169  	media := items.AllOf[0]
   170  	// expect #definitions/media to be captured and reused by ExtraSchema
   171  	assert.Equal(t, "models.Media", media.GoType)
   172  }
   173  
   174  func TestMakeOperationParam(t *testing.T) {
   175  	b, err := opBuilder("getTasks", "")
   176  	require.NoError(t, err)
   177  
   178  	resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc}
   179  	gO, err := b.MakeParameter("a", resolver, b.Operation.Parameters[0], nil)
   180  	require.NoError(t, err)
   181  
   182  	assert.Equal(t, "size", gO.Name)
   183  	assert.True(t, gO.IsPrimitive)
   184  }
   185  
   186  func TestMakeOperationParamItem(t *testing.T) {
   187  	b, err := opBuilder("arrayQueryParams", "../fixtures/codegen/todolist.arrayquery.yml")
   188  	require.NoError(t, err)
   189  	resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc}
   190  	gO, err := b.MakeParameterItem("a", "siString", "ii", "siString", "a.SiString", "query", resolver, b.Operation.Parameters[1].Items, nil)
   191  	require.NoError(t, err)
   192  	assert.Nil(t, gO.Parent)
   193  	assert.True(t, gO.IsPrimitive)
   194  }
   195  
   196  func TestMakeOperation(t *testing.T) {
   197  	b, err := opBuilder("getTasks", "")
   198  	require.NoError(t, err)
   199  	gO, err := b.MakeOperation()
   200  	require.NoError(t, err)
   201  	assert.Equal(t, "getTasks", gO.Name)
   202  	assert.Equal(t, "GET", gO.Method)
   203  	assert.Equal(t, "/tasks", gO.Path)
   204  	assert.Len(t, gO.Params, 2)
   205  	assert.Len(t, gO.Responses, 1)
   206  	assert.NotNil(t, gO.DefaultResponse)
   207  	assert.NotNil(t, gO.SuccessResponse)
   208  }
   209  
   210  func TestRenderOperation_InstagramSearch(t *testing.T) {
   211  	defer discardOutput()()
   212  
   213  	b, err := methodPathOpBuilder("get", "/media/search", "../fixtures/codegen/instagram.yml")
   214  	require.NoError(t, err)
   215  
   216  	gO, err := b.MakeOperation()
   217  	require.NoError(t, err)
   218  
   219  	buf := bytes.NewBuffer(nil)
   220  	opt := opts()
   221  
   222  	require.NoError(t, opt.templates.MustGet("serverOperation").Execute(buf, gO))
   223  
   224  	ff, err := opt.LanguageOpts.FormatContent("operation.go", buf.Bytes())
   225  	require.NoErrorf(t, err, buf.String())
   226  
   227  	res := string(ff)
   228  	assertInCode(t, "type GetMediaSearchOKBody struct {", res)
   229  	// codegen does not assumes objects are only in models
   230  	// this is inlined
   231  	assertInCode(t, "Data []*GetMediaSearchOKBodyDataItems0 `json:\"data\"`", res)
   232  	assertInCode(t, "type GetMediaSearchOKBodyDataItems0 struct {", res)
   233  	// this is a definition: expect this definition to be reused from the models pkg
   234  	assertInCode(t, "models.Media", res)
   235  
   236  	buf = bytes.NewBuffer(nil)
   237  	require.NoError(t, opt.templates.MustGet("serverResponses").Execute(buf, gO))
   238  
   239  	ff, err = opt.LanguageOpts.FormatContent("response.go", buf.Bytes())
   240  	require.NoErrorf(t, err, buf.String())
   241  
   242  	res = string(ff)
   243  	// codegen does not assumes objects are only in models
   244  	assertInCode(t, "type GetMediaSearchOK struct {", res)
   245  	assertInCode(t, "GetMediaSearchOKBody", res)
   246  
   247  	b, err = methodPathOpBuilderWithFlatten("get", "/media/search", "../fixtures/codegen/instagram.yml")
   248  	require.NoError(t, err)
   249  
   250  	gO, err = b.MakeOperation()
   251  	require.NoError(t, err)
   252  
   253  	buf = bytes.NewBuffer(nil)
   254  	opt = opts()
   255  	require.NoError(t, opt.templates.MustGet("serverOperation").Execute(buf, gO))
   256  
   257  	ff, err = opt.LanguageOpts.FormatContent("operation.go", buf.Bytes())
   258  	require.NoErrorf(t, err, buf.String())
   259  
   260  	res = string(ff)
   261  	assertNotInCode(t, "DataItems0", res)
   262  	assertNotInCode(t, "models", res)
   263  
   264  	buf = bytes.NewBuffer(nil)
   265  	require.NoError(t, opt.templates.MustGet("serverResponses").Execute(buf, gO))
   266  
   267  	ff, err = opt.LanguageOpts.FormatContent("operation.go", buf.Bytes())
   268  	require.NoErrorf(t, err, buf.String())
   269  
   270  	res = string(ff)
   271  	assertInCode(t, "Payload *models.GetMediaSearchOKBody", res)
   272  }
   273  
   274  func methodPathOpBuilder(method, path, fname string) (codeGenOpBuilder, error) {
   275  	defer discardOutput()()
   276  
   277  	if fname == "" {
   278  		fname = "../fixtures/codegen/todolist.simple.yml"
   279  	}
   280  	o := opts()
   281  	o.Spec = fname
   282  	specDoc, analyzed, err := o.analyzeSpec()
   283  	if err != nil {
   284  		return codeGenOpBuilder{}, err
   285  	}
   286  	op, ok := analyzed.OperationFor(method, path)
   287  	if !ok {
   288  		return codeGenOpBuilder{}, errors.New("No operation could be found for " + method + " " + path)
   289  	}
   290  
   291  	return codeGenOpBuilder{
   292  		Name:          method + " " + path,
   293  		Method:        method,
   294  		Path:          path,
   295  		APIPackage:    "restapi",
   296  		ModelsPackage: "models",
   297  		Principal:     "models.User",
   298  		Target:        ".",
   299  		Operation:     *op,
   300  		Doc:           specDoc,
   301  		PristineDefs:  specDoc.Pristine(),
   302  		Analyzed:      analyzed,
   303  		Authed:        false,
   304  		ExtraSchemas:  make(map[string]GenSchema),
   305  		GenOpts:       o,
   306  	}, nil
   307  }
   308  
   309  // methodPathOpBuilderWithFlatten prepares an operation build based on method and path, with spec full flattening
   310  func methodPathOpBuilderWithFlatten(method, path, fname string) (codeGenOpBuilder, error) {
   311  	defer discardOutput()()
   312  
   313  	if fname == "" {
   314  		fname = "../fixtures/codegen/todolist.simple.yml"
   315  	}
   316  
   317  	o := opBuildGetOpts(fname, true, false) // flatten: true, minimal: false
   318  	o.Spec = fname
   319  	specDoc, analyzed, err := o.analyzeSpec()
   320  	if err != nil {
   321  		return codeGenOpBuilder{}, err
   322  	}
   323  	op, ok := analyzed.OperationFor(method, path)
   324  	if !ok {
   325  		return codeGenOpBuilder{}, errors.New("No operation could be found for " + method + " " + path)
   326  	}
   327  
   328  	return codeGenOpBuilder{
   329  		Name:          method + " " + path,
   330  		Method:        method,
   331  		Path:          path,
   332  		APIPackage:    "restapi",
   333  		ModelsPackage: "models",
   334  		Principal:     "models.User",
   335  		Target:        ".",
   336  		Operation:     *op,
   337  		Doc:           specDoc,
   338  		Analyzed:      analyzed,
   339  		Authed:        false,
   340  		ExtraSchemas:  make(map[string]GenSchema),
   341  		GenOpts:       opts(),
   342  	}, nil
   343  }
   344  
   345  // opBuilderWithOpts prepares the making of an operation with spec flattening options
   346  func opBuilderWithOpts(name, fname string, o *GenOpts) (codeGenOpBuilder, error) {
   347  	defer discardOutput()()
   348  
   349  	if fname == "" {
   350  		// default fixture
   351  		fname = "../fixtures/codegen/todolist.simple.yml"
   352  	}
   353  
   354  	o.Spec = fname
   355  	specDoc, analyzed, err := o.analyzeSpec()
   356  	if err != nil {
   357  		return codeGenOpBuilder{}, err
   358  	}
   359  
   360  	method, path, op, ok := analyzed.OperationForName(name)
   361  	if !ok {
   362  		return codeGenOpBuilder{}, errors.New("No operation could be found for " + name)
   363  	}
   364  
   365  	return codeGenOpBuilder{
   366  		Name:          name,
   367  		Method:        method,
   368  		Path:          path,
   369  		BasePath:      specDoc.BasePath(),
   370  		APIPackage:    "restapi",
   371  		ModelsPackage: "models",
   372  		Principal:     "models.User",
   373  		Target:        ".",
   374  		Operation:     *op,
   375  		Doc:           specDoc,
   376  		PristineDefs:  specDoc.Pristine(),
   377  		Analyzed:      analyzed,
   378  		Authed:        false,
   379  		ExtraSchemas:  make(map[string]GenSchema),
   380  		GenOpts:       o,
   381  	}, nil
   382  }
   383  
   384  func opBuildGetOpts(specName string, withFlatten bool, withMinimalFlatten bool) (opts *GenOpts) {
   385  	opts = &GenOpts{}
   386  	opts.ValidateSpec = true
   387  	opts.FlattenOpts = &analysis.FlattenOpts{
   388  		Expand:  !withFlatten,
   389  		Minimal: withMinimalFlatten,
   390  	}
   391  	opts.Spec = specName
   392  	if err := opts.EnsureDefaults(); err != nil {
   393  		panic("Cannot initialize GenOpts")
   394  	}
   395  	return
   396  }
   397  
   398  // opBuilderWithFlatten prepares the making of an operation with spec full flattening prior to rendering
   399  func opBuilderWithFlatten(name, fname string) (codeGenOpBuilder, error) {
   400  	o := opBuildGetOpts(fname, true, false) // flatten: true, minimal: false
   401  	return opBuilderWithOpts(name, fname, o)
   402  }
   403  
   404  /*
   405  // opBuilderWithMinimalFlatten prepares the making of an operation with spec minimal flattening prior to rendering
   406  func opBuilderWithMinimalFlatten(name, fname string) (codeGenOpBuilder, error) {
   407  	o := opBuildGetOpts(fname, true, true) // flatten: true, minimal: true
   408  	return opBuilderWithOpts(name, fname, o)
   409  }
   410  */
   411  
   412  // opBuilderWithExpand prepares the making of an operation with spec expansion prior to rendering
   413  func opBuilderWithExpand(name, fname string) (codeGenOpBuilder, error) {
   414  	o := opBuildGetOpts(fname, false, false) // flatten: false => expand
   415  	return opBuilderWithOpts(name, fname, o)
   416  }
   417  
   418  // opBuilder prepares the making of an operation with spec minimal flattening (default for CLI)
   419  func opBuilder(name, fname string) (codeGenOpBuilder, error) {
   420  	o := opBuildGetOpts(fname, true, true) // flatten:true, minimal: true
   421  	// some fixtures do not fully validate - skip this
   422  	o.ValidateSpec = false
   423  	return opBuilderWithOpts(name, fname, o)
   424  }
   425  
   426  func findResponseHeader(op *spec.Operation, code int, name string) *spec.Header {
   427  	resp := op.Responses.Default
   428  	if code > 0 {
   429  		bb, ok := op.Responses.StatusCodeResponses[code]
   430  		if ok {
   431  			resp = &bb
   432  		}
   433  	}
   434  
   435  	if resp == nil {
   436  		return nil
   437  	}
   438  
   439  	hdr, ok := resp.Headers[name]
   440  	if !ok {
   441  		return nil
   442  	}
   443  
   444  	return &hdr
   445  }
   446  
   447  func TestDateFormat_Spec1(t *testing.T) {
   448  	b, err := opBuilder("putTesting", "../fixtures/bugs/193/spec1.json")
   449  	require.NoError(t, err)
   450  
   451  	op, err := b.MakeOperation()
   452  	require.NoError(t, err)
   453  
   454  	buf := bytes.NewBuffer(nil)
   455  	opts := opts()
   456  	opts.defaultsEnsured = false
   457  	opts.IsClient = true
   458  	require.NoError(t, opts.EnsureDefaults())
   459  
   460  	require.NoError(t, opts.templates.MustGet("clientParameter").Execute(buf, op))
   461  
   462  	ff, err := opts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes())
   463  	require.NoErrorf(t, err, buf.String())
   464  
   465  	assertInCode(t, "frTestingThis.String()", string(ff))
   466  }
   467  
   468  func TestDateFormat_Spec2(t *testing.T) {
   469  	b, err := opBuilder("putTesting", "../fixtures/bugs/193/spec2.json")
   470  	require.NoError(t, err)
   471  
   472  	op, err := b.MakeOperation()
   473  	require.NoError(t, err)
   474  
   475  	buf := bytes.NewBuffer(nil)
   476  	opts := opts()
   477  	opts.defaultsEnsured = false
   478  	opts.IsClient = true
   479  	require.NoError(t, opts.EnsureDefaults())
   480  
   481  	require.NoError(t, opts.templates.MustGet("clientParameter").Execute(buf, op))
   482  
   483  	ff, err := opts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes())
   484  	require.NoErrorf(t, err, buf.String())
   485  
   486  	res := string(ff)
   487  	assertInCode(t, "o.TestingThis != nil {", res)
   488  	assertInCode(t, "joinedTestingThis := o.bindParamTestingThis(reg)", res)
   489  	assertInCode(t, `if err := r.SetFormParam("testingThis", joinedTestingThis...); err != nil {`, res)
   490  	assertInCode(t, "func (o *PutTestingParams) bindParamTestingThis(formats strfmt.Registry) []string {", res)
   491  	assertInCode(t, "testingThisIR := o.TestingThis", res)
   492  	assertInCode(t, "var testingThisIC []string", res)
   493  	assertInCode(t, "for _, testingThisIIR := range testingThisIR {", res)
   494  	assertInCode(t, "testingThisIIV := testingThisIIR.String()", res)
   495  	assertInCode(t, "testingThisIC = append(testingThisIC, testingThisIIV)", res)
   496  	assertInCode(t, `testingThisIS := swag.JoinByFormat(testingThisIC, "")`, res)
   497  	assertInCode(t, "return testingThisIS", res)
   498  }
   499  
   500  func TestBuilder_Issue1703(t *testing.T) {
   501  	defer discardOutput()()
   502  
   503  	dr := testCwd(t)
   504  
   505  	opts := &GenOpts{
   506  		GenOptsCommon: GenOptsCommon{
   507  			Spec:              filepath.FromSlash("../fixtures/codegen/existing-model.yml"),
   508  			IncludeModel:      true,
   509  			IncludeHandler:    true,
   510  			IncludeParameters: true,
   511  			IncludeResponses:  true,
   512  			IncludeMain:       true,
   513  			APIPackage:        "restapi",
   514  			ModelPackage:      "model",
   515  			ServerPackage:     "server",
   516  			ClientPackage:     "client",
   517  			Target:            dr,
   518  		},
   519  	}
   520  	require.NoError(t, opts.EnsureDefaults())
   521  
   522  	appGen, err := newAppGenerator("x-go-type-import-bug", nil, nil, opts)
   523  	require.NoError(t, err)
   524  
   525  	op, err := appGen.makeCodegenApp()
   526  	require.NoError(t, err)
   527  
   528  	for _, o := range op.Operations {
   529  		buf := bytes.NewBuffer(nil)
   530  		require.NoError(t, opts.templates.MustGet("serverResponses").Execute(buf, o))
   531  
   532  		ff, err := appGen.GenOpts.LanguageOpts.FormatContent("response.go", buf.Bytes())
   533  		require.NoErrorf(t, err, buf.String())
   534  
   535  		assertInCode(t, "jwk \"github.com/user/package\"", string(ff))
   536  	}
   537  }
   538  
   539  func TestBuilder_Issue287(t *testing.T) {
   540  	defer discardOutput()()
   541  
   542  	dr := testCwd(t)
   543  
   544  	opts := &GenOpts{
   545  		GenOptsCommon: GenOptsCommon{
   546  			Spec:              filepath.FromSlash("../fixtures/bugs/287/swagger.yml"),
   547  			IncludeModel:      true,
   548  			IncludeHandler:    true,
   549  			IncludeParameters: true,
   550  			IncludeResponses:  true,
   551  			IncludeMain:       true,
   552  			APIPackage:        "restapi",
   553  			ModelPackage:      "model",
   554  			ServerPackage:     "server",
   555  			ClientPackage:     "client",
   556  			Target:            dr,
   557  		},
   558  	}
   559  	require.NoError(t, opts.EnsureDefaults())
   560  
   561  	appGen, err := newAppGenerator("plainTexter", nil, nil, opts)
   562  	require.NoError(t, err)
   563  
   564  	op, err := appGen.makeCodegenApp()
   565  	require.NoError(t, err)
   566  
   567  	buf := bytes.NewBuffer(nil)
   568  	require.NoError(t, opts.templates.MustGet("serverBuilder").Execute(buf, op))
   569  
   570  	ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes())
   571  	require.NoErrorf(t, err, buf.String())
   572  
   573  	assertInCode(t, "case \"text/plain\":", string(ff))
   574  }
   575  
   576  func TestBuilder_Issue465(t *testing.T) {
   577  	defer discardOutput()()
   578  
   579  	dr := testCwd(t)
   580  
   581  	opts := &GenOpts{
   582  		GenOptsCommon: GenOptsCommon{
   583  			Spec:              filepath.FromSlash("../fixtures/bugs/465/swagger.yml"),
   584  			IncludeModel:      true,
   585  			IncludeHandler:    true,
   586  			IncludeParameters: true,
   587  			IncludeResponses:  true,
   588  			IncludeMain:       true,
   589  			APIPackage:        "restapi",
   590  			ModelPackage:      "model",
   591  			ServerPackage:     "server",
   592  			ClientPackage:     "client",
   593  			Target:            dr,
   594  			IsClient:          true,
   595  		},
   596  	}
   597  	require.NoError(t, opts.EnsureDefaults())
   598  
   599  	appGen, err := newAppGenerator("plainTexter", nil, nil, opts)
   600  	require.NoError(t, err)
   601  
   602  	op, err := appGen.makeCodegenApp()
   603  	require.NoError(t, err)
   604  
   605  	buf := bytes.NewBuffer(nil)
   606  	require.NoError(t, opts.templates.MustGet("clientFacade").Execute(buf, op))
   607  
   608  	ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes())
   609  	require.NoErrorf(t, err, buf.String())
   610  
   611  	assertInCode(t, "/v1/fancyAPI", string(ff))
   612  }
   613  
   614  func TestBuilder_Issue500(t *testing.T) {
   615  	defer discardOutput()()
   616  
   617  	dr := testCwd(t)
   618  
   619  	opts := &GenOpts{
   620  		GenOptsCommon: GenOptsCommon{
   621  			Spec:              filepath.FromSlash("../fixtures/bugs/500/swagger.yml"),
   622  			IncludeModel:      true,
   623  			IncludeHandler:    true,
   624  			IncludeParameters: true,
   625  			IncludeResponses:  true,
   626  			IncludeMain:       true,
   627  			APIPackage:        "restapi",
   628  			ModelPackage:      "model",
   629  			ServerPackage:     "server",
   630  			ClientPackage:     "client",
   631  			Target:            dr,
   632  		},
   633  	}
   634  	require.NoError(t, opts.EnsureDefaults())
   635  
   636  	appGen, err := newAppGenerator("multiTags", nil, nil, opts)
   637  	require.NoError(t, err)
   638  
   639  	op, err := appGen.makeCodegenApp()
   640  	require.NoError(t, err)
   641  
   642  	buf := bytes.NewBuffer(nil)
   643  	require.NoError(t, opts.templates.MustGet("serverBuilder").Execute(buf, op))
   644  
   645  	ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes())
   646  	require.NoErrorf(t, err, buf.String())
   647  
   648  	res := string(ff)
   649  	assertNotInCode(t, `o.handlers["GET"]["/payment/{invoice_id}/payments/{payment_id}"] = NewGetPaymentByID(o.context, o.GetPaymentByIDHandler)`, res)
   650  	assertInCode(t, `o.handlers["GET"]["/payment/{invoice_id}/payments/{payment_id}"] = invoices.NewGetPaymentByID(o.context, o.InvoicesGetPaymentByIDHandler)`, res)
   651  }
   652  
   653  func TestGenClient_IllegalBOM(t *testing.T) {
   654  	b, err := methodPathOpBuilder("get", "/v3/attachments/{attachmentId}", "../fixtures/bugs/727/swagger.json")
   655  	require.NoError(t, err)
   656  
   657  	op, err := b.MakeOperation()
   658  	require.NoError(t, err)
   659  
   660  	buf := bytes.NewBuffer(nil)
   661  	opts := opts()
   662  	opts.defaultsEnsured = false
   663  	opts.IsClient = true
   664  	require.NoError(t, opts.EnsureDefaults())
   665  
   666  	require.NoError(t, opts.templates.MustGet("clientResponse").Execute(buf, op))
   667  }
   668  
   669  func TestGenClient_CustomFormatPath(t *testing.T) {
   670  	b, err := methodPathOpBuilder("get", "/mosaic/experimental/series/{SeriesId}/mosaics", "../fixtures/bugs/789/swagger.yml")
   671  	require.NoError(t, err)
   672  
   673  	op, err := b.MakeOperation()
   674  	require.NoError(t, err)
   675  
   676  	buf := bytes.NewBuffer(nil)
   677  	opts := opts()
   678  	opts.defaultsEnsured = false
   679  	opts.IsClient = true
   680  	require.NoError(t, opts.EnsureDefaults())
   681  
   682  	require.NoError(t, opts.templates.MustGet("clientParameter").Execute(buf, op))
   683  
   684  	assertInCode(t, `if err := r.SetPathParam("SeriesId", o.SeriesID.String()); err != nil`, buf.String())
   685  }
   686  
   687  func TestGenClient_Issue733(t *testing.T) {
   688  	b, err := opBuilder("get_characters_character_id_mail_mail_id", "../fixtures/bugs/733/swagger.json")
   689  	require.NoError(t, err)
   690  
   691  	op, err := b.MakeOperation()
   692  	require.NoError(t, err)
   693  
   694  	buf := bytes.NewBuffer(nil)
   695  	opts := opts()
   696  	opts.defaultsEnsured = false
   697  	opts.IsClient = true
   698  	require.NoError(t, opts.EnsureDefaults())
   699  
   700  	require.NoError(t, opts.templates.MustGet("clientResponse").Execute(buf, op))
   701  
   702  	assertInCode(t, "Labels []*int64 `json:\"labels\"`", buf.String())
   703  }
   704  
   705  func TestGenServerIssue890_ValidationTrueFlatteningTrue(t *testing.T) {
   706  	defer discardOutput()()
   707  
   708  	dr := testCwd(t)
   709  
   710  	opts := &GenOpts{
   711  		GenOptsCommon: GenOptsCommon{
   712  			Spec:              filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"),
   713  			IncludeModel:      true,
   714  			IncludeHandler:    true,
   715  			IncludeParameters: true,
   716  			IncludeResponses:  true,
   717  			IncludeMain:       true,
   718  			ValidateSpec:      true,
   719  			APIPackage:        "restapi",
   720  			ModelPackage:      "model",
   721  			ServerPackage:     "server",
   722  			ClientPackage:     "client",
   723  			Target:            dr,
   724  			IsClient:          true,
   725  		},
   726  	}
   727  
   728  	// Testing Server Generation
   729  	require.NoError(t, opts.EnsureDefaults())
   730  
   731  	// Full flattening
   732  	opts.FlattenOpts.Expand = false
   733  	opts.FlattenOpts.Minimal = false
   734  	appGen, err := newAppGenerator("JsonRefOperation", nil, nil, opts)
   735  	require.NoError(t, err)
   736  
   737  	op, err := appGen.makeCodegenApp()
   738  	require.NoError(t, err)
   739  
   740  	buf := bytes.NewBuffer(nil)
   741  	require.NoError(t, opts.templates.MustGet("serverOperation").Execute(buf, op.Operations[0]))
   742  
   743  	filecontent, err := appGen.GenOpts.LanguageOpts.FormatContent("operation.go", buf.Bytes())
   744  	require.NoErrorf(t, err, buf.String())
   745  
   746  	res := string(filecontent)
   747  	assertInCode(t, "GetHealthCheck", res)
   748  }
   749  
   750  func TestGenClientIssue890_ValidationTrueFlatteningTrue(t *testing.T) {
   751  	defer discardOutput()()
   752  
   753  	dr := testCwd(t)
   754  	defer func() {
   755  		_ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi"))
   756  	}()
   757  
   758  	opts := testGenOpts()
   759  	opts.Spec = "../fixtures/bugs/890/swagger.yaml"
   760  	opts.ValidateSpec = true
   761  	opts.FlattenOpts.Minimal = false
   762  
   763  	// Testing this is enough as there is only one operation which is specified as $ref.
   764  	// If this doesn't get resolved then there will be an error definitely.
   765  	require.NoError(t, GenerateClient("foo", nil, nil, opts))
   766  }
   767  
   768  func TestGenServerIssue890_ValidationFalseFlattenTrue(t *testing.T) {
   769  	defer discardOutput()()
   770  
   771  	dr := testCwd(t)
   772  
   773  	opts := &GenOpts{
   774  		GenOptsCommon: GenOptsCommon{
   775  			Spec:              filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"),
   776  			IncludeModel:      true,
   777  			IncludeHandler:    true,
   778  			IncludeParameters: true,
   779  			IncludeResponses:  true,
   780  			IncludeMain:       true,
   781  			APIPackage:        "restapi",
   782  			ModelPackage:      "model",
   783  			ServerPackage:     "server",
   784  			ClientPackage:     "client",
   785  			Target:            dr,
   786  			IsClient:          true,
   787  		},
   788  	}
   789  
   790  	// Testing Server Generation
   791  	require.NoError(t, opts.EnsureDefaults())
   792  
   793  	// full flattening
   794  	opts.FlattenOpts.Minimal = false
   795  	appGen, err := newAppGenerator("JsonRefOperation", nil, nil, opts)
   796  	require.NoError(t, err)
   797  
   798  	op, err := appGen.makeCodegenApp()
   799  	require.NoError(t, err)
   800  
   801  	buf := bytes.NewBuffer(nil)
   802  	err = opts.templates.MustGet("serverOperation").Execute(buf, op.Operations[0])
   803  	require.NoError(t, err)
   804  
   805  	filecontent, err := appGen.GenOpts.LanguageOpts.FormatContent("operation.go", buf.Bytes())
   806  	require.NoErrorf(t, err, buf.String())
   807  
   808  	res := string(filecontent)
   809  	assertInCode(t, "GetHealthCheck", res)
   810  }
   811  
   812  func TestGenClientIssue890_ValidationFalseFlatteningTrue(t *testing.T) {
   813  	defer discardOutput()()
   814  
   815  	dr := testCwd(t)
   816  	defer func() {
   817  		_ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi"))
   818  	}()
   819  
   820  	opts := testGenOpts()
   821  	opts.Spec = "../fixtures/bugs/890/swagger.yaml"
   822  	opts.ValidateSpec = false
   823  	// full flattening
   824  	opts.FlattenOpts.Minimal = false
   825  	// Testing this is enough as there is only one operation which is specified as $ref.
   826  	// If this doesn't get resolved then there will be an error definitely.
   827  	require.NoError(t, GenerateClient("foo", nil, nil, opts))
   828  }
   829  
   830  func TestGenServerIssue890_ValidationFalseFlattenFalse(t *testing.T) {
   831  	defer discardOutput()()
   832  
   833  	dr := testCwd(t)
   834  
   835  	opts := &GenOpts{
   836  		GenOptsCommon: GenOptsCommon{
   837  			Spec:              filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"),
   838  			IncludeModel:      true,
   839  			IncludeHandler:    true,
   840  			IncludeParameters: true,
   841  			IncludeResponses:  true,
   842  			IncludeMain:       true,
   843  			ValidateSpec:      false,
   844  			APIPackage:        "restapi",
   845  			ModelPackage:      "model",
   846  			ServerPackage:     "server",
   847  			ClientPackage:     "client",
   848  			Target:            dr,
   849  			IsClient:          true,
   850  		},
   851  	}
   852  
   853  	// Testing Server Generation
   854  	require.NoError(t, opts.EnsureDefaults())
   855  
   856  	// minimal flattening
   857  	opts.FlattenOpts.Minimal = true
   858  	_, err := newAppGenerator("JsonRefOperation", nil, nil, opts)
   859  	// if flatten is not set, expand takes over so this would resume normally
   860  	require.NoError(t, err)
   861  }
   862  
   863  func TestGenClientIssue890_ValidationFalseFlattenFalse(t *testing.T) {
   864  	defer discardOutput()()
   865  
   866  	dr := testCwd(t)
   867  	defer func() {
   868  		_ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi"))
   869  	}()
   870  
   871  	opts := testGenOpts()
   872  	opts.Spec = "../fixtures/bugs/890/swagger.yaml"
   873  	opts.ValidateSpec = false
   874  	// minimal flattening
   875  	opts.FlattenOpts.Minimal = true
   876  	// Testing this is enough as there is only one operation which is specified as $ref.
   877  	// If this doesn't get resolved then there will be an error definitely.
   878  	// New: Now if flatten is false, expand takes over so server generation should resume normally
   879  	require.NoError(t, GenerateClient("foo", nil, nil, opts))
   880  }
   881  
   882  func TestGenServerIssue890_ValidationTrueFlattenFalse(t *testing.T) {
   883  	defer discardOutput()()
   884  
   885  	dr := testCwd(t)
   886  
   887  	opts := &GenOpts{
   888  		GenOptsCommon: GenOptsCommon{
   889  			Spec:              filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"),
   890  			IncludeModel:      true,
   891  			IncludeHandler:    true,
   892  			IncludeParameters: true,
   893  			IncludeResponses:  true,
   894  			IncludeMain:       true,
   895  			ValidateSpec:      true,
   896  			APIPackage:        "restapi",
   897  			ModelPackage:      "model",
   898  			ServerPackage:     "server",
   899  			ClientPackage:     "client",
   900  			Target:            dr,
   901  			IsClient:          true,
   902  		},
   903  	}
   904  
   905  	// Testing Server Generation
   906  	require.NoError(t, opts.EnsureDefaults())
   907  
   908  	// minimal flattening
   909  	opts.FlattenOpts.Minimal = true
   910  
   911  	_, err := newAppGenerator("JsonRefOperation", nil, nil, opts)
   912  	// now if flatten is false, expand takes over so server generation should resume normally
   913  	require.NoError(t, err)
   914  }
   915  
   916  func TestGenServerWithTemplate(t *testing.T) {
   917  	defer discardOutput()()
   918  
   919  	dr := testCwd(t)
   920  
   921  	tests := []struct {
   922  		name      string
   923  		opts      *GenOpts
   924  		wantError bool
   925  	}{
   926  		{
   927  			name: "None_existing_contributor_template",
   928  			opts: &GenOpts{
   929  				GenOptsCommon: GenOptsCommon{
   930  					Spec:              filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"),
   931  					IncludeModel:      true,
   932  					IncludeHandler:    true,
   933  					IncludeParameters: true,
   934  					IncludeResponses:  true,
   935  					IncludeMain:       true,
   936  					ValidateSpec:      true,
   937  					APIPackage:        "restapi",
   938  					ModelPackage:      "model",
   939  					ServerPackage:     "server",
   940  					ClientPackage:     "client",
   941  					Target:            dr,
   942  					IsClient:          true,
   943  					Template:          "InvalidTemplate",
   944  				},
   945  			},
   946  			wantError: true,
   947  		},
   948  		{
   949  			name: "Existing_contributor",
   950  			opts: &GenOpts{
   951  				GenOptsCommon: GenOptsCommon{
   952  					Spec:              filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"),
   953  					IncludeModel:      true,
   954  					IncludeHandler:    true,
   955  					IncludeParameters: true,
   956  					IncludeResponses:  true,
   957  					IncludeMain:       true,
   958  					ValidateSpec:      true,
   959  					APIPackage:        "restapi",
   960  					ModelPackage:      "model",
   961  					ServerPackage:     "server",
   962  					ClientPackage:     "client",
   963  					Target:            dr,
   964  					IsClient:          true,
   965  					Template:          "stratoscale",
   966  				},
   967  			},
   968  			wantError: false,
   969  		},
   970  	}
   971  
   972  	t.Run("codegen operations", func(t *testing.T) {
   973  		for _, toPin := range tests {
   974  			tt := toPin
   975  			t.Run(tt.name, func(t *testing.T) {
   976  				t.Parallel()
   977  
   978  				// Testing Server Generation
   979  				require.NoError(t, tt.opts.EnsureDefaults())
   980  
   981  				// minimal flattening
   982  				tt.opts.FlattenOpts.Minimal = true
   983  				_, err := newAppGenerator("JsonRefOperation", nil, nil, tt.opts)
   984  				if tt.wantError {
   985  					require.Error(t, err)
   986  				} else {
   987  					require.NoError(t, err)
   988  				}
   989  			})
   990  		}
   991  	})
   992  }
   993  
   994  func TestGenClientIssue890_ValidationTrueFlattenFalse(t *testing.T) {
   995  	defer discardOutput()()
   996  
   997  	dr := testCwd(t)
   998  	defer func() {
   999  		_ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi"))
  1000  	}()
  1001  
  1002  	opts := testGenOpts()
  1003  	opts.Spec = filepath.FromSlash("../fixtures/bugs/890/swagger.yaml")
  1004  	opts.ValidateSpec = true
  1005  	// Testing this is enough as there is only one operation which is specified as $ref.
  1006  	// If this doesn't get resolved then there will be an error definitely.
  1007  	// same here: now if flatten doesn't resume, expand takes over
  1008  	require.NoError(t, GenerateClient("foo", nil, nil, opts))
  1009  }
  1010  
  1011  // This tests that securityDefinitions generate stable code
  1012  func TestBuilder_Issue1214(t *testing.T) {
  1013  	defer discardOutput()()
  1014  
  1015  	dr := testCwd(t)
  1016  	const matchAny = `(.|\n)+`
  1017  
  1018  	opts := &GenOpts{
  1019  		GenOptsCommon: GenOptsCommon{
  1020  			Spec:              filepath.FromSlash("../fixtures/bugs/1214/fixture-1214.yaml"),
  1021  			IncludeModel:      true,
  1022  			IncludeHandler:    true,
  1023  			IncludeParameters: true,
  1024  			IncludeResponses:  true,
  1025  			IncludeMain:       true,
  1026  			APIPackage:        "restapi",
  1027  			ModelPackage:      "model",
  1028  			ServerPackage:     "server",
  1029  			ClientPackage:     "client",
  1030  			Target:            dr,
  1031  			IsClient:          false,
  1032  		},
  1033  	}
  1034  	require.NoError(t, opts.EnsureDefaults())
  1035  
  1036  	appGen, e := newAppGenerator("fixture-1214", nil, nil, opts)
  1037  	require.NoError(t, e)
  1038  
  1039  	op, e := appGen.makeCodegenApp()
  1040  	require.NoError(t, e)
  1041  
  1042  	for i := 0; i < 5; i++ {
  1043  		buf := bytes.NewBuffer(nil)
  1044  		err := templates.MustGet("serverConfigureapi").Execute(buf, op)
  1045  		require.NoError(t, err)
  1046  
  1047  		ff, err := appGen.GenOpts.LanguageOpts.FormatContent("fixture_1214_configure_api.go", buf.Bytes())
  1048  		require.NoErrorf(t, err, buf.String())
  1049  
  1050  		res := string(ff)
  1051  		assertRegexpInCode(t, matchAny+
  1052  			`api\.AAuth = func\(user string, pass string\)`+matchAny+
  1053  			`api\.BAuth = func\(token string\)`+matchAny+
  1054  			`api\.CAuth = func\(token string\)`+matchAny+
  1055  			`api\.DAuth = func\(token string\)`+matchAny+
  1056  			`api\.EAuth = func\(token string, scopes \[\]string\)`+matchAny, res)
  1057  
  1058  		buf = bytes.NewBuffer(nil)
  1059  		require.NoError(t, opts.templates.MustGet("serverBuilder").Execute(buf, op))
  1060  
  1061  		ff, err = appGen.GenOpts.LanguageOpts.FormatContent("fixture_1214_server.go", buf.Bytes())
  1062  		require.NoErrorf(t, err, buf.String())
  1063  
  1064  		res = string(ff)
  1065  		assertRegexpInCode(t, matchAny+
  1066  			`AAuth: func\(user string, pass string\) \(interface{}, error\) {`+matchAny+
  1067  			`BAuth: func\(token string\) \(interface{}, error\) {`+matchAny+
  1068  			`CAuth: func\(token string\) \(interface{}, error\) {`+matchAny+
  1069  			`DAuth: func\(token string\) \(interface{}, error\) {`+matchAny+
  1070  			`EAuth: func\(token string, scopes \[\]string\) \(interface{}, error\) {`+matchAny+
  1071  
  1072  			`AAuth func\(string, string\) \(interface{}, error\)`+matchAny+
  1073  			`BAuth func\(string\) \(interface{}, error\)`+matchAny+
  1074  			`CAuth func\(string\) \(interface{}, error\)`+matchAny+
  1075  			`DAuth func\(string\) \(interface{}, error\)`+matchAny+
  1076  			`EAuth func\(string, \[\]string\) \(interface{}, error\)`+matchAny+
  1077  
  1078  			`if o\.AAuth == nil {`+matchAny+
  1079  			`unregistered = append\(unregistered, "AAuth"\)`+matchAny+
  1080  			`if o\.BAuth == nil {`+matchAny+
  1081  			`unregistered = append\(unregistered, "K1Auth"\)`+matchAny+
  1082  			`if o\.CAuth == nil {`+matchAny+
  1083  			`unregistered = append\(unregistered, "K2Auth"\)`+matchAny+
  1084  			`if o\.DAuth == nil {`+matchAny+
  1085  			`unregistered = append\(unregistered, "K3Auth"\)`+matchAny+
  1086  			`if o\.EAuth == nil {`+matchAny+
  1087  			`unregistered = append\(unregistered, "EAuth"\)`+matchAny+
  1088  
  1089  			`case "A":`+matchAny+
  1090  			`case "B":`+matchAny+
  1091  			`case "C":`+matchAny+
  1092  			`case "D":`+matchAny+
  1093  			`case "E":`+matchAny, res)
  1094  	}
  1095  }
  1096  
  1097  func TestGenSecurityRequirements(t *testing.T) {
  1098  	for i := 0; i < 5; i++ {
  1099  		operation := "asecOp"
  1100  		b, err := opBuilder(operation, "../fixtures/bugs/1214/fixture-1214.yaml")
  1101  		require.NoError(t, err)
  1102  
  1103  		b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation)
  1104  		genRequirements := b.makeSecurityRequirements("o")
  1105  		assert.Len(t, genRequirements, 2)
  1106  		assert.Equal(t, []GenSecurityRequirements{
  1107  			{
  1108  				GenSecurityRequirement{
  1109  					Name:   "A",
  1110  					Scopes: []string{},
  1111  				},
  1112  				GenSecurityRequirement{
  1113  					Name:   "B",
  1114  					Scopes: []string{},
  1115  				},
  1116  				GenSecurityRequirement{
  1117  					Name:   "E",
  1118  					Scopes: []string{"s0", "s1", "s2", "s3", "s4"},
  1119  				},
  1120  			},
  1121  			{
  1122  				GenSecurityRequirement{
  1123  					Name:   "C",
  1124  					Scopes: []string{},
  1125  				},
  1126  				GenSecurityRequirement{
  1127  					Name:   "D",
  1128  					Scopes: []string{},
  1129  				},
  1130  				GenSecurityRequirement{
  1131  					Name:   "E",
  1132  					Scopes: []string{"s5", "s6", "s7", "s8", "s9"},
  1133  				},
  1134  			},
  1135  		}, genRequirements)
  1136  
  1137  		operation = "bsecOp"
  1138  		b, err = opBuilder(operation, "../fixtures/bugs/1214/fixture-1214.yaml")
  1139  		require.NoError(t, err)
  1140  
  1141  		b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation)
  1142  		genRequirements = b.makeSecurityRequirements("o")
  1143  		assert.Len(t, genRequirements, 2)
  1144  		assert.Equal(t, []GenSecurityRequirements{
  1145  			{
  1146  				GenSecurityRequirement{
  1147  					Name:   "A",
  1148  					Scopes: []string{},
  1149  				},
  1150  				GenSecurityRequirement{
  1151  					Name:   "E",
  1152  					Scopes: []string{"s0", "s1", "s2", "s3", "s4"},
  1153  				},
  1154  			},
  1155  			{
  1156  				GenSecurityRequirement{
  1157  					Name:   "D",
  1158  					Scopes: []string{},
  1159  				},
  1160  				GenSecurityRequirement{
  1161  					Name:   "E",
  1162  					Scopes: []string{"s5", "s6", "s7", "s8", "s9"},
  1163  				},
  1164  			},
  1165  		}, genRequirements)
  1166  	}
  1167  
  1168  	operation := "csecOp"
  1169  	b, err := opBuilder(operation, "../fixtures/bugs/1214/fixture-1214.yaml")
  1170  	require.NoError(t, err)
  1171  
  1172  	b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation)
  1173  	genRequirements := b.makeSecurityRequirements("o")
  1174  	assert.NotNil(t, genRequirements)
  1175  	assert.Empty(t, genRequirements)
  1176  
  1177  	operation = "nosecOp"
  1178  	b, err = opBuilder(operation, "../fixtures/bugs/1214/fixture-1214-2.yaml")
  1179  	require.NoError(t, err)
  1180  
  1181  	b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation)
  1182  	genRequirements = b.makeSecurityRequirements("o")
  1183  	assert.Nil(t, genRequirements)
  1184  }
  1185  
  1186  func TestGenerateServerOperation(t *testing.T) {
  1187  	defer discardOutput()()
  1188  
  1189  	fname := "../fixtures/codegen/todolist.simple.yml"
  1190  
  1191  	tgt, _ := os.MkdirTemp(filepath.Dir(fname), "generated")
  1192  	defer func() {
  1193  		_ = os.RemoveAll(tgt)
  1194  	}()
  1195  	o := &GenOpts{
  1196  		GenOptsCommon: GenOptsCommon{
  1197  			ValidateSpec:      false,
  1198  			IncludeModel:      true,
  1199  			IncludeHandler:    true,
  1200  			IncludeParameters: true,
  1201  			IncludeResponses:  true,
  1202  			ModelPackage:      "models",
  1203  			Spec:              fname,
  1204  			Target:            tgt,
  1205  		},
  1206  	}
  1207  	require.NoError(t, o.EnsureDefaults())
  1208  
  1209  	require.Error(t, GenerateServerOperation([]string{"createTask"}, nil))
  1210  
  1211  	d := o.TemplateDir
  1212  	o.TemplateDir = "./nowhere"
  1213  	require.Error(t, GenerateServerOperation([]string{"notFound"}, o))
  1214  
  1215  	o.TemplateDir = d
  1216  	d = o.Spec
  1217  	o.Spec = "nowhere.yaml"
  1218  	require.Error(t, GenerateServerOperation([]string{"notFound"}, o))
  1219  
  1220  	o.Spec = d
  1221  	require.Error(t, GenerateServerOperation([]string{"notFound"}, o))
  1222  
  1223  	require.NoError(t, GenerateServerOperation([]string{"createTask"}, o))
  1224  
  1225  	// check expected files are generated and that's it
  1226  	_, err := os.Stat(filepath.Join(tgt, "tasks", "create_task.go"))
  1227  	require.NoError(t, err)
  1228  	_, err = os.Stat(filepath.Join(tgt, "tasks", "create_task_parameters.go"))
  1229  	require.NoError(t, err)
  1230  	_, err = os.Stat(filepath.Join(tgt, "tasks", "create_task_responses.go"))
  1231  	require.NoError(t, err)
  1232  
  1233  	origStdout := os.Stdout
  1234  	defer func() {
  1235  		os.Stdout = origStdout
  1236  	}()
  1237  	os.Stdout, _ = os.Create(filepath.Join(tgt, "stdout"))
  1238  	o.DumpData = true
  1239  	// just checks this does not fail
  1240  	err = GenerateServerOperation([]string{"createTask"}, o)
  1241  	require.NoError(t, err)
  1242  	_, err = os.Stat(filepath.Join(tgt, "stdout"))
  1243  	require.NoError(t, err)
  1244  }
  1245  
  1246  // This tests that mimetypes generate stable code
  1247  func TestBuilder_Issue1646(t *testing.T) {
  1248  	defer discardOutput()()
  1249  
  1250  	dr := testCwd(t)
  1251  
  1252  	opts := &GenOpts{
  1253  		GenOptsCommon: GenOptsCommon{
  1254  			Spec:              filepath.FromSlash("../fixtures/bugs/1646/fixture-1646.yaml"),
  1255  			IncludeModel:      true,
  1256  			IncludeHandler:    true,
  1257  			IncludeParameters: true,
  1258  			IncludeResponses:  true,
  1259  			IncludeMain:       true,
  1260  			APIPackage:        "restapi",
  1261  			ModelPackage:      "model",
  1262  			ServerPackage:     "server",
  1263  			ClientPackage:     "client",
  1264  			Target:            dr,
  1265  			IsClient:          false,
  1266  		},
  1267  	}
  1268  	err := opts.EnsureDefaults()
  1269  	require.NoError(t, err)
  1270  	appGen, err := newAppGenerator("fixture-1646", nil, nil, opts)
  1271  	require.NoError(t, err)
  1272  
  1273  	preCons, preConj := appGen.makeConsumes()
  1274  	preProds, preProdj := appGen.makeProduces()
  1275  	assert.True(t, preConj)
  1276  	assert.True(t, preProdj)
  1277  	for i := 0; i < 5; i++ {
  1278  		cons, conj := appGen.makeConsumes()
  1279  		prods, prodj := appGen.makeProduces()
  1280  		assert.True(t, conj)
  1281  		assert.True(t, prodj)
  1282  		assert.Equal(t, preConj, conj)
  1283  		assert.Equal(t, preProdj, prodj)
  1284  		assert.Equal(t, preCons, cons)
  1285  		assert.Equal(t, preProds, prods)
  1286  	}
  1287  }
  1288  
  1289  func TestGenServer_StrictAdditionalProperties(t *testing.T) {
  1290  	defer discardOutput()()
  1291  
  1292  	dr := testCwd(t)
  1293  
  1294  	opts := &GenOpts{
  1295  		GenOptsCommon: GenOptsCommon{
  1296  			Spec:              filepath.FromSlash("../fixtures/codegen/strict-additional-properties.yml"),
  1297  			IncludeModel:      true,
  1298  			IncludeHandler:    true,
  1299  			IncludeParameters: true,
  1300  			IncludeResponses:  true,
  1301  			IncludeMain:       true,
  1302  			APIPackage:        "restapi",
  1303  			ModelPackage:      "model",
  1304  			ServerPackage:     "server",
  1305  			ClientPackage:     "client",
  1306  			Target:            dr,
  1307  			IsClient:          false,
  1308  		},
  1309  	}
  1310  	err := opts.EnsureDefaults()
  1311  	require.NoError(t, err)
  1312  
  1313  	opts.StrictAdditionalProperties = true
  1314  
  1315  	appGen, err := newAppGenerator("StrictAdditionalProperties", nil, nil, opts)
  1316  	require.NoError(t, err)
  1317  
  1318  	op, err := appGen.makeCodegenApp()
  1319  	require.NoError(t, err)
  1320  
  1321  	buf := bytes.NewBuffer(nil)
  1322  	err = templates.MustGet("serverOperation").Execute(buf, op.Operations[0])
  1323  	require.NoError(t, err)
  1324  
  1325  	ff, err := appGen.GenOpts.LanguageOpts.FormatContent("strictAdditionalProperties.go", buf.Bytes())
  1326  	require.NoErrorf(t, err, buf.String())
  1327  
  1328  	res := string(ff)
  1329  	for _, tt := range []struct {
  1330  		name      string
  1331  		assertion func(testing.TB, string, string) bool
  1332  	}{
  1333  		{"PostTestBody", assertInCode},
  1334  		{"PostTestParamsBodyExplicit", assertInCode},
  1335  		{"PostTestParamsBodyImplicit", assertInCode},
  1336  		{"PostTestParamsBodyDisabled", assertNotInCode},
  1337  	} {
  1338  		fn := funcBody(res, "*"+tt.name+") UnmarshalJSON(data []byte) error")
  1339  		require.NotEmpty(t, fn, "Method UnmarshalJSON should be defined for type *"+tt.name)
  1340  		tt.assertion(t, "dec.DisallowUnknownFields()", fn)
  1341  	}
  1342  }
  1343  
  1344  func makeClientTimeoutNameTest() []struct {
  1345  	seenIDs  map[string]interface{}
  1346  	name     string
  1347  	expected string
  1348  } {
  1349  	return []struct {
  1350  		seenIDs  map[string]interface{}
  1351  		name     string
  1352  		expected string
  1353  	}{
  1354  		{
  1355  			seenIDs:  nil,
  1356  			name:     "witness",
  1357  			expected: "witness",
  1358  		},
  1359  		{
  1360  			seenIDs: map[string]interface{}{
  1361  				"id": true,
  1362  			},
  1363  			name:     "timeout",
  1364  			expected: "timeout",
  1365  		},
  1366  		{
  1367  			seenIDs: map[string]interface{}{
  1368  				"timeout":        true,
  1369  				"requesttimeout": true,
  1370  			},
  1371  			name:     "timeout",
  1372  			expected: "httpRequestTimeout",
  1373  		},
  1374  		{
  1375  			seenIDs: map[string]interface{}{
  1376  				"timeout":            true,
  1377  				"requesttimeout":     true,
  1378  				"httprequesttimeout": true,
  1379  				"swaggertimeout":     true,
  1380  				"operationtimeout":   true,
  1381  				"optimeout":          true,
  1382  			},
  1383  			name:     "timeout",
  1384  			expected: "operTimeout",
  1385  		},
  1386  		{
  1387  			seenIDs: map[string]interface{}{
  1388  				"timeout":            true,
  1389  				"requesttimeout":     true,
  1390  				"httprequesttimeout": true,
  1391  				"swaggertimeout":     true,
  1392  				"operationtimeout":   true,
  1393  				"optimeout":          true,
  1394  				"opertimeout":        true,
  1395  				"opertimeout1":       true,
  1396  			},
  1397  			name:     "timeout",
  1398  			expected: "operTimeout11",
  1399  		},
  1400  	}
  1401  }
  1402  
  1403  func TestRenameTimeout(t *testing.T) {
  1404  	for idx, toPin := range makeClientTimeoutNameTest() {
  1405  		i := idx
  1406  		testCase := toPin
  1407  		t.Run(testCase.name, func(t *testing.T) {
  1408  			t.Parallel()
  1409  			assert.Equalf(t, testCase.expected, renameTimeout(testCase.seenIDs, testCase.name), "unexpected deconflicting value [%d]", i)
  1410  		})
  1411  	}
  1412  }
  1413  
  1414  func testInvalidParams() map[string]spec.Parameter {
  1415  	return map[string]spec.Parameter{
  1416  		"query#param1": *spec.QueryParam("param1"),
  1417  		"path#param1":  *spec.PathParam("param1"),
  1418  		"body#param1":  *spec.BodyParam("param1", &spec.Schema{}),
  1419  	}
  1420  }
  1421  
  1422  func TestParamMappings(t *testing.T) {
  1423  	// Test deconfliction of duplicate param names across param locations
  1424  	mappings, _ := paramMappings(testInvalidParams())
  1425  	require.Contains(t, mappings, "query")
  1426  	require.Contains(t, mappings, "path")
  1427  	require.Contains(t, mappings, "body")
  1428  	q := mappings["query"]
  1429  	p := mappings["path"]
  1430  	b := mappings["body"]
  1431  	require.Len(t, q, 1)
  1432  	require.Len(t, p, 1)
  1433  	require.Len(t, b, 1)
  1434  	require.Containsf(t, q, "param1", "unexpected content of %#v", q)
  1435  	require.Containsf(t, p, "param1", "unexpected content of %#v", p)
  1436  	require.Containsf(t, b, "param1", "unexpected content of %#v", b)
  1437  	assert.Equalf(t, "QueryParam1", q["param1"], "unexpected content of %#v", q["param1"])
  1438  	assert.Equalf(t, "PathParam1", p["param1"], "unexpected content of %#v", p["param1"])
  1439  	assert.Equalf(t, "BodyParam1", b["param1"], "unexpected content of %#v", b["param1"])
  1440  }
  1441  
  1442  func TestDeconflictTag(t *testing.T) {
  1443  	assert.Equal(t, "runtimeops", deconflictTag(nil, "runtime"))
  1444  	assert.Equal(t, "apiops", deconflictTag([]string{"tag1"}, "api"))
  1445  	assert.Equal(t, "apiops1", deconflictTag([]string{"tag1", "apiops"}, "api"))
  1446  	assert.Equal(t, "tlsops", deconflictTag([]string{"tag1"}, "tls"))
  1447  	assert.Equal(t, "mytag", deconflictTag([]string{"tag1", "apiops"}, "mytag"))
  1448  
  1449  	assert.Equal(t, "operationsops", renameOperationPackage([]string{"tag1"}, "operations"))
  1450  	assert.Equal(t, "operationsops11", renameOperationPackage([]string{"tag1", "operationsops1", "operationsops"}, "operations"))
  1451  }
  1452  
  1453  func TestGenServer_2161_panic(t *testing.T) {
  1454  	t.Parallel()
  1455  	defer discardOutput()()
  1456  
  1457  	generated, err := os.MkdirTemp(testCwd(t), "generated_2161")
  1458  	require.NoError(t, err)
  1459  
  1460  	defer func() {
  1461  		_ = os.RemoveAll(generated)
  1462  	}()
  1463  
  1464  	opts := &GenOpts{
  1465  		GenOptsCommon: GenOptsCommon{
  1466  			Spec:                       filepath.FromSlash("../fixtures/bugs/2161/fixture-2161-panic.json"),
  1467  			IncludeModel:               true,
  1468  			IncludeHandler:             true,
  1469  			IncludeParameters:          true,
  1470  			IncludeResponses:           true,
  1471  			IncludeMain:                true,
  1472  			APIPackage:                 "restapi",
  1473  			ModelPackage:               "model",
  1474  			ServerPackage:              "server",
  1475  			ClientPackage:              "client",
  1476  			Target:                     generated,
  1477  			IsClient:                   false,
  1478  			StrictAdditionalProperties: true,
  1479  		},
  1480  	}
  1481  	require.NoError(t, opts.EnsureDefaults())
  1482  
  1483  	appGen, err := newAppGenerator("inlinedSubtype", nil, nil, opts)
  1484  	require.NoError(t, err)
  1485  
  1486  	op, err := appGen.makeCodegenApp()
  1487  	require.NoError(t, err)
  1488  
  1489  	buf := bytes.NewBuffer(nil)
  1490  	var selectedOp int
  1491  	for i := range op.Operations {
  1492  		if op.Operations[i].Name == "configuration_update_configuration_module" {
  1493  			selectedOp = i
  1494  		}
  1495  	}
  1496  	require.NotEmpty(t, selectedOp, "dev error: invalid test vs fixture")
  1497  
  1498  	require.NoError(t, opts.templates.MustGet("serverOperation").Execute(buf, op.Operations[selectedOp]))
  1499  
  1500  	_, err = appGen.GenOpts.LanguageOpts.FormatContent(op.Operations[selectedOp].Name+".go", buf.Bytes())
  1501  	require.NoErrorf(t, err, buf.String())
  1502  	// NOTE(fred): I know that the generated model is wrong from this spec at the moment.
  1503  	// The test with this fix simply asserts that there is no panic / internal error with building this.
  1504  }
  1505  
  1506  func TestGenServer_1659_Principal(t *testing.T) {
  1507  	defer discardOutput()()
  1508  
  1509  	dr := testCwd(t)
  1510  
  1511  	for _, toPin := range []struct {
  1512  		Title       string
  1513  		Opts        *GenOpts
  1514  		Expected    map[string][]string
  1515  		NotExpected map[string][]string
  1516  	}{
  1517  		{
  1518  			Title: "default",
  1519  			Opts: &GenOpts{
  1520  				GenOptsCommon: GenOptsCommon{
  1521  					Spec:              filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"),
  1522  					IncludeHandler:    true,
  1523  					IncludeParameters: true,
  1524  					IncludeResponses:  true,
  1525  					IncludeMain:       false,
  1526  					APIPackage:        "restapi",
  1527  					ModelPackage:      "models",
  1528  					ServerPackage:     "server",
  1529  					ClientPackage:     "client",
  1530  					Target:            dr,
  1531  					IsClient:          false,
  1532  				},
  1533  			},
  1534  			Expected: map[string][]string{
  1535  				"configure": {
  1536  					`if api.ApikeyAuth == nil {`,
  1537  					`api.ApikeyAuth = func(token string) (interface{}, error) {`,
  1538  					`if api.BasicAuth == nil {`,
  1539  					`api.BasicAuth = func(user string, pass string) (interface{}, error) {`,
  1540  					`if api.PetstoreAuthAuth == nil {`,
  1541  					`api.PetstoreAuthAuth = func(token string, scopes []string) (interface{}, error) {`,
  1542  					`api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal interface{}) middleware.Responder {`,
  1543  					`api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`,
  1544  				},
  1545  				"get": {
  1546  					`type GetHandlerFunc func(GetParams, interface{})  middleware.Responder`,
  1547  					`func (fn GetHandlerFunc) Handle(params GetParams, principal interface{})  middleware.Responder  {`,
  1548  					`return fn(params, principal)`,
  1549  					`type GetHandler interface {`,
  1550  					`Handle(GetParams, interface{})  middleware.Responder`,
  1551  					`uprinc, aCtx, err := o.Context.Authorize(r, route)`,
  1552  					`if uprinc != nil {`,
  1553  					`principal = uprinc.(interface{})`,
  1554  					`res := o.Handler.Handle(Params, principal)`,
  1555  				},
  1556  				"post": {
  1557  					`type PostHandlerFunc func(PostParams)  middleware.Responder`,
  1558  					`return fn(params)`,
  1559  					`type PostHandler interface {`,
  1560  					`Handle(PostParams)  middleware.Responder`,
  1561  					`res := o.Handler.Handle(Params)`,
  1562  				},
  1563  			},
  1564  			NotExpected: map[string][]string{
  1565  				"post": {
  1566  					`uprinc, aCtx, err := o.Context.Authorize(r, route)`,
  1567  					`principal = uprinc.(interface{})`,
  1568  				},
  1569  			},
  1570  		},
  1571  		{
  1572  			Title: "principal is struct",
  1573  			Opts: &GenOpts{
  1574  				GenOptsCommon: GenOptsCommon{
  1575  					Spec:              filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"),
  1576  					IncludeHandler:    true,
  1577  					IncludeParameters: true,
  1578  					IncludeResponses:  true,
  1579  					IncludeMain:       false,
  1580  					APIPackage:        "restapi",
  1581  					ModelPackage:      "models",
  1582  					ServerPackage:     "server",
  1583  					ClientPackage:     "client",
  1584  					Target:            dr,
  1585  					Principal:         "github.com/example/security.Principal",
  1586  					IsClient:          false,
  1587  				},
  1588  			},
  1589  			Expected: map[string][]string{
  1590  				"configure": {
  1591  					`auth "github.com/example/security"`,
  1592  					`if api.ApikeyAuth == nil {`,
  1593  					`api.ApikeyAuth = func(token string) (*auth.Principal, error) {`,
  1594  					`if api.BasicAuth == nil {`,
  1595  					`api.BasicAuth = func(user string, pass string) (*auth.Principal, error) {`,
  1596  					`if api.PetstoreAuthAuth == nil {`,
  1597  					`api.PetstoreAuthAuth = func(token string, scopes []string) (*auth.Principal, error) {`,
  1598  					`api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal *auth.Principal) middleware.Responder {`,
  1599  					`api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`,
  1600  				},
  1601  				"get": {
  1602  					`type GetHandlerFunc func(GetParams, *auth.Principal)  middleware.Responder`,
  1603  					`func (fn GetHandlerFunc) Handle(params GetParams, principal *auth.Principal)  middleware.Responder  {`,
  1604  					`return fn(params, principal)`,
  1605  					`type GetHandler interface {`,
  1606  					`Handle(GetParams, *auth.Principal)  middleware.Responder`,
  1607  					`uprinc, aCtx, err := o.Context.Authorize(r, route)`,
  1608  					`if uprinc != nil {`,
  1609  					`principal = uprinc.(*auth.Principal)`,
  1610  					`res := o.Handler.Handle(Params, principal)`,
  1611  				},
  1612  				"post": {
  1613  					`type PostHandlerFunc func(PostParams)  middleware.Responder`,
  1614  					`return fn(params)`,
  1615  					`type PostHandler interface {`,
  1616  					`Handle(PostParams)  middleware.Responder`,
  1617  					`res := o.Handler.Handle(Params)`,
  1618  				},
  1619  			},
  1620  			NotExpected: map[string][]string{
  1621  				"post": {
  1622  					`uprinc, aCtx, err := o.Context.Authorize(r, route)`,
  1623  					`principal = uprinc.(`,
  1624  				},
  1625  			},
  1626  		},
  1627  		{
  1628  			Title: "principal is interface",
  1629  			Opts: &GenOpts{
  1630  				GenOptsCommon: GenOptsCommon{
  1631  					Spec:                 filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"),
  1632  					IncludeHandler:       true,
  1633  					IncludeParameters:    true,
  1634  					IncludeResponses:     true,
  1635  					IncludeMain:          false,
  1636  					APIPackage:           "restapi",
  1637  					ModelPackage:         "models",
  1638  					ServerPackage:        "server",
  1639  					ClientPackage:        "client",
  1640  					Target:               dr,
  1641  					Principal:            "github.com/example/security.PrincipalIface",
  1642  					IsClient:             false,
  1643  					PrincipalCustomIface: true,
  1644  				},
  1645  			},
  1646  			Expected: map[string][]string{
  1647  				"configure": {
  1648  					`auth "github.com/example/security"`,
  1649  					`if api.ApikeyAuth == nil {`,
  1650  					`api.ApikeyAuth = func(token string) (auth.PrincipalIface, error) {`,
  1651  					`if api.BasicAuth == nil {`,
  1652  					`api.BasicAuth = func(user string, pass string) (auth.PrincipalIface, error) {`,
  1653  					`if api.PetstoreAuthAuth == nil {`,
  1654  					`api.PetstoreAuthAuth = func(token string, scopes []string) (auth.PrincipalIface, error) {`,
  1655  					`api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal auth.PrincipalIface) middleware.Responder {`,
  1656  					`api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`,
  1657  				},
  1658  				"get": {
  1659  					`type GetHandlerFunc func(GetParams, auth.PrincipalIface)  middleware.Responder`,
  1660  					`func (fn GetHandlerFunc) Handle(params GetParams, principal auth.PrincipalIface)  middleware.Responder  {`,
  1661  					`return fn(params, principal)`,
  1662  					`type GetHandler interface {`,
  1663  					`Handle(GetParams, auth.PrincipalIface)  middleware.Responder`,
  1664  					`uprinc, aCtx, err := o.Context.Authorize(r, route)`,
  1665  					`if uprinc != nil {`,
  1666  					`principal = uprinc.(auth.PrincipalIface)`,
  1667  					`res := o.Handler.Handle(Params, principal)`,
  1668  				},
  1669  				"post": {
  1670  					`type PostHandlerFunc func(PostParams)  middleware.Responder`,
  1671  					`return fn(params)`,
  1672  					`type PostHandler interface {`,
  1673  					`Handle(PostParams)  middleware.Responder`,
  1674  					`res := o.Handler.Handle(Params)`,
  1675  				},
  1676  			},
  1677  			NotExpected: map[string][]string{
  1678  				"post": {
  1679  					`uprinc, aCtx, err := o.Context.Authorize(r, route)`,
  1680  					`principal = uprinc.(`,
  1681  				},
  1682  			},
  1683  		},
  1684  		{
  1685  			Title: "stratoscale: principal is struct",
  1686  			Opts: &GenOpts{
  1687  				GenOptsCommon: GenOptsCommon{
  1688  					Spec:              filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"),
  1689  					IncludeHandler:    true,
  1690  					IncludeParameters: true,
  1691  					IncludeResponses:  true,
  1692  					IncludeMain:       false,
  1693  					APIPackage:        "restapi",
  1694  					ModelPackage:      "models",
  1695  					ServerPackage:     "server",
  1696  					ClientPackage:     "client",
  1697  					Target:            dr,
  1698  					Principal:         "github.com/example/security.Principal",
  1699  					IsClient:          false,
  1700  					Template:          "stratoscale",
  1701  				},
  1702  			},
  1703  			Expected: map[string][]string{
  1704  				"configure": {
  1705  					`auth "github.com/example/security"`,
  1706  					`AuthApikey func(token string) (*auth.Principal, error)`,
  1707  					`AuthBasic func(user string, pass string) (*auth.Principal, error)`,
  1708  					`AuthPetstoreAuth func(token string, scopes []string) (*auth.Principal, error)`,
  1709  					`api.ApikeyAuth = func(token string) (*auth.Principal, error) {`,
  1710  					`if c.AuthApikey == nil {`,
  1711  					`panic("you specified a custom principal type, but did not provide the authenticator to provide this")`,
  1712  					`return c.AuthApikey(token)`,
  1713  					`api.BasicAuth = func(user string, pass string) (*auth.Principal, error) {`,
  1714  					`if c.AuthBasic == nil {`,
  1715  					`panic("you specified a custom principal type, but did not provide the authenticator to provide this")`,
  1716  					`return c.AuthBasic(user, pass)`,
  1717  					`api.PetstoreAuthAuth = func(token string, scopes []string) (*auth.Principal, error) {`,
  1718  					`if c.AuthPetstoreAuth == nil {`,
  1719  					`panic("you specified a custom principal type, but did not provide the authenticator to provide this")`,
  1720  					`return c.AuthPetstoreAuth(token, scopes)`,
  1721  					`api.APIAuthorizer = authorizer(c.Authorizer)`,
  1722  					`api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal *auth.Principal) middleware.Responder {`,
  1723  					`ctx = storeAuth(ctx, principal)`,
  1724  					`return c.RestapiAPI.Get(ctx, params)`,
  1725  					`api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`,
  1726  					`return c.RestapiAPI.Post(ctx, params)`,
  1727  					`func (a authorizer) Authorize(req *http.Request, principal interface{}) error {`,
  1728  					`ctx := storeAuth(req.Context(), principal)`,
  1729  					`func storeAuth(ctx context.Context, principal interface{})`,
  1730  				},
  1731  			},
  1732  		},
  1733  		{
  1734  			Title: "stratoscale: principal is interface",
  1735  			Opts: &GenOpts{
  1736  				GenOptsCommon: GenOptsCommon{
  1737  					Spec:                 filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"),
  1738  					IncludeHandler:       true,
  1739  					IncludeParameters:    true,
  1740  					IncludeResponses:     true,
  1741  					IncludeMain:          false,
  1742  					APIPackage:           "restapi",
  1743  					ModelPackage:         "models",
  1744  					ServerPackage:        "server",
  1745  					ClientPackage:        "client",
  1746  					Target:               dr,
  1747  					Principal:            "github.com/example/security.PrincipalIface",
  1748  					IsClient:             false,
  1749  					PrincipalCustomIface: true,
  1750  					Template:             "stratoscale",
  1751  				},
  1752  			},
  1753  			Expected: map[string][]string{
  1754  				"configure": {
  1755  					`auth "github.com/example/security"`,
  1756  					`AuthApikey func(token string) (auth.PrincipalIface, error)`,
  1757  					`AuthBasic func(user string, pass string) (auth.PrincipalIface, error)`,
  1758  					`AuthPetstoreAuth func(token string, scopes []string) (auth.PrincipalIface, error)`,
  1759  					`api.ApikeyAuth = func(token string) (auth.PrincipalIface, error) {`,
  1760  					`if c.AuthApikey == nil {`,
  1761  					`panic("you specified a custom principal type, but did not provide the authenticator to provide this")`,
  1762  					`return c.AuthApikey(token)`,
  1763  					`api.BasicAuth = func(user string, pass string) (auth.PrincipalIface, error) {`,
  1764  					`if c.AuthBasic == nil {`,
  1765  					`panic("you specified a custom principal type, but did not provide the authenticator to provide this")`,
  1766  					`return c.AuthBasic(user, pass)`,
  1767  					`api.PetstoreAuthAuth = func(token string, scopes []string) (auth.PrincipalIface, error) {`,
  1768  					`if c.AuthPetstoreAuth == nil {`,
  1769  					`panic("you specified a custom principal type, but did not provide the authenticator to provide this")`,
  1770  					`return c.AuthPetstoreAuth(token, scopes)`,
  1771  					`api.APIAuthorizer = authorizer(c.Authorizer)`,
  1772  					`api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal auth.PrincipalIface) middleware.Responder {`,
  1773  					`ctx = storeAuth(ctx, principal)`,
  1774  					`return c.RestapiAPI.Get(ctx, params)`,
  1775  					`api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`,
  1776  					`return c.RestapiAPI.Post(ctx, params)`,
  1777  					`func (a authorizer) Authorize(req *http.Request, principal interface{}) error {`,
  1778  					`ctx := storeAuth(req.Context(), principal)`,
  1779  					`func storeAuth(ctx context.Context, principal interface{})`,
  1780  				},
  1781  			},
  1782  		},
  1783  	} {
  1784  		fixture := toPin
  1785  		t.Run(fixture.Title, func(t *testing.T) {
  1786  			t.Parallel()
  1787  
  1788  			opts := fixture.Opts
  1789  			require.NoError(t, opts.EnsureDefaults())
  1790  			require.NoError(t, opts.setTemplates())
  1791  
  1792  			appGen, err := newAppGenerator(fixture.Title, nil, nil, opts)
  1793  			require.NoError(t, err)
  1794  
  1795  			op, err := appGen.makeCodegenApp()
  1796  			require.NoError(t, err)
  1797  
  1798  			bufC := bytes.NewBuffer(nil)
  1799  			require.NoError(t, opts.templates.MustGet("serverConfigureapi").Execute(bufC, op))
  1800  
  1801  			_, err = appGen.GenOpts.LanguageOpts.FormatContent("configure_api.go", bufC.Bytes())
  1802  			require.NoErrorf(t, err, bufC.String())
  1803  
  1804  			for _, line := range fixture.Expected["configure"] {
  1805  				assertInCode(t, line, bufC.String())
  1806  			}
  1807  			for _, line := range fixture.NotExpected["configure"] {
  1808  				assertNotInCode(t, line, bufC.String())
  1809  			}
  1810  
  1811  			for i := range op.Operations {
  1812  				bufO := bytes.NewBuffer(nil)
  1813  				require.NoError(t, opts.templates.MustGet("serverOperation").Execute(bufO, op.Operations[i]))
  1814  
  1815  				_, erf := appGen.GenOpts.LanguageOpts.FormatContent(op.Operations[i].Name+".go", bufO.Bytes())
  1816  				require.NoErrorf(t, erf, bufO.String())
  1817  
  1818  				for _, line := range fixture.Expected[op.Operations[i].Name] {
  1819  					assertInCode(t, line, bufO.String())
  1820  				}
  1821  				for _, line := range fixture.NotExpected[op.Operations[i].Name] {
  1822  					assertNotInCode(t, line, bufO.String())
  1823  				}
  1824  			}
  1825  		})
  1826  	}
  1827  }