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 15package generator 16 17import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "io/ioutil" 22 "log" 23 "os" 24 "path/filepath" 25 "testing" 26 27 "github.com/go-openapi/analysis" 28 "github.com/go-openapi/loads" 29 "github.com/go-openapi/spec" 30 "github.com/stretchr/testify/assert" 31) 32 33func TestUniqueOperationNames(t *testing.T) { 34 doc, err := loads.Spec("../fixtures/codegen/todolist.simple.yml") 35 if assert.NoError(t, err) { 36 sp := doc.Spec() 37 sp.Paths.Paths["/tasks"].Post.ID = "saveTask" 38 sp.Paths.Paths["/tasks"].Post.AddExtension("origName", "createTask") 39 sp.Paths.Paths["/tasks/{id}"].Put.ID = "saveTask" 40 sp.Paths.Paths["/tasks/{id}"].Put.AddExtension("origName", "updateTask") 41 analyzed := analysis.New(sp) 42 43 ops := gatherOperations(analyzed, nil) 44 assert.Len(t, ops, 6) 45 _, exists := ops["saveTask"] 46 assert.True(t, exists) 47 _, exists = ops["PutTasksID"] 48 assert.True(t, exists) 49 } 50} 51 52func TestEmptyOperationNames(t *testing.T) { 53 doc, err := loads.Spec("../fixtures/codegen/todolist.simple.yml") 54 if assert.NoError(t, err) { 55 sp := doc.Spec() 56 sp.Paths.Paths["/tasks"].Post.ID = "" 57 sp.Paths.Paths["/tasks"].Post.AddExtension("origName", "createTask") 58 sp.Paths.Paths["/tasks/{id}"].Put.ID = "" 59 sp.Paths.Paths["/tasks/{id}"].Put.AddExtension("origName", "updateTask") 60 analyzed := analysis.New(sp) 61 62 ops := gatherOperations(analyzed, nil) 63 assert.Len(t, ops, 6) 64 _, exists := ops["PostTasks"] 65 assert.True(t, exists) 66 _, exists = ops["PutTasksID"] 67 assert.True(t, exists) 68 } 69} 70 71func TestMakeResponseHeader(t *testing.T) { 72 b, err := opBuilder("getTasks", "") 73 if assert.NoError(t, err) { 74 hdr := findResponseHeader(&b.Operation, 200, "X-Rate-Limit") 75 gh, er := b.MakeHeader("a", "X-Rate-Limit", *hdr) 76 if assert.NoError(t, er) { 77 assert.True(t, gh.IsPrimitive) 78 assert.Equal(t, "int32", gh.GoType) 79 assert.Equal(t, "X-Rate-Limit", gh.Name) 80 } 81 } 82} 83 84func TestMakeResponseHeaderDefaultValues(t *testing.T) { 85 b, err := opBuilder("getTasks", "") 86 if assert.NoError(t, err) { 87 var testCases = []struct { 88 name string // input 89 typeStr string // expected type 90 defaultValue interface{} // expected result 91 }{ 92 {"Access-Control-Allow-Origin", "string", "*"}, 93 {"X-Rate-Limit", "int32", nil}, 94 {"X-Rate-Limit-Remaining", "int32", float64(42)}, 95 {"X-Rate-Limit-Reset", "int32", "1449875311"}, 96 {"X-Rate-Limit-Reset-Human", "string", "3 days"}, 97 {"X-Rate-Limit-Reset-Human-Number", "string", float64(3)}, 98 } 99 100 for _, tc := range testCases { 101 // t.Logf("tc: %+v", tc) 102 hdr := findResponseHeader(&b.Operation, 200, tc.name) 103 assert.NotNil(t, hdr) 104 gh, er := b.MakeHeader("a", tc.name, *hdr) 105 if assert.NoError(t, er) { 106 assert.True(t, gh.IsPrimitive) 107 assert.Equal(t, tc.typeStr, gh.GoType) 108 assert.Equal(t, tc.name, gh.Name) 109 assert.Exactly(t, tc.defaultValue, gh.Default) 110 } 111 } 112 } 113} 114 115func TestMakeResponse(t *testing.T) { 116 b, err := opBuilder("getTasks", "") 117 if assert.NoError(t, err) { 118 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 119 resolver.KnownDefs = make(map[string]struct{}) 120 for k := range b.Doc.Spec().Definitions { 121 resolver.KnownDefs[k] = struct{}{} 122 } 123 gO, err := b.MakeResponse("a", "getTasksSuccess", true, resolver, 200, b.Operation.Responses.StatusCodeResponses[200]) 124 if assert.NoError(t, err) { 125 assert.Len(t, gO.Headers, 6) 126 assert.NotNil(t, gO.Schema) 127 assert.True(t, gO.Schema.IsArray) 128 assert.NotNil(t, gO.Schema.Items) 129 assert.False(t, gO.Schema.IsAnonymous) 130 assert.Equal(t, "[]*models.Task", gO.Schema.GoType) 131 } 132 } 133} 134 135func TestMakeResponse_WithAllOfSchema(t *testing.T) { 136 b, err := methodPathOpBuilder("get", "/media/search", "../fixtures/codegen/instagram.yml") 137 if assert.NoError(t, err) { 138 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 139 resolver.KnownDefs = make(map[string]struct{}) 140 for k := range b.Doc.Spec().Definitions { 141 resolver.KnownDefs[k] = struct{}{} 142 } 143 gO, err := b.MakeResponse("a", "get /media/search", true, resolver, 200, b.Operation.Responses.StatusCodeResponses[200]) 144 if assert.NoError(t, err) { 145 if assert.NotNil(t, gO.Schema) { 146 assert.Equal(t, "GetMediaSearchBody", gO.Schema.GoType) 147 } 148 if assert.NotEmpty(t, b.ExtraSchemas) { 149 body := b.ExtraSchemas["GetMediaSearchBody"] 150 if assert.NotEmpty(t, body.Properties) { 151 prop := body.Properties[0] 152 assert.Equal(t, "data", prop.Name) 153 assert.Equal(t, "[]*DataItems0", prop.GoType) 154 } 155 items := b.ExtraSchemas["DataItems0"] 156 if assert.NotEmpty(t, items.AllOf) { 157 media := items.AllOf[0] 158 assert.Equal(t, "models.Media", media.GoType) 159 } 160 } 161 } 162 } 163} 164 165func TestMakeOperationParam(t *testing.T) { 166 b, err := opBuilder("getTasks", "") 167 if assert.NoError(t, err) { 168 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 169 gO, err := b.MakeParameter("a", resolver, b.Operation.Parameters[0], nil) 170 if assert.NoError(t, err) { 171 assert.Equal(t, "size", gO.Name) 172 assert.True(t, gO.IsPrimitive) 173 } 174 } 175} 176 177func TestMakeOperationParamItem(t *testing.T) { 178 b, err := opBuilder("arrayQueryParams", "../fixtures/codegen/todolist.arrayquery.yml") 179 if assert.NoError(t, err) { 180 resolver := &typeResolver{ModelsPackage: b.ModelsPackage, Doc: b.Doc} 181 gO, err := b.MakeParameterItem("a", "siString", "ii", "siString", "a.SiString", "query", resolver, b.Operation.Parameters[1].Items, nil) 182 if assert.NoError(t, err) { 183 assert.Nil(t, gO.Parent) 184 assert.True(t, gO.IsPrimitive) 185 } 186 } 187} 188 189func TestMakeOperation(t *testing.T) { 190 b, err := opBuilder("getTasks", "") 191 if assert.NoError(t, err) { 192 gO, err := b.MakeOperation() 193 if assert.NoError(t, err) { 194 //pretty.Println(gO) 195 assert.Equal(t, "getTasks", gO.Name) 196 assert.Equal(t, "GET", gO.Method) 197 assert.Equal(t, "/tasks", gO.Path) 198 assert.Len(t, gO.Params, 2) 199 assert.Len(t, gO.Responses, 1) 200 assert.NotNil(t, gO.DefaultResponse) 201 assert.NotNil(t, gO.SuccessResponse) 202 } 203 204 // TODO: validate rendering of a complex operation 205 } 206} 207 208func TestRenderOperation_InstagramSearch(t *testing.T) { 209 b, err := methodPathOpBuilder("get", "/media/search", "../fixtures/codegen/instagram.yml") 210 if assert.NoError(t, err) { 211 gO, err := b.MakeOperation() 212 if assert.NoError(t, err) { 213 buf := bytes.NewBuffer(nil) 214 opts := opts() 215 err := templates.MustGet("serverOperation").Execute(buf, gO) 216 if assert.NoError(t, err) { 217 ff, err := opts.LanguageOpts.FormatContent("operation.go", buf.Bytes()) 218 if assert.NoError(t, err) { 219 res := string(ff) 220 // fmt.Println(res) 221 assertInCode(t, "Data []*DataItems0 `json:\"data\"`", res) 222 assertInCode(t, "models.Media", res) 223 } else { 224 fmt.Println(buf.String()) 225 } 226 } 227 } 228 } 229} 230 231func methodPathOpBuilder(method, path, fname string) (codeGenOpBuilder, error) { 232 if fname == "" { 233 fname = "../fixtures/codegen/todolist.simple.yml" 234 } 235 236 specDoc, err := loads.Spec(fname) 237 if err != nil { 238 return codeGenOpBuilder{}, err 239 } 240 241 analyzed := analysis.New(specDoc.Spec()) 242 op, ok := analyzed.OperationFor(method, path) 243 if !ok { 244 return codeGenOpBuilder{}, errors.New("No operation could be found for " + method + " " + path) 245 } 246 247 return codeGenOpBuilder{ 248 Name: method + " " + path, 249 Method: method, 250 Path: path, 251 APIPackage: "restapi", 252 ModelsPackage: "models", 253 Principal: "models.User", 254 Target: ".", 255 Operation: *op, 256 Doc: specDoc, 257 Analyzed: analyzed, 258 Authed: false, 259 ExtraSchemas: make(map[string]GenSchema), 260 }, nil 261} 262 263func opBuilder(name, fname string) (codeGenOpBuilder, error) { 264 if fname == "" { 265 fname = "../fixtures/codegen/todolist.simple.yml" 266 } 267 268 specDoc, err := loads.Spec(fname) 269 if err != nil { 270 return codeGenOpBuilder{}, err 271 } 272 analyzed := analysis.New(specDoc.Spec()) 273 274 method, path, op, ok := analyzed.OperationForName(name) 275 if !ok { 276 return codeGenOpBuilder{}, errors.New("No operation could be found for " + name) 277 } 278 279 return codeGenOpBuilder{ 280 Name: name, 281 Method: method, 282 Path: path, 283 BasePath: specDoc.BasePath(), 284 APIPackage: "restapi", 285 ModelsPackage: "models", 286 Principal: "models.User", 287 Target: ".", 288 Operation: *op, 289 Doc: specDoc, 290 Analyzed: analyzed, 291 Authed: false, 292 ExtraSchemas: make(map[string]GenSchema), 293 }, nil 294} 295 296func findResponseHeader(op *spec.Operation, code int, name string) *spec.Header { 297 resp := op.Responses.Default 298 if code > 0 { 299 bb, ok := op.Responses.StatusCodeResponses[code] 300 if ok { 301 resp = &bb 302 } 303 } 304 305 if resp == nil { 306 return nil 307 } 308 309 hdr, ok := resp.Headers[name] 310 if !ok { 311 return nil 312 } 313 314 return &hdr 315} 316 317func TestDateFormat_Spec1(t *testing.T) { 318 b, err := opBuilder("putTesting", "../fixtures/bugs/193/spec1.json") 319 if assert.NoError(t, err) { 320 op, err := b.MakeOperation() 321 if assert.NoError(t, err) { 322 buf := bytes.NewBuffer(nil) 323 opts := opts() 324 opts.defaultsEnsured = false 325 opts.EnsureDefaults(true) 326 err := templates.MustGet("clientParameter").Execute(buf, op) 327 if assert.NoError(t, err) { 328 ff, err := opts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 329 if assert.NoError(t, err) { 330 res := string(ff) 331 assertInCode(t, "frTestingThis.String()", res) 332 } else { 333 fmt.Println(buf.String()) 334 } 335 } 336 } 337 } 338} 339 340func TestDateFormat_Spec2(t *testing.T) { 341 b, err := opBuilder("putTesting", "../fixtures/bugs/193/spec2.json") 342 if assert.NoError(t, err) { 343 op, err := b.MakeOperation() 344 if assert.NoError(t, err) { 345 buf := bytes.NewBuffer(nil) 346 opts := opts() 347 opts.defaultsEnsured = false 348 opts.EnsureDefaults(true) 349 err := templates.MustGet("clientParameter").Execute(buf, op) 350 if assert.NoError(t, err) { 351 ff, err := opts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 352 if assert.NoError(t, err) { 353 res := string(ff) 354 assertInCode(t, "valuesTestingThis = append(valuesTestingThis, v.String())", res) 355 } else { 356 fmt.Println(buf.String()) 357 } 358 } 359 } 360 } 361} 362 363func TestBuilder_Issue287(t *testing.T) { 364 log.SetOutput(ioutil.Discard) 365 defer log.SetOutput(os.Stderr) 366 dr, _ := os.Getwd() 367 368 opts := &GenOpts{ 369 Spec: filepath.FromSlash("../fixtures/bugs/287/swagger.yml"), 370 IncludeModel: true, 371 IncludeValidator: true, 372 IncludeHandler: true, 373 IncludeParameters: true, 374 IncludeResponses: true, 375 IncludeMain: true, 376 APIPackage: "restapi", 377 ModelPackage: "model", 378 ServerPackage: "server", 379 ClientPackage: "client", 380 Target: dr, 381 } 382 opts.EnsureDefaults(false) 383 appGen, err := newAppGenerator("plainTexter", nil, nil, opts) 384 if assert.NoError(t, err) { 385 op, err := appGen.makeCodegenApp() 386 if assert.NoError(t, err) { 387 buf := bytes.NewBuffer(nil) 388 err := templates.MustGet("serverBuilder").Execute(buf, op) 389 if assert.NoError(t, err) { 390 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 391 if assert.NoError(t, err) { 392 res := string(ff) 393 assertInCode(t, "case \"text/plain\":", res) 394 } else { 395 fmt.Println(buf.String()) 396 } 397 } 398 } 399 } 400} 401 402func TestBuilder_Issue465(t *testing.T) { 403 log.SetOutput(ioutil.Discard) 404 defer log.SetOutput(os.Stderr) 405 dr, _ := os.Getwd() 406 opts := &GenOpts{ 407 Spec: filepath.FromSlash("../fixtures/bugs/465/swagger.yml"), 408 IncludeModel: true, 409 IncludeValidator: true, 410 IncludeHandler: true, 411 IncludeParameters: true, 412 IncludeResponses: true, 413 IncludeMain: true, 414 APIPackage: "restapi", 415 ModelPackage: "model", 416 ServerPackage: "server", 417 ClientPackage: "client", 418 Target: dr, 419 } 420 opts.EnsureDefaults(true) 421 appGen, err := newAppGenerator("plainTexter", nil, nil, opts) 422 if assert.NoError(t, err) { 423 op, err := appGen.makeCodegenApp() 424 if assert.NoError(t, err) { 425 buf := bytes.NewBuffer(nil) 426 err := templates.MustGet("clientFacade").Execute(buf, op) 427 if assert.NoError(t, err) { 428 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 429 if assert.NoError(t, err) { 430 res := string(ff) 431 assertInCode(t, "/v1/fancyAPI", res) 432 } else { 433 fmt.Println(buf.String()) 434 } 435 } 436 } 437 } 438} 439 440func TestBuilder_Issue500(t *testing.T) { 441 log.SetOutput(ioutil.Discard) 442 defer log.SetOutput(os.Stderr) 443 dr, _ := os.Getwd() 444 opts := &GenOpts{ 445 Spec: filepath.FromSlash("../fixtures/bugs/500/swagger.yml"), 446 IncludeModel: true, 447 IncludeValidator: true, 448 IncludeHandler: true, 449 IncludeParameters: true, 450 IncludeResponses: true, 451 IncludeMain: true, 452 APIPackage: "restapi", 453 ModelPackage: "model", 454 ServerPackage: "server", 455 ClientPackage: "client", 456 Target: dr, 457 } 458 opts.EnsureDefaults(false) 459 appGen, err := newAppGenerator("multiTags", nil, nil, opts) 460 if assert.NoError(t, err) { 461 op, err := appGen.makeCodegenApp() 462 if assert.NoError(t, err) { 463 buf := bytes.NewBuffer(nil) 464 err := templates.MustGet("serverBuilder").Execute(buf, op) 465 if assert.NoError(t, err) { 466 ff, err := appGen.GenOpts.LanguageOpts.FormatContent("put_testing.go", buf.Bytes()) 467 if assert.NoError(t, err) { 468 res := string(ff) 469 assertNotInCode(t, `o.handlers["GET"]["/payment/{invoice_id}/payments/{payment_id}"] = invoices.NewGetPaymentByID(o.context, o.InvoicesGetPaymentByIDHandler)`, res) 470 assertInCode(t, `o.handlers["GET"]["/payment/{invoice_id}/payments/{payment_id}"] = NewGetPaymentByID(o.context, o.GetPaymentByIDHandler)`, res) 471 } else { 472 fmt.Println(buf.String()) 473 } 474 } 475 } 476 } 477} 478 479func TestGenClient_IllegalBOM(t *testing.T) { 480 b, err := methodPathOpBuilder("get", "/v3/attachments/{attachmentId}", "../fixtures/bugs/727/swagger.json") 481 if assert.NoError(t, err) { 482 op, err := b.MakeOperation() 483 if assert.NoError(t, err) { 484 buf := bytes.NewBuffer(nil) 485 opts := opts() 486 opts.defaultsEnsured = false 487 opts.EnsureDefaults(true) 488 err := templates.MustGet("clientResponse").Execute(buf, op) 489 assert.NoError(t, err) 490 } 491 } 492} 493 494func TestGenClient_CustomFormatPath(t *testing.T) { 495 b, err := methodPathOpBuilder("get", "/mosaic/experimental/series/{SeriesId}/mosaics", "../fixtures/bugs/789/swagger.yml") 496 if assert.NoError(t, err) { 497 op, err := b.MakeOperation() 498 if assert.NoError(t, err) { 499 buf := bytes.NewBuffer(nil) 500 opts := opts() 501 opts.defaultsEnsured = false 502 opts.EnsureDefaults(true) 503 err := templates.MustGet("clientParameter").Execute(buf, op) 504 if assert.NoError(t, err) { 505 assertInCode(t, `if err := r.SetPathParam("SeriesId", o.SeriesID.String()); err != nil`, buf.String()) 506 } 507 } 508 } 509} 510 511func TestGenClient_Issue733(t *testing.T) { 512 b, err := opBuilder("get_characters_character_id_mail_mail_id", "../fixtures/bugs/733/swagger.json") 513 if assert.NoError(t, err) { 514 op, err := b.MakeOperation() 515 if assert.NoError(t, err) { 516 buf := bytes.NewBuffer(nil) 517 opts := opts() 518 opts.defaultsEnsured = false 519 opts.EnsureDefaults(true) 520 err := templates.MustGet("clientResponse").Execute(buf, op) 521 if assert.NoError(t, err) { 522 assertInCode(t, "Labels []*int64 `json:\"labels\"`", buf.String()) 523 } 524 } 525 } 526} 527