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

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