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 }