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 }