github.com/AngusLu/go-swagger@v0.28.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/go-openapi/analysis" 14 "github.com/go-openapi/loads" 15 "github.com/go-openapi/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 }