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