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