github.com/6543-forks/go-swagger@v0.26.0/generator/template_repo_test.go (about) 1 package generator 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "log" 7 "os" 8 "testing" 9 10 "github.com/go-openapi/loads" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 const ( 16 // Test template environment 17 singleTemplate = `test` 18 multipleDefinitions = `{{ define "T1" }}T1{{end}}{{ define "T2" }}T2{{end}}` 19 dependantTemplate = `{{ template "T1" }}D1` 20 cirularDeps1 = `{{ define "T1" }}{{ .Name }}: {{ range .Children }}{{ template "T2" . }}{{end}}{{end}}{{template "T1" . }}` 21 cirularDeps2 = `{{ define "T2" }}{{if .Recurse }}{{ template "T1" . }}{{ else }}Children{{end}}{{end}}` 22 customHeader = `custom header` 23 customMultiple = `{{define "bindprimitiveparam" }}custom primitive{{end}}` 24 customNewTemplate = `new template` 25 customExistingUsesNew = `{{define "bindprimitiveparam" }}{{ template "newtemplate" }}{{end}}` 26 ) 27 28 func testFuncTpl() string { 29 return ` 30 Pascalize={{ pascalize "WeArePonies_Of_the_round table" }} 31 Snakize={{ snakize "WeArePonies_Of_the_round table" }} 32 Humanize={{ humanize "WeArePonies_Of_the_round table" }} 33 PluralizeFirstWord={{ pluralizeFirstWord "pony of the round table" }} 34 PluralizeFirstOfOneWord={{ pluralizeFirstWord "dwarf" }} 35 PluralizeFirstOfNoWord={{ pluralizeFirstWord "" }} 36 DropPackage={{ dropPackage "prefix.suffix" }} 37 DropNoPackage={{ dropPackage "suffix" }} 38 DropEmptyPackage={{ dropPackage "" }} 39 ContainsString={{ contains .DependsOn "x"}} 40 DoesNotContainString={{ contains .DependsOn "y"}} 41 PadSurround1={{ padSurround "padme" "-" 3 12}} 42 PadSurround2={{ padSurround "padme" "-" 0 12}} 43 Json={{ json .DefaultImports }} 44 PrettyJson={{ prettyjson . }} 45 Snakize1={{ snakize "endingInOsNameLinux" }} 46 Snakize2={{ snakize "endingInArchNameLinuxAmd64" }} 47 Snakize3={{ snakize "endingInTest" }} 48 toPackage1={{ toPackage "a/b-c/d-e" }} 49 toPackage2={{ toPackage "a.a/b_c/d_e" }} 50 toPackage3={{ toPackage "d_e" }} 51 toPackage4={{ toPackage "d-e" }} 52 toPackageName={{ toPackageName "d-e/f-g" }} 53 PascalizeSpecialChar1={{ pascalize "+1" }} 54 PascalizeSpecialChar2={{ pascalize "-1" }} 55 PascalizeSpecialChar3={{ pascalize "1" }} 56 PascalizeSpecialChar4={{ pascalize "-" }} 57 PascalizeSpecialChar5={{ pascalize "+" }} 58 Dict={{ template "dictTemplate" dict "Animal" "Pony" "Shape" "round" "Furniture" "table" }} 59 {{ define "dictTemplate" }}{{ .Animal }} of the {{ .Shape }} {{ .Furniture }}{{ end }} 60 ` 61 } 62 63 func TestTemplates_CustomTemplates(t *testing.T) { 64 65 var buf bytes.Buffer 66 headerTempl, err := templates.Get("bindprimitiveparam") 67 assert.NoError(t, err) 68 69 err = headerTempl.Execute(&buf, nil) 70 require.NoError(t, err) 71 require.NotNil(t, buf) 72 assert.Equal(t, "\n", buf.String()) 73 74 buf.Reset() 75 err = templates.AddFile("bindprimitiveparam", customHeader) 76 assert.NoError(t, err) 77 78 headerTempl, err = templates.Get("bindprimitiveparam") 79 assert.NoError(t, err) 80 assert.NotNil(t, headerTempl) 81 82 err = headerTempl.Execute(&buf, nil) 83 assert.NoError(t, err) 84 assert.Equal(t, "custom header", buf.String()) 85 86 } 87 88 func TestTemplates_CustomTemplatesMultiple(t *testing.T) { 89 var buf bytes.Buffer 90 91 err := templates.AddFile("differentFileName", customMultiple) 92 assert.NoError(t, err) 93 94 headerTempl, err := templates.Get("bindprimitiveparam") 95 assert.NoError(t, err) 96 97 err = headerTempl.Execute(&buf, nil) 98 require.NoError(t, err) 99 100 assert.Equal(t, "custom primitive", buf.String()) 101 } 102 103 func TestTemplates_CustomNewTemplates(t *testing.T) { 104 var buf bytes.Buffer 105 106 err := templates.AddFile("newtemplate", customNewTemplate) 107 assert.NoError(t, err) 108 109 err = templates.AddFile("existingUsesNew", customExistingUsesNew) 110 assert.NoError(t, err) 111 112 headerTempl, err := templates.Get("bindprimitiveparam") 113 assert.NoError(t, err) 114 115 err = headerTempl.Execute(&buf, nil) 116 require.NoError(t, err) 117 118 assert.Equal(t, "new template", buf.String()) 119 } 120 121 func TestTemplates_RepoLoadingTemplates(t *testing.T) { 122 123 repo := NewRepository(nil) 124 125 err := repo.AddFile("simple", singleTemplate) 126 assert.NoError(t, err) 127 128 templ, err := repo.Get("simple") 129 require.NoError(t, err) 130 131 var b bytes.Buffer 132 err = templ.Execute(&b, nil) 133 require.NoError(t, err) 134 135 assert.Equal(t, "test", b.String()) 136 } 137 138 func TestTemplates_RepoLoadsAllTemplatesDefined(t *testing.T) { 139 140 var b bytes.Buffer 141 repo := NewRepository(nil) 142 143 err := repo.AddFile("multiple", multipleDefinitions) 144 assert.NoError(t, err) 145 146 templ, err := repo.Get("multiple") 147 assert.NoError(t, err) 148 149 err = templ.Execute(&b, nil) 150 require.NoError(t, err) 151 152 assert.Equal(t, "", b.String()) 153 154 templ, err = repo.Get("T1") 155 require.NoError(t, err) 156 require.NotNil(t, templ) 157 158 err = templ.Execute(&b, nil) 159 require.NoError(t, err) 160 161 assert.Equal(t, "T1", b.String()) 162 } 163 164 type testData struct { 165 Children []testData 166 Name string 167 Recurse bool 168 } 169 170 func TestTemplates_RepoLoadsAllDependantTemplates(t *testing.T) { 171 172 var b bytes.Buffer 173 repo := NewRepository(nil) 174 175 err := repo.AddFile("multiple", multipleDefinitions) 176 assert.NoError(t, err) 177 178 err = repo.AddFile("dependant", dependantTemplate) 179 assert.NoError(t, err) 180 181 templ, err := repo.Get("dependant") 182 require.NoError(t, err) 183 require.NotNil(t, templ) 184 185 err = templ.Execute(&b, nil) 186 require.NoError(t, err) 187 188 assert.Equal(t, "T1D1", b.String()) 189 } 190 191 func TestTemplates_RepoRecursiveTemplates(t *testing.T) { 192 193 var b bytes.Buffer 194 repo := NewRepository(nil) 195 196 err := repo.AddFile("c1", cirularDeps1) 197 assert.NoError(t, err) 198 199 err = repo.AddFile("c2", cirularDeps2) 200 assert.NoError(t, err) 201 202 templ, err := repo.Get("c1") 203 require.NoError(t, err) 204 require.NotNil(t, templ) 205 206 data := testData{ 207 Name: "Root", 208 Children: []testData{ 209 {Recurse: false}, 210 }, 211 } 212 expected := `Root: Children` 213 err = templ.Execute(&b, data) 214 require.NoError(t, err) 215 assert.Equal(t, expected, b.String()) 216 217 data = testData{ 218 Name: "Root", 219 Children: []testData{ 220 {Name: "Child1", Recurse: true, Children: []testData{{Name: "Child2"}}}, 221 }, 222 } 223 224 b.Reset() 225 226 expected = `Root: Child1: Children` 227 228 err = templ.Execute(&b, data) 229 require.NoError(t, err) 230 231 assert.Equal(t, expected, b.String()) 232 233 data = testData{ 234 Name: "Root", 235 Children: []testData{ 236 {Name: "Child1", Recurse: false, Children: []testData{{Name: "Child2"}}}, 237 }, 238 } 239 240 b.Reset() 241 242 expected = `Root: Children` 243 244 err = templ.Execute(&b, data) 245 require.NoError(t, err) 246 247 assert.Equal(t, expected, b.String()) 248 } 249 250 // Test that definitions are available to templates 251 // TODO: should test also with the codeGenApp context 252 253 // Test copyright definition 254 func TestTemplates_DefinitionCopyright(t *testing.T) { 255 const copyright = `{{ .Copyright }}` 256 log.SetOutput(os.Stdout) 257 258 repo := NewRepository(nil) 259 260 err := repo.AddFile("copyright", copyright) 261 assert.NoError(t, err) 262 263 templ, err := repo.Get("copyright") 264 require.NoError(t, err) 265 require.NotNil(t, templ) 266 267 opts := opts() 268 opts.Copyright = "My copyright clause" 269 expected := opts.Copyright 270 271 // executes template against model definitions 272 genModel, err := getModelEnvironment("../fixtures/codegen/todolist.models.yml", opts) 273 require.NoError(t, err) 274 require.NotNil(t, genModel) 275 276 rendered := bytes.NewBuffer(nil) 277 err = templ.Execute(rendered, genModel) 278 assert.NoError(t, err) 279 assert.Equal(t, expected, rendered.String()) 280 281 // executes template against operations definitions 282 genOperation, err := getOperationEnvironment("get", "/media/search", "../fixtures/codegen/instagram.yml", opts) 283 require.NoError(t, err) 284 require.NotNil(t, genOperation) 285 286 rendered.Reset() 287 288 err = templ.Execute(rendered, genOperation) 289 require.NoError(t, err) 290 291 assert.Equal(t, expected, rendered.String()) 292 293 } 294 295 // Test TargetImportPath definition 296 func TestTemplates_DefinitionTargetImportPath(t *testing.T) { 297 const targetImportPath = `{{ .TargetImportPath }}` 298 log.SetOutput(os.Stdout) 299 300 repo := NewRepository(nil) 301 302 err := repo.AddFile("targetimportpath", targetImportPath) 303 assert.NoError(t, err) 304 305 templ, err := repo.Get("targetimportpath") 306 require.NoError(t, err) 307 require.NotNil(t, templ) 308 309 opts := opts() 310 // Non existing target would panic: to be tested too, but in another module 311 opts.Target = "../fixtures" 312 var expected = "github.com/go-swagger/go-swagger/fixtures" 313 314 // executes template against model definitions 315 genModel, err := getModelEnvironment("../fixtures/codegen/todolist.models.yml", opts) 316 require.NoError(t, err) 317 require.NotNil(t, genModel) 318 319 rendered := bytes.NewBuffer(nil) 320 err = templ.Execute(rendered, genModel) 321 assert.NoError(t, err) 322 323 assert.Equal(t, expected, rendered.String()) 324 325 // executes template against operations definitions 326 genOperation, err := getOperationEnvironment("get", "/media/search", "../fixtures/codegen/instagram.yml", opts) 327 require.NoError(t, err) 328 require.NotNil(t, genOperation) 329 330 rendered.Reset() 331 332 err = templ.Execute(rendered, genOperation) 333 require.NoError(t, err) 334 335 assert.Equal(t, expected, rendered.String()) 336 337 } 338 339 // Simulates a definition environment for model templates 340 func getModelEnvironment(spec string, opts *GenOpts) (*GenDefinition, error) { 341 // Don't want stderr output to pollute CI 342 log.SetOutput(ioutil.Discard) 343 defer log.SetOutput(os.Stdout) 344 345 specDoc, err := loads.Spec("../fixtures/codegen/todolist.models.yml") 346 if err != nil { 347 return nil, err 348 } 349 definitions := specDoc.Spec().Definitions 350 351 for k, schema := range definitions { 352 genModel, err := makeGenDefinition(k, "models", schema, specDoc, opts) 353 if err != nil { 354 return nil, err 355 } 356 // One is enough 357 return genModel, nil 358 } 359 return nil, nil 360 } 361 362 // Simulates a definition environment for operation templates 363 func getOperationEnvironment(operation string, path string, spec string, opts *GenOpts) (*GenOperation, error) { 364 // Don't want stderr output to pollute CI 365 log.SetOutput(ioutil.Discard) 366 defer log.SetOutput(os.Stdout) 367 368 b, err := methodPathOpBuilder(operation, path, spec) 369 if err != nil { 370 return nil, err 371 } 372 b.GenOpts = opts 373 g, err := b.MakeOperation() 374 if err != nil { 375 return nil, err 376 } 377 return &g, nil 378 } 379 380 // Exercises FuncMap 381 // Just running basic tests to make sure the function map works and all functions are available as expected. 382 // More complete unit tests are provided by go-openapi/swag. 383 func TestTemplates_FuncMap(t *testing.T) { 384 log.SetOutput(os.Stdout) 385 funcTpl := testFuncTpl() 386 387 err := templates.AddFile("functpl", funcTpl) 388 require.NoError(t, err) 389 390 templ, err := templates.Get("functpl") 391 require.NoError(t, err) 392 393 opts := opts() 394 // executes template against model definitions 395 genModel, err := getModelEnvironment("../fixtures/codegen/todolist.models.yml", opts) 396 require.NoError(t, err) 397 398 genModel.DependsOn = []string{"x", "z"} 399 rendered := bytes.NewBuffer(nil) 400 err = templ.Execute(rendered, genModel) 401 require.NoError(t, err) 402 403 assert.Contains(t, rendered.String(), "Pascalize=WeArePoniesOfTheRoundTable\n") 404 assert.Contains(t, rendered.String(), "Snakize=we_are_ponies_of_the_round_table\n") 405 assert.Contains(t, rendered.String(), "Humanize=we are ponies of the round table\n") 406 assert.Contains(t, rendered.String(), "PluralizeFirstWord=ponies of the round table\n") 407 assert.Contains(t, rendered.String(), "PluralizeFirstOfOneWord=dwarves\n") 408 assert.Contains(t, rendered.String(), "PluralizeFirstOfNoWord=\n") 409 assert.Contains(t, rendered.String(), "DropPackage=suffix\n") 410 assert.Contains(t, rendered.String(), "DropNoPackage=suffix\n") 411 assert.Contains(t, rendered.String(), "DropEmptyPackage=\n") 412 assert.Contains(t, rendered.String(), "DropEmptyPackage=\n") 413 assert.Contains(t, rendered.String(), "ContainsString=true\n") 414 assert.Contains(t, rendered.String(), "DoesNotContainString=false\n") 415 assert.Contains(t, rendered.String(), "PadSurround1=-,-,-,padme,-,-,-,-,-,-,-,-\n") 416 assert.Contains(t, rendered.String(), "PadSurround2=padme,-,-,-,-,-,-,-,-,-,-,-\n") 417 assert.Contains(t, rendered.String(), `Json={"errors":"github.com/go-openapi/errors","runtime":"github.com/go-openapi/runtime","swag":"github.com/go-openapi/swag","validate":"github.com/go-openapi/validate"}`) 418 assert.Contains(t, rendered.String(), "\"TargetImportPath\": \"github.com/go-swagger/go-swagger/generator\"") 419 assert.Contains(t, rendered.String(), "Snakize1=ending_in_os_name_linux_swagger\n") 420 assert.Contains(t, rendered.String(), "Snakize2=ending_in_arch_name_linux_amd64_swagger\n") 421 assert.Contains(t, rendered.String(), "Snakize3=ending_in_test_swagger\n") 422 assert.Contains(t, rendered.String(), "toPackage1=a/b-c/d_e\n") 423 assert.Contains(t, rendered.String(), "toPackage2=a.a/b_c/d_e\n") 424 assert.Contains(t, rendered.String(), "toPackage3=d_e\n") 425 assert.Contains(t, rendered.String(), "toPackage4=d_e\n") 426 assert.Contains(t, rendered.String(), "toPackageName=f_g\n") 427 assert.Contains(t, rendered.String(), "PascalizeSpecialChar1=Plus1\n") 428 assert.Contains(t, rendered.String(), "PascalizeSpecialChar2=Minus1\n") 429 assert.Contains(t, rendered.String(), "PascalizeSpecialChar3=Nr1\n") 430 assert.Contains(t, rendered.String(), "PascalizeSpecialChar4=Minus\n") 431 assert.Contains(t, rendered.String(), "PascalizeSpecialChar5=Plus\n") 432 assert.Contains(t, rendered.String(), "Dict=Pony of the round table\n") 433 } 434 435 // AddFile() global package function (protected vs unprotected) 436 // Mostly unused in tests, since the Repository.AddFile() 437 // is generally preferred. 438 func TestTemplates_AddFile(t *testing.T) { 439 log.SetOutput(os.Stdout) 440 funcTpl := testFuncTpl() 441 442 // unprotected 443 err := AddFile("functpl", funcTpl) 444 require.NoError(t, err) 445 446 _, err = templates.Get("functpl") 447 require.NoError(t, err) 448 449 // protected 450 err = AddFile("schemabody", funcTpl) 451 require.Error(t, err) 452 assert.Contains(t, err.Error(), "cannot overwrite protected template") 453 } 454 455 // Test LoadDir 456 func TestTemplates_LoadDir(t *testing.T) { 457 log.SetOutput(os.Stdout) 458 459 // Fails 460 err := templates.LoadDir("") 461 require.Error(t, err) 462 assert.Contains(t, err.Error(), "could not complete") 463 464 // Fails again (from any dir?) 465 err = templates.LoadDir("templates") 466 require.Error(t, err) 467 assert.Contains(t, err.Error(), "cannot overwrite protected template") 468 469 // TODO: success case 470 // To force a success, we need to empty the global list of protected 471 // templates... 472 origProtectedTemplates := protectedTemplates 473 474 defer func() { 475 // Restore variable initialized with package 476 protectedTemplates = origProtectedTemplates 477 }() 478 479 protectedTemplates = make(map[string]bool) 480 repo := NewRepository(FuncMapFunc(DefaultLanguageFunc())) 481 err = repo.LoadDir("templates") 482 assert.NoError(t, err) 483 } 484 485 // Test LoadDir 486 func TestTemplates_SetAllowOverride(t *testing.T) { 487 log.SetOutput(os.Stdout) 488 489 // adding protected file with allowOverride set to false fails 490 templates.SetAllowOverride(false) 491 err := templates.AddFile("schemabody", "some data") 492 require.Error(t, err) 493 assert.Contains(t, err.Error(), "cannot overwrite protected template schemabody") 494 495 // adding protected file with allowOverride set to true should not fail 496 templates.SetAllowOverride(true) 497 err = templates.AddFile("schemabody", "some data") 498 assert.NoError(t, err) 499 } 500 501 // Test LoadContrib 502 func TestTemplates_LoadContrib(t *testing.T) { 503 tests := []struct { 504 name string 505 template string 506 wantError bool 507 }{ 508 { 509 name: "None_existing_contributor_template", 510 template: "NonExistingContributorTemplate", 511 wantError: true, 512 }, 513 { 514 name: "Existing_contributor", 515 template: "stratoscale", 516 wantError: false, 517 }, 518 } 519 520 for _, tt := range tests { 521 t.Run(tt.name, func(t *testing.T) { 522 err := templates.LoadContrib(tt.template) 523 if tt.wantError { 524 assert.Error(t, err) 525 } else { 526 assert.NoError(t, err) 527 } 528 }) 529 } 530 } 531 532 // TODO: test error case in LoadDefaults() 533 // test DumpTemplates() 534 func TestTemplates_DumpTemplates(t *testing.T) { 535 buf := bytes.NewBuffer(nil) 536 log.SetOutput(buf) 537 defer func() { 538 log.SetOutput(os.Stdout) 539 }() 540 541 templates.DumpTemplates() 542 assert.NotEmpty(t, buf) 543 // Sample output 544 assert.Contains(t, buf.String(), "## tupleSerializer") 545 assert.Contains(t, buf.String(), "Defined in `tupleserializer.gotmpl`") 546 assert.Contains(t, buf.String(), "####requires \n - schemaType") 547 } 548 549 func TestFuncMap_Pascalize(t *testing.T) { 550 assert.Equal(t, "Plus1", pascalize("+1")) 551 assert.Equal(t, "Plus", pascalize("+")) 552 assert.Equal(t, "Minus1", pascalize("-1")) 553 assert.Equal(t, "Minus", pascalize("-")) 554 assert.Equal(t, "Nr8", pascalize("8")) 555 556 assert.Equal(t, "Hello", pascalize("+hello")) 557 558 // other values from swag rules 559 assert.Equal(t, "At8", pascalize("@8")) 560 assert.Equal(t, "AtHello", pascalize("@hello")) 561 assert.Equal(t, "Bang8", pascalize("!8")) 562 assert.Equal(t, "At", pascalize("@")) 563 564 // # values 565 assert.Equal(t, "Hello", pascalize("#hello")) 566 assert.Equal(t, "BangHello", pascalize("#!hello")) 567 assert.Equal(t, "HashTag8", pascalize("#8")) 568 assert.Equal(t, "HashTag", pascalize("#")) 569 570 // single '_' 571 assert.Equal(t, "Nr", pascalize("_")) 572 assert.Equal(t, "Hello", pascalize("_hello")) 573 574 // remove spaces 575 assert.Equal(t, "HelloWorld", pascalize("# hello world")) 576 assert.Equal(t, "HashTag8HelloWorld", pascalize("# 8 hello world")) 577 578 assert.Equal(t, "Empty", pascalize("")) 579 } 580 581 func TestFuncMap_DropPackage(t *testing.T) { 582 assert.Equal(t, "trail", dropPackage("base.trail")) 583 assert.Equal(t, "trail", dropPackage("base.another.trail")) 584 assert.Equal(t, "trail", dropPackage("trail")) 585 } 586 587 func TestFuncMap_AsJSON(t *testing.T) { 588 for _, jsonFunc := range []func(interface{}) (string, error){ 589 asJSON, 590 asPrettyJSON, 591 } { 592 res, err := jsonFunc(struct { 593 A string `json:"a"` 594 B int 595 }{A: "good", B: 3}) 596 require.NoError(t, err) 597 assert.JSONEq(t, `{"a":"good","B":3}`, res) 598 599 _, err = jsonFunc(struct { 600 A string `json:"a"` 601 B func() string 602 }{A: "good", B: func() string { return "" }}) 603 require.Error(t, err) 604 } 605 } 606 607 func TestFuncMap_Dict(t *testing.T) { 608 d, err := dict("a", "b", "c", "d") 609 require.NoError(t, err) 610 assert.Equal(t, map[string]interface{}{"a": "b", "c": "d"}, d) 611 612 // odd number of arguments 613 _, err = dict("a", "b", "c") 614 require.Error(t, err) 615 616 // none-string key 617 _, err = dict("a", "b", 3, "d") 618 require.Error(t, err) 619 }