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