github.com/go-swagger/go-swagger@v0.31.0/generator/server_test.go (about)

     1  package generator
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"os"
     7  	"path/filepath"
     8  	"regexp"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/go-openapi/analysis"
    13  	"github.com/go-openapi/loads"
    14  	"github.com/go-openapi/runtime"
    15  	"github.com/go-openapi/swag"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  const invalidSpecExample = "../fixtures/bugs/825/swagger.yml"
    21  
    22  func testAppGenerator(t testing.TB, specPath, name string) (*appGenerator, error) {
    23  	specDoc, err := loads.Spec(specPath)
    24  	require.NoError(t, err)
    25  	analyzed := analysis.New(specDoc.Spec())
    26  
    27  	models, err := gatherModels(specDoc, nil)
    28  	require.NoError(t, err)
    29  
    30  	operations := gatherOperations(analyzed, nil)
    31  	if len(operations) == 0 {
    32  		return nil, errors.New("no operations were selected")
    33  	}
    34  
    35  	opts := testGenOpts()
    36  	opts.Spec = specPath
    37  	apiPackage := opts.LanguageOpts.MangleName(swag.ToFileName(opts.APIPackage), "api")
    38  
    39  	return &appGenerator{
    40  		Name:            appNameOrDefault(specDoc, name, "swagger"),
    41  		Receiver:        "o",
    42  		SpecDoc:         specDoc,
    43  		Analyzed:        analyzed,
    44  		Models:          models,
    45  		Operations:      operations,
    46  		Target:          ".",
    47  		DumpData:        opts.DumpData,
    48  		Package:         apiPackage,
    49  		APIPackage:      apiPackage,
    50  		ModelsPackage:   opts.LanguageOpts.MangleName(swag.ToFileName(opts.ModelPackage), "definitions"),
    51  		ServerPackage:   opts.LanguageOpts.MangleName(swag.ToFileName(opts.ServerPackage), "server"),
    52  		ClientPackage:   opts.LanguageOpts.MangleName(swag.ToFileName(opts.ClientPackage), "client"),
    53  		Principal:       opts.Principal,
    54  		DefaultScheme:   "http",
    55  		DefaultProduces: runtime.JSONMime,
    56  		DefaultConsumes: runtime.JSONMime,
    57  		GenOpts:         opts,
    58  	}, nil
    59  }
    60  
    61  func TestServer_UrlEncoded(t *testing.T) {
    62  	defer discardOutput()()
    63  
    64  	gen, err := testAppGenerator(t, "../fixtures/codegen/simplesearch.yml", "search")
    65  	require.NoError(t, err)
    66  
    67  	app, err := gen.makeCodegenApp()
    68  	require.NoError(t, err)
    69  
    70  	buf := bytes.NewBuffer(nil)
    71  	require.NoError(t, app.GenOpts.templates.MustGet("serverBuilder").Execute(buf, app))
    72  
    73  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("search_api.go", buf.Bytes())
    74  	require.NoErrorf(t, err, buf.String())
    75  
    76  	res := string(formatted)
    77  	assert.Regexp(t, "UrlformConsumer:\\s+runtime\\.DiscardConsumer", res)
    78  
    79  	buf = bytes.NewBuffer(nil)
    80  	require.NoError(t, app.GenOpts.templates.MustGet("serverConfigureapi").Execute(buf, app))
    81  
    82  	formatted, err = app.GenOpts.LanguageOpts.FormatContent("configure_search_api.go", buf.Bytes())
    83  	require.NoErrorf(t, err, buf.String())
    84  
    85  	assertInCode(t, "api.UrlformConsumer = runtime.DiscardConsumer", string(formatted))
    86  }
    87  
    88  func TestServer_MultipartForm(t *testing.T) {
    89  	defer discardOutput()()
    90  
    91  	gen, err := testAppGenerator(t, "../fixtures/codegen/shipyard.yml", "shipyard")
    92  	require.NoError(t, err)
    93  
    94  	app, err := gen.makeCodegenApp()
    95  	require.NoError(t, err)
    96  
    97  	buf := bytes.NewBuffer(nil)
    98  	require.NoError(t, app.GenOpts.templates.MustGet("serverBuilder").Execute(buf, app))
    99  
   100  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("shipyard_api.go", buf.Bytes())
   101  	require.NoErrorf(t, err, buf.String())
   102  
   103  	assert.Regexp(t, "MultipartformConsumer:\\s+runtime\\.DiscardConsumer", string(formatted))
   104  
   105  	buf = bytes.NewBuffer(nil)
   106  	require.NoError(t, app.GenOpts.templates.MustGet("serverConfigureapi").Execute(buf, app))
   107  
   108  	formatted, err = app.GenOpts.LanguageOpts.FormatContent("configure_shipyard_api.go", buf.Bytes())
   109  	require.NoErrorf(t, err, buf.String())
   110  
   111  	assertInCode(t, "api.MultipartformConsumer = runtime.DiscardConsumer", string(formatted))
   112  }
   113  
   114  func TestServer_InvalidSpec(t *testing.T) {
   115  	defer discardOutput()()
   116  
   117  	opts := testGenOpts()
   118  	opts.Spec = invalidSpecExample
   119  	opts.ValidateSpec = true
   120  
   121  	require.Error(t, GenerateServer("foo", nil, nil, opts))
   122  }
   123  
   124  func TestServer_TrailingSlash(t *testing.T) {
   125  	defer discardOutput()()
   126  
   127  	gen, err := testAppGenerator(t, "../fixtures/bugs/899/swagger.yml", "trailing slash")
   128  	require.NoError(t, err)
   129  
   130  	app, err := gen.makeCodegenApp()
   131  	require.NoError(t, err)
   132  
   133  	buf := bytes.NewBuffer(nil)
   134  	require.NoError(t, app.GenOpts.templates.MustGet("serverBuilder").Execute(buf, app))
   135  
   136  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("shipyard_api.go", buf.Bytes())
   137  	require.NoError(t, err, buf.String())
   138  
   139  	assertInCode(t, `o.handlers["GET"]["/trailingslashpath"]`, string(formatted))
   140  }
   141  
   142  func TestServer_Issue987(t *testing.T) {
   143  	defer discardOutput()()
   144  
   145  	gen, err := testAppGenerator(t, "../fixtures/bugs/987/swagger.yml", "deeper consumes produces")
   146  	require.NoError(t, err)
   147  
   148  	app, err := gen.makeCodegenApp()
   149  	require.NoError(t, err)
   150  
   151  	buf := bytes.NewBuffer(nil)
   152  	require.NoError(t, app.GenOpts.templates.MustGet("serverBuilder").Execute(buf, app))
   153  
   154  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("shipyard_api.go", buf.Bytes())
   155  	require.NoErrorf(t, err, buf.String())
   156  
   157  	res := string(formatted)
   158  	assertRegexpInCode(t, `JSONConsumer:\s+runtime.JSONConsumer()`, res)
   159  	assertRegexpInCode(t, `JSONProducer:\s+runtime.JSONProducer()`, res)
   160  	assertInCode(t, `result["application/json"] = o.JSONConsumer`, res)
   161  	assertInCode(t, `result["application/json"] = o.JSONProducer`, res)
   162  }
   163  
   164  func TestServer_FilterByTag(t *testing.T) {
   165  	defer discardOutput()()
   166  
   167  	gen, err := testAppGenerator(t, "../fixtures/codegen/simplesearch.yml", "search")
   168  	require.NoError(t, err)
   169  
   170  	gen.GenOpts.Tags = []string{"search"}
   171  	app, err := gen.makeCodegenApp()
   172  	require.NoError(t, err)
   173  
   174  	buf := bytes.NewBuffer(nil)
   175  	require.NoError(t, app.GenOpts.templates.MustGet("serverBuilder").Execute(buf, app))
   176  
   177  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("search_api.go", buf.Bytes())
   178  	require.NoErrorf(t, err, buf.String())
   179  
   180  	res := string(formatted)
   181  	assertInCode(t, `o.handlers["POST"]["/search"]`, res)
   182  	assertNotInCode(t, `o.handlers["POST"]["/tasks"]`, res)
   183  }
   184  
   185  func TestServer_BadTemplate(t *testing.T) {
   186  	// Checking error handling code: panic on mismatched template
   187  
   188  	defer discardOutput()()
   189  
   190  	gen, err := testAppGenerator(nil, "../fixtures/bugs/899/swagger.yml", "trailing slash")
   191  	require.NoError(t, err)
   192  
   193  	app, err := gen.makeCodegenApp()
   194  	require.NoError(t, err)
   195  
   196  	badTemplateCall := func() {
   197  		buf := bytes.NewBuffer(nil)
   198  		_ = app.GenOpts.templates.MustGet("serverBuilderX").Execute(buf, app)
   199  	}
   200  
   201  	assert.Panics(t, badTemplateCall, "templates.MustGet() did not panic() as currently expected")
   202  }
   203  
   204  func TestServer_ErrorParsingTemplate(t *testing.T) {
   205  	// Checking error handling code: panic on bad parsing template
   206  	// High level test with AppGenerator
   207  
   208  	defer discardOutput()()
   209  
   210  	badParse := `{{{ define "T1" }}T1{{end}}{{ define "T2" }}T2{{end}}`
   211  
   212  	gen, err := testAppGenerator(nil, "../fixtures/bugs/899/swagger.yml", "trailing slash")
   213  	require.NoError(t, err)
   214  
   215  	require.Error(t, gen.GenOpts.templates.AddFile("badparse", badParse)) // template is not loaded
   216  
   217  	badParseCall := func() {
   218  		_ = templates.MustGet("badparse") // MustGet panics
   219  	}
   220  
   221  	assert.Panics(t, badParseCall, "templates.MustGet() did not panic() as currently expected")
   222  }
   223  
   224  func TestServer_OperationGroups(t *testing.T) {
   225  	defer discardOutput()()
   226  	defer func() {
   227  		_ = os.RemoveAll(filepath.Join(".", "restapi"))
   228  		_ = os.RemoveAll(filepath.Join(".", "search"))
   229  		_ = os.RemoveAll(filepath.Join(".", "tasks"))
   230  	}()
   231  
   232  	gen, err := testAppGenerator(t, "../fixtures/codegen/simplesearch.yml", "search")
   233  	require.NoError(t, err)
   234  
   235  	gen.GenOpts.Tags = []string{"search", "tasks"}
   236  	gen.GenOpts.IncludeModel = false
   237  	gen.GenOpts.IncludeHandler = true
   238  	gen.GenOpts.Sections.OperationGroups = []TemplateOpts{
   239  		{
   240  			Name:     "opGroupTest",
   241  			Source:   "asset:opGroupTest",
   242  			Target:   "{{ joinFilePath .Target .Name }}",
   243  			FileName: "{{ (snakize (pascalize .Name)) }}_opgroup_test.gol",
   244  		},
   245  	}
   246  
   247  	err = gen.Generate()
   248  	require.Error(t, err)
   249  	assert.Contains(t, strings.ToLower(err.Error()), "template doesn't exist") // Tolerates case variations on error message
   250  
   251  	opGroupTpl := `
   252  // OperationGroupName={{.Name}}
   253  // RootPackage={{.RootPackage}}
   254  {{ range .Operations }}
   255  	// OperationName={{.Name}}
   256  {{end}}`
   257  	_ = gen.GenOpts.templates.AddFile("opGroupTest", opGroupTpl)
   258  	require.NoError(t, gen.Generate())
   259  
   260  	genContent, err := os.ReadFile("./search/search_opgroup_test.gol")
   261  	require.NoError(t, err, "Generator should have written a file")
   262  
   263  	assert.Contains(t, string(genContent), "// OperationGroupName=search")
   264  	assert.Contains(t, string(genContent), "// RootPackage=operations")
   265  	assert.Contains(t, string(genContent), "// OperationName=search")
   266  
   267  	genContent, err = os.ReadFile("./tasks/tasks_opgroup_test.gol")
   268  	require.NoError(t, err, "Generator should have written a file")
   269  
   270  	assert.Contains(t, string(genContent), "// OperationGroupName=tasks")
   271  	assert.Contains(t, string(genContent), "// RootPackage=operations")
   272  	assert.Contains(t, string(genContent), "// OperationName=createTask")
   273  	assert.Contains(t, string(genContent), "// OperationName=deleteTask")
   274  	assert.Contains(t, string(genContent), "// OperationName=getTasks")
   275  	assert.Contains(t, string(genContent), "// OperationName=updateTask")
   276  }
   277  
   278  func TestServer_Issue1301(t *testing.T) {
   279  	defer discardOutput()()
   280  
   281  	gen, err := testAppGenerator(t, "../fixtures/enhancements/1301/swagger.yml", "custom producers")
   282  	require.NoError(t, err)
   283  
   284  	app, err := gen.makeCodegenApp()
   285  	require.NoError(t, err)
   286  
   287  	buf := bytes.NewBuffer(nil)
   288  	require.NoError(t, app.GenOpts.templates.MustGet("serverBuilder").Execute(buf, app))
   289  
   290  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("shipyard_api.go", buf.Bytes())
   291  	require.NoErrorf(t, err, buf.String())
   292  
   293  	res := string(formatted)
   294  
   295  	// initialisation in New<Name>API function
   296  	assertInCode(t, `customConsumers:     make(map[string]runtime.Consumer)`, res)
   297  	assertInCode(t, `customProducers:     make(map[string]runtime.Producer)`, res)
   298  
   299  	// declaration in struct
   300  	assertInCode(t, `customConsumers map[string]runtime.Consumer`, res)
   301  	assertInCode(t, `customProducers map[string]runtime.Producer`, res)
   302  	assertRegexpInCode(t, `if c, ok := o\.customConsumers\[mt\]; ok \{\s+result\[mt\] = c\s+\}`, res)
   303  	assertRegexpInCode(t, `if p, ok := o\.customProducers\[mt\]; ok \{\s+result\[mt\] = p\s+\}`, res)
   304  	assertRegexpInCode(t, `func \(o \*CustomProducersAPI\) RegisterConsumer\(mediaType string, consumer runtime\.Consumer\) \{\s+	o\.customConsumers\[mediaType\] = consumer\s+\}`, res)
   305  	assertRegexpInCode(t, `func \(o \*CustomProducersAPI\) RegisterProducer\(mediaType string, producer runtime\.Producer\) \{\s+	o\.customProducers\[mediaType\] = producer\s+\}`, res)
   306  }
   307  
   308  func TestServer_PreServerShutdown_Issue2108(t *testing.T) {
   309  	defer discardOutput()()
   310  
   311  	gen, err := testAppGenerator(t, "../fixtures/enhancements/2108/swagger.yml", "pre server shutdown")
   312  	require.NoError(t, err)
   313  
   314  	app, err := gen.makeCodegenApp()
   315  	require.NoError(t, err)
   316  
   317  	// check the serverBuilder output
   318  	buf := bytes.NewBuffer(nil)
   319  	require.NoError(t, templates.MustGet("serverBuilder").Execute(buf, app))
   320  
   321  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("shipyard_api.go", buf.Bytes())
   322  	require.NoErrorf(t, err, buf.String())
   323  
   324  	res := string(formatted)
   325  	assertInCode(t, `PreServerShutdown:   func() {},`, res)
   326  	assertInCode(t, `PreServerShutdown func()`, res)
   327  
   328  	buf = bytes.NewBuffer(nil)
   329  	require.NoError(t, templates.MustGet("serverConfigureapi").Execute(buf, app))
   330  
   331  	formatted, err = app.GenOpts.LanguageOpts.FormatContent("configure_shipyard_api.go", buf.Bytes())
   332  	require.NoErrorf(t, err, buf.String())
   333  
   334  	res = string(formatted)
   335  	// initialisation in New<Name>API function
   336  	assertInCode(t, `api.PreServerShutdown = func() {}`, res)
   337  }
   338  
   339  func TestServer_Issue1557(t *testing.T) {
   340  	defer discardOutput()()
   341  
   342  	gen, err := testAppGenerator(t, "../fixtures/enhancements/1557/swagger.yml", "generate consumer/producer handlers that are not whitelisted")
   343  	require.NoError(t, err)
   344  
   345  	app, err := gen.makeCodegenApp()
   346  	require.NoError(t, err)
   347  
   348  	buf := bytes.NewBuffer(nil)
   349  	require.NoError(t, templates.MustGet("serverBuilder").Execute(buf, app))
   350  
   351  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("shipyard_api.go", buf.Bytes())
   352  	require.NoErrorf(t, err, buf.String())
   353  
   354  	res := string(formatted)
   355  	assertInCode(t, `ApplicationDummyConsumer runtime.Consumer`, res)
   356  	assertInCode(t, `ApplicationDummyProducer runtime.Producer`, res)
   357  	assertInCode(t, `ApplicationDummyConsumer: runtime.ConsumerFunc(func(r io.Reader, target interface{}) error {`, res)
   358  	assertInCode(t, `ApplicationDummyProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {`, res)
   359  	assertInCode(t, `BinConsumer: runtime.ByteStreamConsumer(),`, res)
   360  	assertInCode(t, `BinProducer: runtime.ByteStreamProducer(),`, res)
   361  	assertInCode(t, `result["application/pdf"] = o.BinConsumer`, res)
   362  	assertInCode(t, `result["application/pdf"] = o.BinProducer`, res)
   363  	assertInCode(t, `result["application/dummy"] = o.ApplicationDummyConsumer`, res)
   364  	assertInCode(t, `result["application/dummy"] = o.ApplicationDummyProducer`, res)
   365  }
   366  
   367  func TestServer_Issue1648(t *testing.T) {
   368  	defer discardOutput()()
   369  
   370  	gen, err := testAppGenerator(t, "../fixtures/bugs/1648/fixture-1648.yaml", "generate format with missing type in model")
   371  	require.NoError(t, err)
   372  
   373  	_, err = gen.makeCodegenApp()
   374  	require.NoError(t, err)
   375  }
   376  
   377  func TestServer_Issue1746(t *testing.T) {
   378  	defer discardOutput()()
   379  
   380  	targetdir, err := os.MkdirTemp(".", "swagger_server")
   381  	require.NoErrorf(t, err, "failed to create a test target directory: %v", err)
   382  	defer func() {
   383  		_ = os.RemoveAll(targetdir)
   384  	}()
   385  
   386  	cwd := testCwd(t)
   387  	require.NoErrorf(t, os.Chdir(targetdir), "failed to create a test target directory: %v", err)
   388  	defer func() {
   389  		_ = os.Chdir(cwd)
   390  	}()
   391  
   392  	opts := testGenOpts()
   393  	opts.Target = "x"
   394  	opts.Spec = filepath.Join("..", "..", "fixtures", "bugs", "1746", "fixture-1746.yaml")
   395  	tgtSpec := regexp.QuoteMeta(filepath.Join("..", "..", opts.Spec))
   396  
   397  	require.NoError(t, os.Mkdir(opts.Target, 0o755))
   398  
   399  	require.NoError(t, GenerateServer("", nil, nil, opts))
   400  
   401  	gulp, err := os.ReadFile(filepath.Join("x", "restapi", "configure_example_swagger_server.go"))
   402  	require.NoError(t, err)
   403  
   404  	res := string(gulp)
   405  
   406  	tgtPath := regexp.QuoteMeta(filepath.Join("..", "..", opts.Target))
   407  	assertRegexpInCode(t, `go:generate swagger generate server.+\-\-target `+tgtPath, res)
   408  	assertRegexpInCode(t, `go:generate swagger generate server.+\-\-name\s+ExampleSwaggerServer`, res)
   409  	assertRegexpInCode(t, `go:generate swagger generate server.+\-\-spec\s+`+tgtSpec, res)
   410  }
   411  
   412  func doGenAppTemplate(t testing.TB, fixture, template string) string {
   413  	gen, err := testAppGenerator(t, fixture, "generate: "+fixture)
   414  	require.NoError(t, err)
   415  
   416  	app, err := gen.makeCodegenApp()
   417  	require.NoError(t, err)
   418  
   419  	buf := bytes.NewBuffer(nil)
   420  	require.NoError(t, templates.MustGet(template).Execute(buf, app))
   421  
   422  	formatted, err := app.GenOpts.LanguageOpts.FormatContent("foo.go", buf.Bytes())
   423  	require.NoError(t, err)
   424  
   425  	return string(formatted)
   426  }
   427  
   428  func TestServer_Issue1816(t *testing.T) {
   429  	defer discardOutput()()
   430  
   431  	// fixed regression: gob encoding in $ref
   432  	res := doGenAppTemplate(t, "../fixtures/bugs/1816/fixture-1816.yaml", "swaggerJsonEmbed")
   433  	assertNotInCode(t, `"$ref": "#"`, res)
   434  
   435  	// fixed regression: gob encoding in operation security requirements
   436  	res = doGenAppTemplate(t, "../fixtures/bugs/1824/swagger.json", "swaggerJsonEmbed")
   437  	assertInCode(t, `"api_key": []`, res)
   438  	assertNotInCode(t, `"api_key": null`, res)
   439  }
   440  
   441  func TestServer_Issue2346(t *testing.T) {
   442  	defer discardOutput()()
   443  
   444  	targetdir, err := os.MkdirTemp(".", "swagger_server")
   445  	require.NoErrorf(t, err, "failed to create a test target directory: %v", err)
   446  	t.Cleanup(func() {
   447  		_ = os.RemoveAll(targetdir)
   448  	})
   449  
   450  	cwd := testCwd(t)
   451  	require.NoErrorf(t, os.Chdir(targetdir), "failed to chdir to test target directory: %v", err)
   452  	defer func() {
   453  		_ = os.Chdir(cwd)
   454  	}()
   455  
   456  	t.Run("should build server with flatten Expand optio", func(t *testing.T) {
   457  		opts := testGenOpts()
   458  		opts.Target = "x"
   459  		opts.FlattenOpts.Expand = true // this issue pops up spcifically when using this option
   460  		opts.Spec = filepath.Join("..", "..", "fixtures", "bugs", "2346", "swagger.yaml")
   461  		require.NoError(t, os.Mkdir(opts.Target, 0o755))
   462  
   463  		require.NoError(t, GenerateServer("api-2346", nil, nil, opts))
   464  	})
   465  
   466  	t.Run("should build server with flatten Minimal (no expand)", func(t *testing.T) {
   467  		opts := testGenOpts()
   468  		opts.Target = "y"
   469  		opts.FlattenOpts.Minimal = true
   470  		opts.FlattenOpts.Expand = false
   471  		opts.Spec = filepath.Join("..", "..", "fixtures", "bugs", "2346", "swagger.yaml")
   472  		require.NoError(t, os.Mkdir(opts.Target, 0o755))
   473  
   474  		require.NoError(t, GenerateServer("api-2346", nil, nil, opts))
   475  	})
   476  }