github.com/rzurga/go-swagger@v0.28.1-0.20211109195225-5d1f453ffa3a/generator/operation_test.go (about) 1 // Copyright 2015 go-swagger maintainers 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package generator 16 17 import ( 18 "bytes" 19 "errors" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "testing" 24 25 "github.com/go-openapi/analysis" 26 "github.com/go-openapi/loads" 27 "github.com/go-openapi/spec" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestUniqueOperationNameMangling(t *testing.T) { 33 doc, err := loads.Spec("../fixtures/bugs/2213/fixture-2213.yaml") 34 require.NoError(t, err) 35 analyzed := analysis.New(doc.Spec()) 36 ops := gatherOperations(analyzed, nil) 37 assert.Contains(t, ops, "GetFoo") 38 assert.Contains(t, ops, "GetAFoo") 39 } 40 41 func TestUniqueOperationNames(t *testing.T) { 42 doc, err := loads.Spec("../fixtures/codegen/todolist.simple.yml") 43 require.NoError(t, err) 44 45 sp := doc.Spec() 46 sp.Paths.Paths["/tasks"].Post.ID = "saveTask" 47 sp.Paths.Paths["/tasks"].Post.AddExtension("origName", "createTask") 48 sp.Paths.Paths["/tasks/{id}"].Put.ID = "saveTask" 49 sp.Paths.Paths["/tasks/{id}"].Put.AddExtension("origName", "updateTask") 50 analyzed := analysis.New(sp) 51 52 ops := gatherOperations(analyzed, nil) 53 assert.Len(t, ops, 6) 54 _, exists := ops["saveTask"] 55 assert.True(t, exists) 56 _, exists = ops["PutTasksID"] 57 assert.True(t, exists) 58 } 59 60 func TestEmptyOperationNames(t *testing.T) { 61 doc, err := loads.Spec("../fixtures/codegen/todolist.simple.yml") 62 require.NoError(t, err) 63 64 sp := doc.Spec() 65 sp.Paths.Paths["/tasks"].Post.ID = "" 66 sp.Paths.Paths["/tasks"].Post.AddExtension("origName", "createTask") 67 sp.Paths.Paths["/tasks/{id}"].Put.ID = "" 68 sp.Paths.Paths["/tasks/{id}"].Put.AddExtension("origName", "updateTask") 69 analyzed := analysis.New(sp) 70 71 ops := gatherOperations(analyzed, nil) 72 assert.Len(t, ops, 6) 73 _, exists := ops["PostTasks"] 74 assert.True(t, exists) 75 _, exists = ops["PutTasksID"] 76 assert.True(t, exists) 77 } 78 79 func TestMakeResponseHeader(t *testing.T) { 80 b, err := opBuilder("getTasks", "") 81 require.NoError(t, err) 82 83 hdr := findResponseHeader(&b.Operation, 200, "X-Rate-Limit") 84 gh, er := b.MakeHeader("a", "X-Rate-Limit", *hdr) 85 require.NoError(t, er) 86 87 assert.True(t, gh.IsPrimitive) 88 assert.Equal(t, "int32", gh.GoType) 89 assert.Equal(t, "X-Rate-Limit", gh.Name) 90 } 91 92 func TestMakeResponseHeaderDefaultValues(t *testing.T) { 93 b, err := opBuilder("getTasks", "") 94 require.NoError(t, err) 95 96 var testCases = []struct { 97 name string // input 98 typeStr string // expected type 99 defaultValue interface{} // expected result 100 }{ 101 {"Access-Control-Allow-Origin", "string", "*"}, 102 {"X-Rate-Limit", "int32", nil}, 103 {"X-Rate-Limit-Remaining", "int32", float64(42)}, 104 {"X-Rate-Limit-Reset", "int32", "1449875311"}, 105 {"X-Rate-Limit-Reset-Human", "string", "3 days"}, 106 {"X-Rate-Limit-Reset-Human-Number", "string", float64(3)}, 107 } 108 109 for _, tc := range testCases { 110 hdr := findResponseHeader(&b.Operation, 200, tc.name) 111 require.NotNil(t, hdr) 112 113 gh, er := b.MakeHeader("a", tc.name, *hdr) 114 require.NoError(t, er) 115 116 assert.True(t, gh.IsPrimitive) 117 assert.Equal(t, tc.typeStr, gh.GoType) 118 assert.Equal(t, tc.name, gh.Name) 119 assert.Exactly(t, tc.defaultValue, gh.Default) 120 } 121 } 122 123 func TestMakeResponse(t *testing.T) { 124 b, err := opBuilder("getTasks", "") 125 require.NoError(t, err) 126 127 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 128 resolver.KnownDefs = make(map[string]struct{}) 129 for k := range b.Doc.Spec().Definitions { 130 resolver.KnownDefs[k] = struct{}{} 131 } 132 gO, err := b.MakeResponse("a", "getTasksSuccess", true, resolver, 200, b.Operation.Responses.StatusCodeResponses[200]) 133 require.NoError(t, err) 134 135 assert.Len(t, gO.Headers, 6) 136 assert.NotNil(t, gO.Schema) 137 assert.True(t, gO.Schema.IsArray) 138 assert.NotNil(t, gO.Schema.Items) 139 assert.False(t, gO.Schema.IsAnonymous) 140 assert.Equal(t, "[]*models.Task", gO.Schema.GoType) 141 } 142 143 func TestMakeResponse_WithAllOfSchema(t *testing.T) { 144 b, err := methodPathOpBuilder("get", "/media/search", "../fixtures/codegen/instagram.yml") 145 require.NoError(t, err) 146 147 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 148 resolver.KnownDefs = make(map[string]struct{}) 149 for k := range b.Doc.Spec().Definitions { 150 resolver.KnownDefs[k] = struct{}{} 151 } 152 gO, err := b.MakeResponse("a", "get /media/search", true, resolver, 200, b.Operation.Responses.StatusCodeResponses[200]) 153 require.NoError(t, err) 154 155 require.NotNil(t, gO.Schema) 156 assert.Equal(t, "GetMediaSearchBody", gO.Schema.GoType) 157 158 require.NotEmpty(t, b.ExtraSchemas) 159 body := b.ExtraSchemas["GetMediaSearchBody"] 160 require.NotEmpty(t, body.Properties) 161 162 prop := body.Properties[0] 163 assert.Equal(t, "data", prop.Name) 164 // is in models only when definition is flattened: otherwise, ExtraSchema is rendered in operations package 165 assert.Equal(t, "[]*GetMediaSearchBodyDataItems0", prop.GoType) 166 167 items := b.ExtraSchemas["GetMediaSearchBodyDataItems0"] 168 require.NotEmpty(t, items.AllOf) 169 170 media := items.AllOf[0] 171 // expect #definitions/media to be captured and reused by ExtraSchema 172 assert.Equal(t, "models.Media", media.GoType) 173 } 174 175 func TestMakeOperationParam(t *testing.T) { 176 b, err := opBuilder("getTasks", "") 177 require.NoError(t, err) 178 179 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 180 gO, err := b.MakeParameter("a", resolver, b.Operation.Parameters[0], nil) 181 require.NoError(t, err) 182 183 assert.Equal(t, "size", gO.Name) 184 assert.True(t, gO.IsPrimitive) 185 } 186 187 func TestMakeOperationParamItem(t *testing.T) { 188 b, err := opBuilder("arrayQueryParams", "../fixtures/codegen/todolist.arrayquery.yml") 189 require.NoError(t, err) 190 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 191 gO, err := b.MakeParameterItem("a", "siString", "ii", "siString", "a.SiString", "query", resolver, b.Operation.Parameters[1].Items, nil) 192 require.NoError(t, err) 193 assert.Nil(t, gO.Parent) 194 assert.True(t, gO.IsPrimitive) 195 } 196 197 func TestMakeOperation(t *testing.T) { 198 b, err := opBuilder("getTasks", "") 199 require.NoError(t, err) 200 gO, err := b.MakeOperation() 201 require.NoError(t, err) 202 assert.Equal(t, "getTasks", gO.Name) 203 assert.Equal(t, "GET", gO.Method) 204 assert.Equal(t, "/tasks", gO.Path) 205 assert.Len(t, gO.Params, 2) 206 assert.Len(t, gO.Responses, 1) 207 assert.NotNil(t, gO.DefaultResponse) 208 assert.NotNil(t, gO.SuccessResponse) 209 } 210 211 func TestRenderOperation_InstagramSearch(t *testing.T) { 212 defer discardOutput()() 213 214 b, err := methodPathOpBuilder("get", "/media/search", "../fixtures/codegen/instagram.yml") 215 require.NoError(t, err) 216 217 gO, err := b.MakeOperation() 218 require.NoError(t, err) 219 220 buf := bytes.NewBuffer(nil) 221 opt := opts() 222 223 require.NoError(t, opt.templates.MustGet("serverOperation").Execute(buf, gO)) 224 225 ff, err := opt.LanguageOpts.FormatContent("operation.go", buf.Bytes()) 226 require.NoErrorf(t, err, buf.String()) 227 228 res := string(ff) 229 assertInCode(t, "type GetMediaSearchOKBody struct {", res) 230 // codegen does not assumes objects are only in models 231 // this is inlined 232 assertInCode(t, "Data []*GetMediaSearchOKBodyDataItems0 `json:\"data\"`", res) 233 assertInCode(t, "type GetMediaSearchOKBodyDataItems0 struct {", res) 234 // this is a definition: expect this definition to be reused from the models pkg 235 assertInCode(t, "models.Media", res) 236 237 buf = bytes.NewBuffer(nil) 238 require.NoError(t, opt.templates.MustGet("serverResponses").Execute(buf, gO)) 239 240 ff, err = opt.LanguageOpts.FormatContent("response.go", buf.Bytes()) 241 require.NoErrorf(t, err, buf.String()) 242 243 res = string(ff) 244 // codegen does not assumes objects are only in models 245 assertInCode(t, "type GetMediaSearchOK struct {", res) 246 assertInCode(t, "GetMediaSearchOKBody", res) 247 248 b, err = methodPathOpBuilderWithFlatten("get", "/media/search", "../fixtures/codegen/instagram.yml") 249 require.NoError(t, err) 250 251 gO, err = b.MakeOperation() 252 require.NoError(t, err) 253 254 buf = bytes.NewBuffer(nil) 255 opt = opts() 256 require.NoError(t, opt.templates.MustGet("serverOperation").Execute(buf, gO)) 257 258 ff, err = opt.LanguageOpts.FormatContent("operation.go", buf.Bytes()) 259 require.NoErrorf(t, err, buf.String()) 260 261 res = string(ff) 262 assertNotInCode(t, "DataItems0", res) 263 assertNotInCode(t, "models", res) 264 265 buf = bytes.NewBuffer(nil) 266 require.NoError(t, opt.templates.MustGet("serverResponses").Execute(buf, gO)) 267 268 ff, err = opt.LanguageOpts.FormatContent("operation.go", buf.Bytes()) 269 require.NoErrorf(t, err, buf.String()) 270 271 res = string(ff) 272 assertInCode(t, "Payload *models.GetMediaSearchOKBody", res) 273 } 274 275 func methodPathOpBuilder(method, path, fname string) (codeGenOpBuilder, error) { 276 defer discardOutput()() 277 278 if fname == "" { 279 fname = "../fixtures/codegen/todolist.simple.yml" 280 } 281 o := opts() 282 o.Spec = fname 283 specDoc, analyzed, err := o.analyzeSpec() 284 if err != nil { 285 return codeGenOpBuilder{}, err 286 } 287 op, ok := analyzed.OperationFor(method, path) 288 if !ok { 289 return codeGenOpBuilder{}, errors.New("No operation could be found for " + method + " " + path) 290 } 291 292 return codeGenOpBuilder{ 293 Name: method + " " + path, 294 Method: method, 295 Path: path, 296 APIPackage: "restapi", 297 ModelsPackage: "models", 298 Principal: "models.User", 299 Target: ".", 300 Operation: *op, 301 Doc: specDoc, 302 Analyzed: analyzed, 303 Authed: false, 304 ExtraSchemas: make(map[string]GenSchema), 305 GenOpts: o, 306 }, nil 307 } 308 309 // methodPathOpBuilderWithFlatten prepares an operation build based on method and path, with spec full flattening 310 func methodPathOpBuilderWithFlatten(method, path, fname string) (codeGenOpBuilder, error) { 311 defer discardOutput()() 312 313 if fname == "" { 314 fname = "../fixtures/codegen/todolist.simple.yml" 315 } 316 317 o := opBuildGetOpts(fname, true, false) // flatten: true, minimal: false 318 o.Spec = fname 319 specDoc, analyzed, err := o.analyzeSpec() 320 if err != nil { 321 return codeGenOpBuilder{}, err 322 } 323 op, ok := analyzed.OperationFor(method, path) 324 if !ok { 325 return codeGenOpBuilder{}, errors.New("No operation could be found for " + method + " " + path) 326 } 327 328 return codeGenOpBuilder{ 329 Name: method + " " + path, 330 Method: method, 331 Path: path, 332 APIPackage: "restapi", 333 ModelsPackage: "models", 334 Principal: "models.User", 335 Target: ".", 336 Operation: *op, 337 Doc: specDoc, 338 Analyzed: analyzed, 339 Authed: false, 340 ExtraSchemas: make(map[string]GenSchema), 341 GenOpts: opts(), 342 }, nil 343 } 344 345 // opBuilderWithOpts prepares the making of an operation with spec flattening options 346 func opBuilderWithOpts(name, fname string, o *GenOpts) (codeGenOpBuilder, error) { 347 defer discardOutput()() 348 349 if fname == "" { 350 // default fixture 351 fname = "../fixtures/codegen/todolist.simple.yml" 352 } 353 354 o.Spec = fname 355 specDoc, analyzed, err := o.analyzeSpec() 356 if err != nil { 357 return codeGenOpBuilder{}, err 358 } 359 360 method, path, op, ok := analyzed.OperationForName(name) 361 if !ok { 362 return codeGenOpBuilder{}, errors.New("No operation could be found for " + name) 363 } 364 365 return codeGenOpBuilder{ 366 Name: name, 367 Method: method, 368 Path: path, 369 BasePath: specDoc.BasePath(), 370 APIPackage: "restapi", 371 ModelsPackage: "models", 372 Principal: "models.User", 373 Target: ".", 374 Operation: *op, 375 Doc: specDoc, 376 Analyzed: analyzed, 377 Authed: false, 378 ExtraSchemas: make(map[string]GenSchema), 379 GenOpts: o, 380 }, nil 381 } 382 383 func opBuildGetOpts(specName string, withFlatten bool, withMinimalFlatten bool) (opts *GenOpts) { 384 opts = &GenOpts{} 385 opts.ValidateSpec = true 386 opts.FlattenOpts = &analysis.FlattenOpts{ 387 Expand: !withFlatten, 388 Minimal: withMinimalFlatten, 389 } 390 opts.Spec = specName 391 if err := opts.EnsureDefaults(); err != nil { 392 panic("Cannot initialize GenOpts") 393 } 394 return 395 } 396 397 // opBuilderWithFlatten prepares the making of an operation with spec full flattening prior to rendering 398 func opBuilderWithFlatten(name, fname string) (codeGenOpBuilder, error) { 399 o := opBuildGetOpts(fname, true, false) // flatten: true, minimal: false 400 return opBuilderWithOpts(name, fname, o) 401 } 402 403 /* 404 // opBuilderWithMinimalFlatten prepares the making of an operation with spec minimal flattening prior to rendering 405 func opBuilderWithMinimalFlatten(name, fname string) (codeGenOpBuilder, error) { 406 o := opBuildGetOpts(fname, true, true) // flatten: true, minimal: true 407 return opBuilderWithOpts(name, fname, o) 408 } 409 */ 410 411 // opBuilderWithExpand prepares the making of an operation with spec expansion prior to rendering 412 func opBuilderWithExpand(name, fname string) (codeGenOpBuilder, error) { 413 o := opBuildGetOpts(fname, false, false) // flatten: false => expand 414 return opBuilderWithOpts(name, fname, o) 415 } 416 417 // opBuilder prepares the making of an operation with spec minimal flattening (default for CLI) 418 func opBuilder(name, fname string) (codeGenOpBuilder, error) { 419 o := opBuildGetOpts(fname, true, true) // flatten:true, minimal: true 420 // some fixtures do not fully validate - skip this 421 o.ValidateSpec = false 422 return opBuilderWithOpts(name, fname, o) 423 } 424 425 func findResponseHeader(op *spec.Operation, code int, name string) *spec.Header { 426 resp := op.Responses.Default 427 if code > 0 { 428 bb, ok := op.Responses.StatusCodeResponses[code] 429 if ok { 430 resp = &bb 431 } 432 } 433 434 if resp == nil { 435 return nil 436 } 437 438 hdr, ok := resp.Headers[name] 439 if !ok { 440 return nil 441 } 442 443 return &hdr 444 } 445 446 func TestDateFormat_Spec1(t *testing.T) { 447 b, err := opBuilder("putTesting", "../fixtures/bugs/193/spec1.json") 448 require.NoError(t, err) 449 450 op, err := b.MakeOperation() 451 require.NoError(t, err) 452 453 buf := bytes.NewBuffer(nil) 454 opts := opts() 455 opts.defaultsEnsured = false 456 opts.IsClient = true 457 require.NoError(t, opts.EnsureDefaults()) 458 459 require.NoError(t, opts.templates.MustGet("clientParameter").Execute(buf, op)) 460 461 ff, err := opts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 462 require.NoErrorf(t, err, buf.String()) 463 464 assertInCode(t, "frTestingThis.String()", string(ff)) 465 } 466 467 func TestDateFormat_Spec2(t *testing.T) { 468 b, err := opBuilder("putTesting", "../fixtures/bugs/193/spec2.json") 469 require.NoError(t, err) 470 471 op, err := b.MakeOperation() 472 require.NoError(t, err) 473 474 buf := bytes.NewBuffer(nil) 475 opts := opts() 476 opts.defaultsEnsured = false 477 opts.IsClient = true 478 require.NoError(t, opts.EnsureDefaults()) 479 480 require.NoError(t, opts.templates.MustGet("clientParameter").Execute(buf, op)) 481 482 ff, err := opts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 483 require.NoErrorf(t, err, buf.String()) 484 485 res := string(ff) 486 assertInCode(t, "o.TestingThis != nil {", res) 487 assertInCode(t, "joinedTestingThis := o.bindParamTestingThis(reg)", res) 488 assertInCode(t, `if err := r.SetFormParam("testingThis", joinedTestingThis...); err != nil {`, res) 489 assertInCode(t, "func (o *PutTestingParams) bindParamTestingThis(formats strfmt.Registry) []string {", res) 490 assertInCode(t, "testingThisIR := o.TestingThis", res) 491 assertInCode(t, "var testingThisIC []string", res) 492 assertInCode(t, "for _, testingThisIIR := range testingThisIR {", res) 493 assertInCode(t, "testingThisIIV := testingThisIIR.String()", res) 494 assertInCode(t, "testingThisIC = append(testingThisIC, testingThisIIV)", res) 495 assertInCode(t, `testingThisIS := swag.JoinByFormat(testingThisIC, "")`, res) 496 assertInCode(t, "return testingThisIS", res) 497 } 498 499 func TestBuilder_Issue1703(t *testing.T) { 500 defer discardOutput()() 501 502 dr := testCwd(t) 503 504 opts := &GenOpts{ 505 Spec: filepath.FromSlash("../fixtures/codegen/existing-model.yml"), 506 IncludeModel: true, 507 IncludeHandler: true, 508 IncludeParameters: true, 509 IncludeResponses: true, 510 IncludeMain: true, 511 APIPackage: "restapi", 512 ModelPackage: "model", 513 ServerPackage: "server", 514 ClientPackage: "client", 515 Target: dr, 516 } 517 require.NoError(t, opts.EnsureDefaults()) 518 519 appGen, err := newAppGenerator("x-go-type-import-bug", nil, nil, opts) 520 require.NoError(t, err) 521 522 op, err := appGen.makeCodegenApp() 523 require.NoError(t, err) 524 525 for _, o := range op.Operations { 526 buf := bytes.NewBuffer(nil) 527 require.NoError(t, opts.templates.MustGet("serverResponses").Execute(buf, o)) 528 529 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("response.go", buf.Bytes()) 530 require.NoErrorf(t, err, buf.String()) 531 532 assertInCode(t, "jwk \"github.com/user/package\"", string(ff)) 533 } 534 } 535 536 func TestBuilder_Issue287(t *testing.T) { 537 defer discardOutput()() 538 539 dr := testCwd(t) 540 541 opts := &GenOpts{ 542 Spec: filepath.FromSlash("../fixtures/bugs/287/swagger.yml"), 543 IncludeModel: true, 544 IncludeHandler: true, 545 IncludeParameters: true, 546 IncludeResponses: true, 547 IncludeMain: true, 548 APIPackage: "restapi", 549 ModelPackage: "model", 550 ServerPackage: "server", 551 ClientPackage: "client", 552 Target: dr, 553 } 554 require.NoError(t, opts.EnsureDefaults()) 555 556 appGen, err := newAppGenerator("plainTexter", nil, nil, opts) 557 require.NoError(t, err) 558 559 op, err := appGen.makeCodegenApp() 560 require.NoError(t, err) 561 562 buf := bytes.NewBuffer(nil) 563 require.NoError(t, opts.templates.MustGet("serverBuilder").Execute(buf, op)) 564 565 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 566 require.NoErrorf(t, err, buf.String()) 567 568 assertInCode(t, "case \"text/plain\":", string(ff)) 569 } 570 571 func TestBuilder_Issue465(t *testing.T) { 572 defer discardOutput()() 573 574 dr := testCwd(t) 575 576 opts := &GenOpts{ 577 Spec: filepath.FromSlash("../fixtures/bugs/465/swagger.yml"), 578 IncludeModel: true, 579 IncludeHandler: true, 580 IncludeParameters: true, 581 IncludeResponses: true, 582 IncludeMain: true, 583 APIPackage: "restapi", 584 ModelPackage: "model", 585 ServerPackage: "server", 586 ClientPackage: "client", 587 Target: dr, 588 IsClient: true, 589 } 590 require.NoError(t, opts.EnsureDefaults()) 591 592 appGen, err := newAppGenerator("plainTexter", nil, nil, opts) 593 require.NoError(t, err) 594 595 op, err := appGen.makeCodegenApp() 596 require.NoError(t, err) 597 598 buf := bytes.NewBuffer(nil) 599 require.NoError(t, opts.templates.MustGet("clientFacade").Execute(buf, op)) 600 601 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 602 require.NoErrorf(t, err, buf.String()) 603 604 assertInCode(t, "/v1/fancyAPI", string(ff)) 605 } 606 607 func TestBuilder_Issue500(t *testing.T) { 608 defer discardOutput()() 609 610 dr := testCwd(t) 611 612 opts := &GenOpts{ 613 Spec: filepath.FromSlash("../fixtures/bugs/500/swagger.yml"), 614 IncludeModel: true, 615 IncludeHandler: true, 616 IncludeParameters: true, 617 IncludeResponses: true, 618 IncludeMain: true, 619 APIPackage: "restapi", 620 ModelPackage: "model", 621 ServerPackage: "server", 622 ClientPackage: "client", 623 Target: dr, 624 } 625 require.NoError(t, opts.EnsureDefaults()) 626 627 appGen, err := newAppGenerator("multiTags", nil, nil, opts) 628 require.NoError(t, err) 629 630 op, err := appGen.makeCodegenApp() 631 require.NoError(t, err) 632 633 buf := bytes.NewBuffer(nil) 634 require.NoError(t, opts.templates.MustGet("serverBuilder").Execute(buf, op)) 635 636 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 637 require.NoErrorf(t, err, buf.String()) 638 639 res := string(ff) 640 assertNotInCode(t, `o.handlers["GET"]["/payment/{invoice_id}/payments/{payment_id}"] = NewGetPaymentByID(o.context, o.GetPaymentByIDHandler)`, res) 641 assertInCode(t, `o.handlers["GET"]["/payment/{invoice_id}/payments/{payment_id}"] = invoices.NewGetPaymentByID(o.context, o.InvoicesGetPaymentByIDHandler)`, res) 642 } 643 644 func TestGenClient_IllegalBOM(t *testing.T) { 645 b, err := methodPathOpBuilder("get", "/v3/attachments/{attachmentId}", "../fixtures/bugs/727/swagger.json") 646 require.NoError(t, err) 647 648 op, err := b.MakeOperation() 649 require.NoError(t, err) 650 651 buf := bytes.NewBuffer(nil) 652 opts := opts() 653 opts.defaultsEnsured = false 654 opts.IsClient = true 655 require.NoError(t, opts.EnsureDefaults()) 656 657 require.NoError(t, opts.templates.MustGet("clientResponse").Execute(buf, op)) 658 } 659 660 func TestGenClient_CustomFormatPath(t *testing.T) { 661 b, err := methodPathOpBuilder("get", "/mosaic/experimental/series/{SeriesId}/mosaics", "../fixtures/bugs/789/swagger.yml") 662 require.NoError(t, err) 663 664 op, err := b.MakeOperation() 665 require.NoError(t, err) 666 667 buf := bytes.NewBuffer(nil) 668 opts := opts() 669 opts.defaultsEnsured = false 670 opts.IsClient = true 671 require.NoError(t, opts.EnsureDefaults()) 672 673 require.NoError(t, opts.templates.MustGet("clientParameter").Execute(buf, op)) 674 675 assertInCode(t, `if err := r.SetPathParam("SeriesId", o.SeriesID.String()); err != nil`, buf.String()) 676 } 677 678 func TestGenClient_Issue733(t *testing.T) { 679 b, err := opBuilder("get_characters_character_id_mail_mail_id", "../fixtures/bugs/733/swagger.json") 680 require.NoError(t, err) 681 682 op, err := b.MakeOperation() 683 require.NoError(t, err) 684 685 buf := bytes.NewBuffer(nil) 686 opts := opts() 687 opts.defaultsEnsured = false 688 opts.IsClient = true 689 require.NoError(t, opts.EnsureDefaults()) 690 691 require.NoError(t, opts.templates.MustGet("clientResponse").Execute(buf, op)) 692 693 assertInCode(t, "Labels []*int64 `json:\"labels\"`", buf.String()) 694 } 695 696 func TestGenServerIssue890_ValidationTrueFlatteningTrue(t *testing.T) { 697 defer discardOutput()() 698 699 dr := testCwd(t) 700 701 opts := &GenOpts{ 702 Spec: filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"), 703 IncludeModel: true, 704 IncludeHandler: true, 705 IncludeParameters: true, 706 IncludeResponses: true, 707 IncludeMain: true, 708 ValidateSpec: true, 709 APIPackage: "restapi", 710 ModelPackage: "model", 711 ServerPackage: "server", 712 ClientPackage: "client", 713 Target: dr, 714 IsClient: true, 715 } 716 717 // Testing Server Generation 718 require.NoError(t, opts.EnsureDefaults()) 719 720 // Full flattening 721 opts.FlattenOpts.Expand = false 722 opts.FlattenOpts.Minimal = false 723 appGen, err := newAppGenerator("JsonRefOperation", nil, nil, opts) 724 require.NoError(t, err) 725 726 op, err := appGen.makeCodegenApp() 727 require.NoError(t, err) 728 729 buf := bytes.NewBuffer(nil) 730 require.NoError(t, opts.templates.MustGet("serverOperation").Execute(buf, op.Operations[0])) 731 732 filecontent, err := appGen.GenOpts.LanguageOpts.FormatContent("operation.go", buf.Bytes()) 733 require.NoErrorf(t, err, buf.String()) 734 735 res := string(filecontent) 736 assertInCode(t, "GetHealthCheck", res) 737 } 738 739 func TestGenClientIssue890_ValidationTrueFlatteningTrue(t *testing.T) { 740 defer discardOutput()() 741 742 dr := testCwd(t) 743 defer func() { 744 _ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi")) 745 }() 746 747 opts := testGenOpts() 748 opts.Spec = "../fixtures/bugs/890/swagger.yaml" 749 opts.ValidateSpec = true 750 opts.FlattenOpts.Minimal = false 751 752 // Testing this is enough as there is only one operation which is specified as $ref. 753 // If this doesn't get resolved then there will be an error definitely. 754 require.NoError(t, GenerateClient("foo", nil, nil, opts)) 755 } 756 757 func TestGenServerIssue890_ValidationFalseFlattenTrue(t *testing.T) { 758 defer discardOutput()() 759 760 dr := testCwd(t) 761 762 opts := &GenOpts{ 763 Spec: filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"), 764 IncludeModel: true, 765 IncludeHandler: true, 766 IncludeParameters: true, 767 IncludeResponses: true, 768 IncludeMain: true, 769 APIPackage: "restapi", 770 ModelPackage: "model", 771 ServerPackage: "server", 772 ClientPackage: "client", 773 Target: dr, 774 IsClient: true, 775 } 776 777 // Testing Server Generation 778 require.NoError(t, opts.EnsureDefaults()) 779 780 // full flattening 781 opts.FlattenOpts.Minimal = false 782 appGen, err := newAppGenerator("JsonRefOperation", nil, nil, opts) 783 require.NoError(t, err) 784 785 op, err := appGen.makeCodegenApp() 786 require.NoError(t, err) 787 788 buf := bytes.NewBuffer(nil) 789 err = opts.templates.MustGet("serverOperation").Execute(buf, op.Operations[0]) 790 require.NoError(t, err) 791 792 filecontent, err := appGen.GenOpts.LanguageOpts.FormatContent("operation.go", buf.Bytes()) 793 require.NoErrorf(t, err, buf.String()) 794 795 res := string(filecontent) 796 assertInCode(t, "GetHealthCheck", res) 797 } 798 799 func TestGenClientIssue890_ValidationFalseFlatteningTrue(t *testing.T) { 800 defer discardOutput()() 801 802 dr := testCwd(t) 803 defer func() { 804 _ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi")) 805 }() 806 807 opts := testGenOpts() 808 opts.Spec = "../fixtures/bugs/890/swagger.yaml" 809 opts.ValidateSpec = false 810 // full flattening 811 opts.FlattenOpts.Minimal = false 812 // Testing this is enough as there is only one operation which is specified as $ref. 813 // If this doesn't get resolved then there will be an error definitely. 814 assert.NoError(t, GenerateClient("foo", nil, nil, opts)) 815 } 816 817 func TestGenServerIssue890_ValidationFalseFlattenFalse(t *testing.T) { 818 defer discardOutput()() 819 820 dr := testCwd(t) 821 822 opts := &GenOpts{ 823 Spec: filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"), 824 IncludeModel: true, 825 IncludeHandler: true, 826 IncludeParameters: true, 827 IncludeResponses: true, 828 IncludeMain: true, 829 ValidateSpec: false, 830 APIPackage: "restapi", 831 ModelPackage: "model", 832 ServerPackage: "server", 833 ClientPackage: "client", 834 Target: dr, 835 IsClient: true, 836 } 837 838 // Testing Server Generation 839 require.NoError(t, opts.EnsureDefaults()) 840 841 // minimal flattening 842 opts.FlattenOpts.Minimal = true 843 _, err := newAppGenerator("JsonRefOperation", nil, nil, opts) 844 // if flatten is not set, expand takes over so this would resume normally 845 assert.NoError(t, err) 846 } 847 848 func TestGenClientIssue890_ValidationFalseFlattenFalse(t *testing.T) { 849 defer discardOutput()() 850 851 dr := testCwd(t) 852 defer func() { 853 _ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi")) 854 }() 855 856 opts := testGenOpts() 857 opts.Spec = "../fixtures/bugs/890/swagger.yaml" 858 opts.ValidateSpec = false 859 // minimal flattening 860 opts.FlattenOpts.Minimal = true 861 // Testing this is enough as there is only one operation which is specified as $ref. 862 // If this doesn't get resolved then there will be an error definitely. 863 // New: Now if flatten is false, expand takes over so server generation should resume normally 864 assert.NoError(t, GenerateClient("foo", nil, nil, opts)) 865 } 866 867 func TestGenServerIssue890_ValidationTrueFlattenFalse(t *testing.T) { 868 defer discardOutput()() 869 870 dr := testCwd(t) 871 872 opts := &GenOpts{ 873 Spec: filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"), 874 IncludeModel: true, 875 IncludeHandler: true, 876 IncludeParameters: true, 877 IncludeResponses: true, 878 IncludeMain: true, 879 ValidateSpec: true, 880 APIPackage: "restapi", 881 ModelPackage: "model", 882 ServerPackage: "server", 883 ClientPackage: "client", 884 Target: dr, 885 IsClient: true, 886 } 887 888 // Testing Server Generation 889 require.NoError(t, opts.EnsureDefaults()) 890 891 // minimal flattening 892 opts.FlattenOpts.Minimal = true 893 894 _, err := newAppGenerator("JsonRefOperation", nil, nil, opts) 895 // now if flatten is false, expand takes over so server generation should resume normally 896 assert.NoError(t, err) 897 } 898 899 func TestGenServerWithTemplate(t *testing.T) { 900 defer discardOutput()() 901 902 dr := testCwd(t) 903 904 tests := []struct { 905 name string 906 opts *GenOpts 907 wantError bool 908 }{ 909 { 910 name: "None_existing_contributor_template", 911 opts: &GenOpts{ 912 Spec: filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"), 913 IncludeModel: true, 914 IncludeHandler: true, 915 IncludeParameters: true, 916 IncludeResponses: true, 917 IncludeMain: true, 918 ValidateSpec: true, 919 APIPackage: "restapi", 920 ModelPackage: "model", 921 ServerPackage: "server", 922 ClientPackage: "client", 923 Target: dr, 924 IsClient: true, 925 Template: "InvalidTemplate", 926 }, 927 wantError: true, 928 }, 929 { 930 name: "Existing_contributor", 931 opts: &GenOpts{ 932 Spec: filepath.FromSlash("../fixtures/bugs/890/swagger.yaml"), 933 IncludeModel: true, 934 IncludeHandler: true, 935 IncludeParameters: true, 936 IncludeResponses: true, 937 IncludeMain: true, 938 ValidateSpec: true, 939 APIPackage: "restapi", 940 ModelPackage: "model", 941 ServerPackage: "server", 942 ClientPackage: "client", 943 Target: dr, 944 IsClient: true, 945 Template: "stratoscale", 946 }, 947 wantError: false, 948 }, 949 } 950 951 t.Run("codegen operations", func(t *testing.T) { 952 for _, toPin := range tests { 953 tt := toPin 954 t.Run(tt.name, func(t *testing.T) { 955 t.Parallel() 956 957 // Testing Server Generation 958 require.NoError(t, tt.opts.EnsureDefaults()) 959 960 // minimal flattening 961 tt.opts.FlattenOpts.Minimal = true 962 _, err := newAppGenerator("JsonRefOperation", nil, nil, tt.opts) 963 if tt.wantError { 964 require.Error(t, err) 965 } else { 966 require.NoError(t, err) 967 } 968 }) 969 } 970 }) 971 } 972 973 func TestGenClientIssue890_ValidationTrueFlattenFalse(t *testing.T) { 974 defer discardOutput()() 975 976 dr := testCwd(t) 977 defer func() { 978 _ = os.RemoveAll(filepath.Join(filepath.FromSlash(dr), "restapi")) 979 }() 980 981 opts := testGenOpts() 982 opts.Spec = filepath.FromSlash("../fixtures/bugs/890/swagger.yaml") 983 opts.ValidateSpec = true 984 // Testing this is enough as there is only one operation which is specified as $ref. 985 // If this doesn't get resolved then there will be an error definitely. 986 // same here: now if flatten doesn't resume, expand takes over 987 assert.NoError(t, GenerateClient("foo", nil, nil, opts)) 988 } 989 990 // This tests that securityDefinitions generate stable code 991 func TestBuilder_Issue1214(t *testing.T) { 992 defer discardOutput()() 993 994 dr := testCwd(t) 995 const any = `(.|\n)+` 996 997 opts := &GenOpts{ 998 Spec: filepath.FromSlash("../fixtures/bugs/1214/fixture-1214.yaml"), 999 IncludeModel: true, 1000 IncludeHandler: true, 1001 IncludeParameters: true, 1002 IncludeResponses: true, 1003 IncludeMain: true, 1004 APIPackage: "restapi", 1005 ModelPackage: "model", 1006 ServerPackage: "server", 1007 ClientPackage: "client", 1008 Target: dr, 1009 IsClient: false, 1010 } 1011 require.NoError(t, opts.EnsureDefaults()) 1012 1013 appGen, e := newAppGenerator("fixture-1214", nil, nil, opts) 1014 require.NoError(t, e) 1015 1016 op, e := appGen.makeCodegenApp() 1017 require.NoError(t, e) 1018 1019 for i := 0; i < 5; i++ { 1020 buf := bytes.NewBuffer(nil) 1021 err := templates.MustGet("serverConfigureapi").Execute(buf, op) 1022 require.NoError(t, err) 1023 1024 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("fixture_1214_configure_api.go", buf.Bytes()) 1025 require.NoErrorf(t, err, buf.String()) 1026 1027 res := string(ff) 1028 assertRegexpInCode(t, any+ 1029 `api\.AAuth = func\(user string, pass string\)`+any+ 1030 `api\.BAuth = func\(token string\)`+any+ 1031 `api\.CAuth = func\(token string\)`+any+ 1032 `api\.DAuth = func\(token string\)`+any+ 1033 `api\.EAuth = func\(token string, scopes \[\]string\)`+any, res) 1034 1035 buf = bytes.NewBuffer(nil) 1036 require.NoError(t, opts.templates.MustGet("serverBuilder").Execute(buf, op)) 1037 1038 ff, err = appGen.GenOpts.LanguageOpts.FormatContent("fixture_1214_server.go", buf.Bytes()) 1039 require.NoErrorf(t, err, buf.String()) 1040 1041 res = string(ff) 1042 assertRegexpInCode(t, any+ 1043 `AAuth: func\(user string, pass string\) \(interface{}, error\) {`+any+ 1044 `BAuth: func\(token string\) \(interface{}, error\) {`+any+ 1045 `CAuth: func\(token string\) \(interface{}, error\) {`+any+ 1046 `DAuth: func\(token string\) \(interface{}, error\) {`+any+ 1047 `EAuth: func\(token string, scopes \[\]string\) \(interface{}, error\) {`+any+ 1048 1049 `AAuth func\(string, string\) \(interface{}, error\)`+any+ 1050 `BAuth func\(string\) \(interface{}, error\)`+any+ 1051 `CAuth func\(string\) \(interface{}, error\)`+any+ 1052 `DAuth func\(string\) \(interface{}, error\)`+any+ 1053 `EAuth func\(string, \[\]string\) \(interface{}, error\)`+any+ 1054 1055 `if o\.AAuth == nil {`+any+ 1056 `unregistered = append\(unregistered, "AAuth"\)`+any+ 1057 `if o\.BAuth == nil {`+any+ 1058 `unregistered = append\(unregistered, "K1Auth"\)`+any+ 1059 `if o\.CAuth == nil {`+any+ 1060 `unregistered = append\(unregistered, "K2Auth"\)`+any+ 1061 `if o\.DAuth == nil {`+any+ 1062 `unregistered = append\(unregistered, "K3Auth"\)`+any+ 1063 `if o\.EAuth == nil {`+any+ 1064 `unregistered = append\(unregistered, "EAuth"\)`+any+ 1065 1066 `case "A":`+any+ 1067 `case "B":`+any+ 1068 `case "C":`+any+ 1069 `case "D":`+any+ 1070 `case "E":`+any, res) 1071 } 1072 } 1073 1074 func TestGenSecurityRequirements(t *testing.T) { 1075 for i := 0; i < 5; i++ { 1076 operation := "asecOp" 1077 b, err := opBuilder(operation, "../fixtures/bugs/1214/fixture-1214.yaml") 1078 require.NoError(t, err) 1079 1080 b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation) 1081 genRequirements := b.makeSecurityRequirements("o") 1082 assert.Len(t, genRequirements, 2) 1083 assert.Equal(t, []GenSecurityRequirements{ 1084 { 1085 GenSecurityRequirement{ 1086 Name: "A", 1087 Scopes: []string{}, 1088 }, 1089 GenSecurityRequirement{ 1090 Name: "B", 1091 Scopes: []string{}, 1092 }, 1093 GenSecurityRequirement{ 1094 Name: "E", 1095 Scopes: []string{"s0", "s1", "s2", "s3", "s4"}, 1096 }, 1097 }, 1098 { 1099 GenSecurityRequirement{ 1100 Name: "C", 1101 Scopes: []string{}, 1102 }, 1103 GenSecurityRequirement{ 1104 Name: "D", 1105 Scopes: []string{}, 1106 }, 1107 GenSecurityRequirement{ 1108 Name: "E", 1109 Scopes: []string{"s5", "s6", "s7", "s8", "s9"}, 1110 }, 1111 }, 1112 }, genRequirements) 1113 1114 operation = "bsecOp" 1115 b, err = opBuilder(operation, "../fixtures/bugs/1214/fixture-1214.yaml") 1116 require.NoError(t, err) 1117 1118 b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation) 1119 genRequirements = b.makeSecurityRequirements("o") 1120 assert.Len(t, genRequirements, 2) 1121 assert.Equal(t, []GenSecurityRequirements{ 1122 { 1123 GenSecurityRequirement{ 1124 Name: "A", 1125 Scopes: []string{}, 1126 }, 1127 GenSecurityRequirement{ 1128 Name: "E", 1129 Scopes: []string{"s0", "s1", "s2", "s3", "s4"}, 1130 }, 1131 }, 1132 { 1133 GenSecurityRequirement{ 1134 Name: "D", 1135 Scopes: []string{}, 1136 }, 1137 GenSecurityRequirement{ 1138 Name: "E", 1139 Scopes: []string{"s5", "s6", "s7", "s8", "s9"}, 1140 }, 1141 }, 1142 }, genRequirements) 1143 } 1144 1145 operation := "csecOp" 1146 b, err := opBuilder(operation, "../fixtures/bugs/1214/fixture-1214.yaml") 1147 require.NoError(t, err) 1148 1149 b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation) 1150 genRequirements := b.makeSecurityRequirements("o") 1151 assert.NotNil(t, genRequirements) 1152 assert.Len(t, genRequirements, 0) 1153 1154 operation = "nosecOp" 1155 b, err = opBuilder(operation, "../fixtures/bugs/1214/fixture-1214-2.yaml") 1156 require.NoError(t, err) 1157 1158 b.Security = b.Analyzed.SecurityRequirementsFor(&b.Operation) 1159 genRequirements = b.makeSecurityRequirements("o") 1160 assert.Nil(t, genRequirements) 1161 } 1162 1163 func TestGenerateServerOperation(t *testing.T) { 1164 defer discardOutput()() 1165 1166 fname := "../fixtures/codegen/todolist.simple.yml" 1167 1168 tgt, _ := ioutil.TempDir(filepath.Dir(fname), "generated") 1169 defer func() { 1170 _ = os.RemoveAll(tgt) 1171 }() 1172 o := &GenOpts{ 1173 ValidateSpec: false, 1174 IncludeModel: true, 1175 IncludeHandler: true, 1176 IncludeParameters: true, 1177 IncludeResponses: true, 1178 ModelPackage: "models", 1179 Spec: fname, 1180 Target: tgt, 1181 } 1182 require.NoError(t, o.EnsureDefaults()) 1183 1184 require.Error(t, GenerateServerOperation([]string{"createTask"}, nil)) 1185 1186 d := o.TemplateDir 1187 o.TemplateDir = "./nowhere" 1188 require.Error(t, GenerateServerOperation([]string{"notFound"}, o)) 1189 1190 o.TemplateDir = d 1191 d = o.Spec 1192 o.Spec = "nowhere.yaml" 1193 require.Error(t, GenerateServerOperation([]string{"notFound"}, o)) 1194 1195 o.Spec = d 1196 require.Error(t, GenerateServerOperation([]string{"notFound"}, o)) 1197 1198 require.NoError(t, GenerateServerOperation([]string{"createTask"}, o)) 1199 1200 // check expected files are generated and that's it 1201 _, err := os.Stat(filepath.Join(tgt, "tasks", "create_task.go")) 1202 assert.NoError(t, err) 1203 _, err = os.Stat(filepath.Join(tgt, "tasks", "create_task_parameters.go")) 1204 assert.NoError(t, err) 1205 _, err = os.Stat(filepath.Join(tgt, "tasks", "create_task_responses.go")) 1206 assert.NoError(t, err) 1207 1208 origStdout := os.Stdout 1209 defer func() { 1210 os.Stdout = origStdout 1211 }() 1212 os.Stdout, _ = os.Create(filepath.Join(tgt, "stdout")) 1213 o.DumpData = true 1214 // just checks this does not fail 1215 err = GenerateServerOperation([]string{"createTask"}, o) 1216 assert.NoError(t, err) 1217 _, err = os.Stat(filepath.Join(tgt, "stdout")) 1218 assert.NoError(t, err) 1219 } 1220 1221 // This tests that mimetypes generate stable code 1222 func TestBuilder_Issue1646(t *testing.T) { 1223 defer discardOutput()() 1224 1225 dr := testCwd(t) 1226 1227 opts := &GenOpts{ 1228 Spec: filepath.FromSlash("../fixtures/bugs/1646/fixture-1646.yaml"), 1229 IncludeModel: true, 1230 IncludeHandler: true, 1231 IncludeParameters: true, 1232 IncludeResponses: true, 1233 IncludeMain: true, 1234 APIPackage: "restapi", 1235 ModelPackage: "model", 1236 ServerPackage: "server", 1237 ClientPackage: "client", 1238 Target: dr, 1239 IsClient: false, 1240 } 1241 err := opts.EnsureDefaults() 1242 require.NoError(t, err) 1243 appGen, err := newAppGenerator("fixture-1646", nil, nil, opts) 1244 require.NoError(t, err) 1245 1246 preCons, preConj := appGen.makeConsumes() 1247 preProds, preProdj := appGen.makeProduces() 1248 assert.True(t, preConj) 1249 assert.True(t, preProdj) 1250 for i := 0; i < 5; i++ { 1251 cons, conj := appGen.makeConsumes() 1252 prods, prodj := appGen.makeProduces() 1253 assert.True(t, conj) 1254 assert.True(t, prodj) 1255 assert.Equal(t, preConj, conj) 1256 assert.Equal(t, preProdj, prodj) 1257 assert.Equal(t, preCons, cons) 1258 assert.Equal(t, preProds, prods) 1259 } 1260 } 1261 1262 func TestGenServer_StrictAdditionalProperties(t *testing.T) { 1263 defer discardOutput()() 1264 1265 dr := testCwd(t) 1266 1267 opts := &GenOpts{ 1268 Spec: filepath.FromSlash("../fixtures/codegen/strict-additional-properties.yml"), 1269 IncludeModel: true, 1270 IncludeHandler: true, 1271 IncludeParameters: true, 1272 IncludeResponses: true, 1273 IncludeMain: true, 1274 APIPackage: "restapi", 1275 ModelPackage: "model", 1276 ServerPackage: "server", 1277 ClientPackage: "client", 1278 Target: dr, 1279 IsClient: false, 1280 } 1281 err := opts.EnsureDefaults() 1282 require.NoError(t, err) 1283 1284 opts.StrictAdditionalProperties = true 1285 1286 appGen, err := newAppGenerator("StrictAdditionalProperties", nil, nil, opts) 1287 require.NoError(t, err) 1288 1289 op, err := appGen.makeCodegenApp() 1290 require.NoError(t, err) 1291 1292 buf := bytes.NewBuffer(nil) 1293 err = templates.MustGet("serverOperation").Execute(buf, op.Operations[0]) 1294 require.NoError(t, err) 1295 1296 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("strictAdditionalProperties.go", buf.Bytes()) 1297 require.NoErrorf(t, err, buf.String()) 1298 1299 res := string(ff) 1300 for _, tt := range []struct { 1301 name string 1302 assertion func(testing.TB, string, string) bool 1303 }{ 1304 {"PostTestBody", assertInCode}, 1305 {"PostTestParamsBodyExplicit", assertInCode}, 1306 {"PostTestParamsBodyImplicit", assertInCode}, 1307 {"PostTestParamsBodyDisabled", assertNotInCode}, 1308 } { 1309 fn := funcBody(res, "*"+tt.name+") UnmarshalJSON(data []byte) error") 1310 require.NotEmpty(t, fn, "Method UnmarshalJSON should be defined for type *"+tt.name) 1311 tt.assertion(t, "dec.DisallowUnknownFields()", fn) 1312 } 1313 } 1314 1315 func makeClientTimeoutNameTest() []struct { 1316 seenIds map[string]interface{} 1317 name string 1318 expected string 1319 } { 1320 return []struct { 1321 seenIds map[string]interface{} 1322 name string 1323 expected string 1324 }{ 1325 { 1326 seenIds: nil, 1327 name: "witness", 1328 expected: "witness", 1329 }, 1330 { 1331 seenIds: map[string]interface{}{ 1332 "id": true, 1333 }, 1334 name: "timeout", 1335 expected: "timeout", 1336 }, 1337 { 1338 seenIds: map[string]interface{}{ 1339 "timeout": true, 1340 "requesttimeout": true, 1341 }, 1342 name: "timeout", 1343 expected: "httpRequestTimeout", 1344 }, 1345 { 1346 seenIds: map[string]interface{}{ 1347 "timeout": true, 1348 "requesttimeout": true, 1349 "httprequesttimeout": true, 1350 "swaggertimeout": true, 1351 "operationtimeout": true, 1352 "optimeout": true, 1353 }, 1354 name: "timeout", 1355 expected: "operTimeout", 1356 }, 1357 { 1358 seenIds: map[string]interface{}{ 1359 "timeout": true, 1360 "requesttimeout": true, 1361 "httprequesttimeout": true, 1362 "swaggertimeout": true, 1363 "operationtimeout": true, 1364 "optimeout": true, 1365 "opertimeout": true, 1366 "opertimeout1": true, 1367 }, 1368 name: "timeout", 1369 expected: "operTimeout11", 1370 }, 1371 } 1372 } 1373 1374 func TestRenameTimeout(t *testing.T) { 1375 for idx, toPin := range makeClientTimeoutNameTest() { 1376 i := idx 1377 testCase := toPin 1378 t.Run(testCase.name, func(t *testing.T) { 1379 t.Parallel() 1380 assert.Equalf(t, testCase.expected, renameTimeout(testCase.seenIds, testCase.name), "unexpected deconflicting value [%d]", i) 1381 }) 1382 } 1383 } 1384 1385 func testInvalidParams() map[string]spec.Parameter { 1386 return map[string]spec.Parameter{ 1387 "query#param1": *spec.QueryParam("param1"), 1388 "path#param1": *spec.PathParam("param1"), 1389 "body#param1": *spec.BodyParam("param1", &spec.Schema{}), 1390 } 1391 } 1392 1393 func TestParamMappings(t *testing.T) { 1394 // Test deconfliction of duplicate param names across param locations 1395 mappings, _ := paramMappings(testInvalidParams()) 1396 require.Contains(t, mappings, "query") 1397 require.Contains(t, mappings, "path") 1398 require.Contains(t, mappings, "body") 1399 q := mappings["query"] 1400 p := mappings["path"] 1401 b := mappings["body"] 1402 require.Len(t, q, 1) 1403 require.Len(t, p, 1) 1404 require.Len(t, b, 1) 1405 require.Containsf(t, q, "param1", "unexpected content of %#v", q) 1406 require.Containsf(t, p, "param1", "unexpected content of %#v", p) 1407 require.Containsf(t, b, "param1", "unexpected content of %#v", b) 1408 assert.Equalf(t, "QueryParam1", q["param1"], "unexpected content of %#v", q["param1"]) 1409 assert.Equalf(t, "PathParam1", p["param1"], "unexpected content of %#v", p["param1"]) 1410 assert.Equalf(t, "BodyParam1", b["param1"], "unexpected content of %#v", b["param1"]) 1411 } 1412 1413 func TestDeconflictTag(t *testing.T) { 1414 assert.Equal(t, "runtimeops", deconflictTag(nil, "runtime")) 1415 assert.Equal(t, "apiops", deconflictTag([]string{"tag1"}, "api")) 1416 assert.Equal(t, "apiops1", deconflictTag([]string{"tag1", "apiops"}, "api")) 1417 assert.Equal(t, "tlsops", deconflictTag([]string{"tag1"}, "tls")) 1418 assert.Equal(t, "mytag", deconflictTag([]string{"tag1", "apiops"}, "mytag")) 1419 1420 assert.Equal(t, "operationsops", renameOperationPackage([]string{"tag1"}, "operations")) 1421 assert.Equal(t, "operationsops11", renameOperationPackage([]string{"tag1", "operationsops1", "operationsops"}, "operations")) 1422 } 1423 1424 func TestGenServer_2161_panic(t *testing.T) { 1425 t.Parallel() 1426 defer discardOutput()() 1427 1428 generated, err := ioutil.TempDir(testCwd(t), "generated_2161") 1429 require.NoError(t, err) 1430 1431 defer func() { 1432 _ = os.RemoveAll(generated) 1433 }() 1434 1435 opts := &GenOpts{ 1436 Spec: filepath.FromSlash("../fixtures/bugs/2161/fixture-2161-panic.json"), 1437 IncludeModel: true, 1438 IncludeHandler: true, 1439 IncludeParameters: true, 1440 IncludeResponses: true, 1441 IncludeMain: true, 1442 APIPackage: "restapi", 1443 ModelPackage: "model", 1444 ServerPackage: "server", 1445 ClientPackage: "client", 1446 Target: generated, 1447 IsClient: false, 1448 StrictAdditionalProperties: true, 1449 } 1450 require.NoError(t, opts.EnsureDefaults()) 1451 1452 appGen, err := newAppGenerator("inlinedSubtype", nil, nil, opts) 1453 require.NoError(t, err) 1454 1455 op, err := appGen.makeCodegenApp() 1456 require.NoError(t, err) 1457 1458 buf := bytes.NewBuffer(nil) 1459 var selectedOp int 1460 for i := range op.Operations { 1461 if op.Operations[i].Name == "configuration_update_configuration_module" { 1462 selectedOp = i 1463 } 1464 } 1465 require.NotEmpty(t, selectedOp, "dev error: invalid test vs fixture") 1466 1467 require.NoError(t, opts.templates.MustGet("serverOperation").Execute(buf, op.Operations[selectedOp])) 1468 1469 _, err = appGen.GenOpts.LanguageOpts.FormatContent(op.Operations[selectedOp].Name+".go", buf.Bytes()) 1470 require.NoErrorf(t, err, buf.String()) 1471 // NOTE(fred): I know that the generated model is wrong from this spec at the moment. 1472 // The test with this fix simply asserts that there is no panic / internal error with building this. 1473 } 1474 1475 func TestGenServer_1659_Principal(t *testing.T) { 1476 defer discardOutput()() 1477 1478 dr := testCwd(t) 1479 1480 for _, toPin := range []struct { 1481 Title string 1482 Opts *GenOpts 1483 Expected map[string][]string 1484 NotExpected map[string][]string 1485 }{ 1486 { 1487 Title: "default", 1488 Opts: &GenOpts{ 1489 Spec: filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"), 1490 IncludeHandler: true, 1491 IncludeParameters: true, 1492 IncludeResponses: true, 1493 IncludeMain: false, 1494 APIPackage: "restapi", 1495 ModelPackage: "models", 1496 ServerPackage: "server", 1497 ClientPackage: "client", 1498 Target: dr, 1499 IsClient: false, 1500 }, 1501 Expected: map[string][]string{ 1502 "configure": { 1503 `if api.ApikeyAuth == nil {`, 1504 `api.ApikeyAuth = func(token string) (interface{}, error) {`, 1505 `if api.BasicAuth == nil {`, 1506 `api.BasicAuth = func(user string, pass string) (interface{}, error) {`, 1507 `if api.PetstoreAuthAuth == nil {`, 1508 `api.PetstoreAuthAuth = func(token string, scopes []string) (interface{}, error) {`, 1509 `api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal interface{}) middleware.Responder {`, 1510 `api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`, 1511 }, 1512 "get": { 1513 `type GetHandlerFunc func(GetParams, interface{}) middleware.Responder`, 1514 `func (fn GetHandlerFunc) Handle(params GetParams, principal interface{}) middleware.Responder {`, 1515 `return fn(params, principal)`, 1516 `type GetHandler interface {`, 1517 `Handle(GetParams, interface{}) middleware.Responder`, 1518 `uprinc, aCtx, err := o.Context.Authorize(r, route)`, 1519 `if uprinc != nil {`, 1520 `principal = uprinc.(interface{})`, 1521 `res := o.Handler.Handle(Params, principal)`, 1522 }, 1523 "post": { 1524 `type PostHandlerFunc func(PostParams) middleware.Responder`, 1525 `return fn(params)`, 1526 `type PostHandler interface {`, 1527 `Handle(PostParams) middleware.Responder`, 1528 `res := o.Handler.Handle(Params)`, 1529 }, 1530 }, 1531 NotExpected: map[string][]string{ 1532 "post": { 1533 `uprinc, aCtx, err := o.Context.Authorize(r, route)`, 1534 `principal = uprinc.(interface{})`, 1535 }, 1536 }, 1537 }, 1538 { 1539 Title: "principal is struct", 1540 Opts: &GenOpts{ 1541 Spec: filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"), 1542 IncludeHandler: true, 1543 IncludeParameters: true, 1544 IncludeResponses: true, 1545 IncludeMain: false, 1546 APIPackage: "restapi", 1547 ModelPackage: "models", 1548 ServerPackage: "server", 1549 ClientPackage: "client", 1550 Target: dr, 1551 Principal: "github.com/example/security.Principal", 1552 IsClient: false, 1553 }, 1554 Expected: map[string][]string{ 1555 "configure": { 1556 `auth "github.com/example/security"`, 1557 `if api.ApikeyAuth == nil {`, 1558 `api.ApikeyAuth = func(token string) (*auth.Principal, error) {`, 1559 `if api.BasicAuth == nil {`, 1560 `api.BasicAuth = func(user string, pass string) (*auth.Principal, error) {`, 1561 `if api.PetstoreAuthAuth == nil {`, 1562 `api.PetstoreAuthAuth = func(token string, scopes []string) (*auth.Principal, error) {`, 1563 `api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal *auth.Principal) middleware.Responder {`, 1564 `api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`, 1565 }, 1566 "get": { 1567 `type GetHandlerFunc func(GetParams, *auth.Principal) middleware.Responder`, 1568 `func (fn GetHandlerFunc) Handle(params GetParams, principal *auth.Principal) middleware.Responder {`, 1569 `return fn(params, principal)`, 1570 `type GetHandler interface {`, 1571 `Handle(GetParams, *auth.Principal) middleware.Responder`, 1572 `uprinc, aCtx, err := o.Context.Authorize(r, route)`, 1573 `if uprinc != nil {`, 1574 `principal = uprinc.(*auth.Principal)`, 1575 `res := o.Handler.Handle(Params, principal)`, 1576 }, 1577 "post": { 1578 `type PostHandlerFunc func(PostParams) middleware.Responder`, 1579 `return fn(params)`, 1580 `type PostHandler interface {`, 1581 `Handle(PostParams) middleware.Responder`, 1582 `res := o.Handler.Handle(Params)`, 1583 }, 1584 }, 1585 NotExpected: map[string][]string{ 1586 "post": { 1587 `uprinc, aCtx, err := o.Context.Authorize(r, route)`, 1588 `principal = uprinc.(`, 1589 }, 1590 }, 1591 }, 1592 { 1593 Title: "principal is interface", 1594 Opts: &GenOpts{ 1595 Spec: filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"), 1596 IncludeHandler: true, 1597 IncludeParameters: true, 1598 IncludeResponses: true, 1599 IncludeMain: false, 1600 APIPackage: "restapi", 1601 ModelPackage: "models", 1602 ServerPackage: "server", 1603 ClientPackage: "client", 1604 Target: dr, 1605 Principal: "github.com/example/security.PrincipalIface", 1606 IsClient: false, 1607 PrincipalCustomIface: true, 1608 }, 1609 Expected: map[string][]string{ 1610 "configure": { 1611 `auth "github.com/example/security"`, 1612 `if api.ApikeyAuth == nil {`, 1613 `api.ApikeyAuth = func(token string) (auth.PrincipalIface, error) {`, 1614 `if api.BasicAuth == nil {`, 1615 `api.BasicAuth = func(user string, pass string) (auth.PrincipalIface, error) {`, 1616 `if api.PetstoreAuthAuth == nil {`, 1617 `api.PetstoreAuthAuth = func(token string, scopes []string) (auth.PrincipalIface, error) {`, 1618 `api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal auth.PrincipalIface) middleware.Responder {`, 1619 `api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`, 1620 }, 1621 "get": { 1622 `type GetHandlerFunc func(GetParams, auth.PrincipalIface) middleware.Responder`, 1623 `func (fn GetHandlerFunc) Handle(params GetParams, principal auth.PrincipalIface) middleware.Responder {`, 1624 `return fn(params, principal)`, 1625 `type GetHandler interface {`, 1626 `Handle(GetParams, auth.PrincipalIface) middleware.Responder`, 1627 `uprinc, aCtx, err := o.Context.Authorize(r, route)`, 1628 `if uprinc != nil {`, 1629 `principal = uprinc.(auth.PrincipalIface)`, 1630 `res := o.Handler.Handle(Params, principal)`, 1631 }, 1632 "post": { 1633 `type PostHandlerFunc func(PostParams) middleware.Responder`, 1634 `return fn(params)`, 1635 `type PostHandler interface {`, 1636 `Handle(PostParams) middleware.Responder`, 1637 `res := o.Handler.Handle(Params)`, 1638 }, 1639 }, 1640 NotExpected: map[string][]string{ 1641 "post": { 1642 `uprinc, aCtx, err := o.Context.Authorize(r, route)`, 1643 `principal = uprinc.(`, 1644 }, 1645 }, 1646 }, 1647 { 1648 Title: "stratoscale: principal is struct", 1649 Opts: &GenOpts{ 1650 Spec: filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"), 1651 IncludeHandler: true, 1652 IncludeParameters: true, 1653 IncludeResponses: true, 1654 IncludeMain: false, 1655 APIPackage: "restapi", 1656 ModelPackage: "models", 1657 ServerPackage: "server", 1658 ClientPackage: "client", 1659 Target: dr, 1660 Principal: "github.com/example/security.Principal", 1661 IsClient: false, 1662 Template: "stratoscale", 1663 }, 1664 Expected: map[string][]string{ 1665 "configure": { 1666 `auth "github.com/example/security"`, 1667 `AuthApikey func(token string) (*auth.Principal, error)`, 1668 `AuthBasic func(user string, pass string) (*auth.Principal, error)`, 1669 `AuthPetstoreAuth func(token string, scopes []string) (*auth.Principal, error)`, 1670 `api.ApikeyAuth = func(token string) (*auth.Principal, error) {`, 1671 `if c.AuthApikey == nil {`, 1672 `panic("you specified a custom principal type, but did not provide the authenticator to provide this")`, 1673 `return c.AuthApikey(token)`, 1674 `api.BasicAuth = func(user string, pass string) (*auth.Principal, error) {`, 1675 `if c.AuthBasic == nil {`, 1676 `panic("you specified a custom principal type, but did not provide the authenticator to provide this")`, 1677 `return c.AuthBasic(user, pass)`, 1678 `api.PetstoreAuthAuth = func(token string, scopes []string) (*auth.Principal, error) {`, 1679 `if c.AuthPetstoreAuth == nil {`, 1680 `panic("you specified a custom principal type, but did not provide the authenticator to provide this")`, 1681 `return c.AuthPetstoreAuth(token, scopes)`, 1682 `api.APIAuthorizer = authorizer(c.Authorizer)`, 1683 `api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal *auth.Principal) middleware.Responder {`, 1684 `ctx = storeAuth(ctx, principal)`, 1685 `return c.RestapiAPI.Get(ctx, params)`, 1686 `api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`, 1687 `return c.RestapiAPI.Post(ctx, params)`, 1688 `func (a authorizer) Authorize(req *http.Request, principal interface{}) error {`, 1689 `ctx := storeAuth(req.Context(), principal)`, 1690 `func storeAuth(ctx context.Context, principal interface{})`, 1691 }, 1692 }, 1693 }, 1694 { 1695 Title: "stratoscale: principal is interface", 1696 Opts: &GenOpts{ 1697 Spec: filepath.FromSlash("../fixtures/enhancements/1659/fixture-1659.yaml"), 1698 IncludeHandler: true, 1699 IncludeParameters: true, 1700 IncludeResponses: true, 1701 IncludeMain: false, 1702 APIPackage: "restapi", 1703 ModelPackage: "models", 1704 ServerPackage: "server", 1705 ClientPackage: "client", 1706 Target: dr, 1707 Principal: "github.com/example/security.PrincipalIface", 1708 IsClient: false, 1709 PrincipalCustomIface: true, 1710 Template: "stratoscale", 1711 }, 1712 Expected: map[string][]string{ 1713 "configure": { 1714 `auth "github.com/example/security"`, 1715 `AuthApikey func(token string) (auth.PrincipalIface, error)`, 1716 `AuthBasic func(user string, pass string) (auth.PrincipalIface, error)`, 1717 `AuthPetstoreAuth func(token string, scopes []string) (auth.PrincipalIface, error)`, 1718 `api.ApikeyAuth = func(token string) (auth.PrincipalIface, error) {`, 1719 `if c.AuthApikey == nil {`, 1720 `panic("you specified a custom principal type, but did not provide the authenticator to provide this")`, 1721 `return c.AuthApikey(token)`, 1722 `api.BasicAuth = func(user string, pass string) (auth.PrincipalIface, error) {`, 1723 `if c.AuthBasic == nil {`, 1724 `panic("you specified a custom principal type, but did not provide the authenticator to provide this")`, 1725 `return c.AuthBasic(user, pass)`, 1726 `api.PetstoreAuthAuth = func(token string, scopes []string) (auth.PrincipalIface, error) {`, 1727 `if c.AuthPetstoreAuth == nil {`, 1728 `panic("you specified a custom principal type, but did not provide the authenticator to provide this")`, 1729 `return c.AuthPetstoreAuth(token, scopes)`, 1730 `api.APIAuthorizer = authorizer(c.Authorizer)`, 1731 `api.GetHandler =restapi.GetHandlerFunc(func(params restapi.GetParams, principal auth.PrincipalIface) middleware.Responder {`, 1732 `ctx = storeAuth(ctx, principal)`, 1733 `return c.RestapiAPI.Get(ctx, params)`, 1734 `api.PostHandler =restapi.PostHandlerFunc(func(params restapi.PostParams) middleware.Responder {`, 1735 `return c.RestapiAPI.Post(ctx, params)`, 1736 `func (a authorizer) Authorize(req *http.Request, principal interface{}) error {`, 1737 `ctx := storeAuth(req.Context(), principal)`, 1738 `func storeAuth(ctx context.Context, principal interface{})`, 1739 }, 1740 }, 1741 }, 1742 } { 1743 fixture := toPin 1744 t.Run(fixture.Title, func(t *testing.T) { 1745 t.Parallel() 1746 1747 opts := fixture.Opts 1748 require.NoError(t, opts.EnsureDefaults()) 1749 require.NoError(t, opts.setTemplates()) 1750 1751 appGen, err := newAppGenerator(fixture.Title, nil, nil, opts) 1752 require.NoError(t, err) 1753 1754 op, err := appGen.makeCodegenApp() 1755 require.NoError(t, err) 1756 1757 bufC := bytes.NewBuffer(nil) 1758 require.NoError(t, opts.templates.MustGet("serverConfigureapi").Execute(bufC, op)) 1759 1760 _, err = appGen.GenOpts.LanguageOpts.FormatContent("configure_api.go", bufC.Bytes()) 1761 require.NoErrorf(t, err, bufC.String()) 1762 1763 for _, line := range fixture.Expected["configure"] { 1764 assertInCode(t, line, bufC.String()) 1765 } 1766 for _, line := range fixture.NotExpected["configure"] { 1767 assertNotInCode(t, line, bufC.String()) 1768 } 1769 1770 for i := range op.Operations { 1771 bufO := bytes.NewBuffer(nil) 1772 require.NoError(t, opts.templates.MustGet("serverOperation").Execute(bufO, op.Operations[i])) 1773 1774 _, erf := appGen.GenOpts.LanguageOpts.FormatContent(op.Operations[i].Name+".go", bufO.Bytes()) 1775 require.NoErrorf(t, erf, bufO.String()) 1776 1777 for _, line := range fixture.Expected[op.Operations[i].Name] { 1778 assertInCode(t, line, bufO.String()) 1779 } 1780 for _, line := range fixture.NotExpected[op.Operations[i].Name] { 1781 assertNotInCode(t, line, bufO.String()) 1782 } 1783 } 1784 }) 1785 } 1786 }