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