github.com/6543-forks/go-swagger@v0.26.0/generator/server_test.go (about)

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