github.com/AngusLu/go-swagger@v0.28.0/generator/generate_test.go (about) 1 package generator 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "log" 7 "os" 8 "path" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "testing" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestGenerateAndTest(t *testing.T) { 19 defer discardOutput()() 20 21 cwd := testCwd(t) 22 const root = "generated" 23 defer func() { 24 _ = os.RemoveAll(filepath.Join(cwd, root)) 25 }() 26 27 t.Run("server build", func(t *testing.T) { 28 for name, cas := range generateFixtures(t) { 29 thisCas := cas 30 thisName := name 31 32 t.Run(thisName, func(t *testing.T) { 33 t.Parallel() 34 35 log.SetOutput(ioutil.Discard) 36 defer thisCas.warnFailed(t) 37 38 // default opts 39 opts := testGenOpts() 40 41 // create directory layout, defer clean 42 defer thisCas.prepareTarget(t, thisName, "server_test", root, opts)() 43 44 // preparation before generation 45 if thisCas.prepare != nil { 46 thisCas.prepare(t, opts) 47 } 48 49 t.Logf("generating test server at: %s, from %s", opts.Target, opts.Spec) 50 51 err := GenerateServer("", nil, nil, opts) 52 if thisCas.wantError { 53 require.Errorf(t, err, "expected an error for server build fixture: %s", opts.Spec) 54 } else { 55 require.NoError(t, err, "unexpected error for server build fixture: %s", opts.Spec) 56 } 57 58 // verify 59 if thisCas.verify != nil { 60 thisCas.verify(t, opts.Target) 61 } 62 63 // fixture-specific clean 64 if thisCas.clean != nil { 65 thisCas.clean() 66 } 67 }) 68 } 69 }) 70 } 71 72 type generateFixture struct { 73 name string 74 spec string 75 target string 76 wantError bool 77 prepare func(testing.TB, *GenOpts) 78 verify func(testing.TB, string) 79 clean func() 80 } 81 82 func (f generateFixture) base(t testing.TB, root string) (string, func()) { 83 // base generation target 84 cwd := testCwd(t) 85 86 base := filepath.Join(cwd, root) 87 require.NoErrorf(t, os.MkdirAll(base, 0700), "error in test creating target dir") 88 89 generated, err := ioutil.TempDir(base, "generated") 90 require.NoErrorf(t, err, "error in test creating temp dir") 91 92 return generated, func() { 93 _ = os.RemoveAll(generated) 94 } 95 } 96 97 func (f generateFixture) prepareTarget(t testing.TB, name, base, root string, opts *GenOpts) func() { 98 if name == "" { 99 name = f.name 100 } 101 102 spec := filepath.FromSlash(f.spec) 103 opts.Spec = spec 104 105 generated, clean := f.base(t, root) 106 107 if f.target == "" { 108 opts.Target = filepath.Join(generated, opts.LanguageOpts.ManglePackageName(name, base)) 109 } else { 110 opts.Target = filepath.Join(generated, filepath.Base(f.target)) 111 } 112 113 require.NoErrorf(t, os.MkdirAll(opts.Target, 0700), "error in test creating target dir") 114 115 return clean 116 } 117 118 func (f generateFixture) warnFailed(t testing.TB) func() { 119 return func() { 120 if t.Failed() { 121 t.Log("ERROR: generation failed") 122 } 123 } 124 } 125 126 func generateFixtures(t testing.TB) map[string]generateFixture { 127 return map[string]generateFixture{ 128 "issue 1943": { 129 spec: "../fixtures/bugs/1943/fixture-1943.yaml", 130 target: "../fixtures/bugs/1943", 131 prepare: func(_ testing.TB, opts *GenOpts) { 132 input, err := ioutil.ReadFile("../fixtures/bugs/1943/datarace_test.go") 133 require.NoError(t, err) 134 135 // rewrite imports for the relocated test program 136 cwd := testCwd(t) 137 rebased := bytes.ReplaceAll( 138 input, 139 []byte("/fixtures/bugs/1943"), 140 []byte(filepath.ToSlash(strings.TrimPrefix(opts.Target, filepath.Dir(cwd)))), 141 ) 142 143 require.NoError(t, ioutil.WriteFile(filepath.Join(opts.Target, "datarace_test.go"), rebased, 0600)) 144 opts.ExcludeSpec = false 145 }, 146 verify: func(t testing.TB, target string) { 147 if runtime.GOOS == "windows" { 148 // don't run race tests on Appveyor CI 149 t.Logf("warn: race test skipped on windows") 150 return 151 } 152 153 const packages = "./..." 154 testPrg := "datarace_test.go" 155 156 goExecInDir(t, target, "get", packages) 157 158 t.Log("running data race test on generated server") 159 goExecInDir(t, target, "test", "-v", "-race", testPrg) 160 }, 161 }, 162 "packages_mangling": { 163 spec: "../fixtures/bugs/2111/fixture-2111.yaml", 164 prepare: func(_ testing.TB, opts *GenOpts) { 165 opts.IncludeMain = true 166 }, 167 verify: func(t testing.TB, target string) { 168 require.True(t, fileExists(target, defaultServerTarget)) 169 assert.True(t, fileExists(filepath.Join(target, "cmd", "unsafe-tag-names-server"), "main.go")) 170 171 srvTarget := filepath.Join(target, defaultServerTarget) 172 opsTarget := filepath.Join(srvTarget, defaultOperationsTarget) 173 require.True(t, fileExists(opsTarget, "")) 174 175 for _, fileOrDir := range []string{ 176 "abc_linux", "abc_test", 177 "api", 178 "custom", 179 "hash_tag_donuts", 180 "nr123abc", "nr_at_donuts", "plus_donuts", 181 "strfmt", 182 "forced", 183 "gtl", 184 "nr12nasty", 185 "override", 186 "get_notag.go", 187 "operationsops", 188 } { 189 assert.True(t, fileExists(opsTarget, fileOrDir)) 190 } 191 192 buf, err := ioutil.ReadFile(filepath.Join(srvTarget, "configure_unsafe_tag_names.go")) 193 require.NoError(t, err) 194 195 code := string(buf) 196 197 // assert imports, with deconfliction 198 cwd := testCwd(t) 199 base := path.Join("github.com", "go-swagger", "go-swagger", 200 filepath.ToSlash(strings.TrimPrefix(target, filepath.Dir(cwd))), 201 ) 202 203 baseImport := path.Join(base, `restapi/operations`) 204 assertImports(t, baseImport, code) 205 206 assertInCode(t, `api.APIGetConflictHandler = apiops.GetConflictHandlerFunc(`, code) 207 assertInCode(t, `api.StrfmtGetAnotherConflictHandler = strfmtops.GetAnotherConflictHandlerFunc(`, code) 208 assertInCode(t, `api.GetNotagHandler = operations.GetNotagHandlerFunc(`, code) 209 210 buf2, err := ioutil.ReadFile(filepath.Join(opsTarget, "unsafe_tag_names_api.go")) 211 require.NoError(t, err) 212 213 api := string(buf2) 214 assertImports(t, baseImport, api) 215 216 assertInCode(t, `APIGetConflictHandler: apiops.GetConflictHandlerFunc(func(params apiops.GetConflictParams) middleware.Responder {`, api) 217 assertInCode(t, `StrfmtGetAnotherConflictHandler: strfmtops.GetAnotherConflictHandlerFunc(func(params strfmtops.GetAnotherConflictParams) middleware.Responder {`, api) 218 assertInCode(t, `GetNotagHandler: GetNotagHandlerFunc(func(params GetNotagParams) middleware.Responder {`, api) 219 220 assertInCode(t, `OverrideDeleteTestOverrideHandler override.DeleteTestOverrideHandler`, api) 221 assertInCode(t, `StrfmtGetAnotherConflictHandler strfmtops.GetAnotherConflictHandler`, api) 222 assertInCode(t, `APIGetConflictHandler apiops.GetConflictHandler`, api) 223 assertInCode(t, `CustomGetCustomHandler custom.GetCustomHandler`, api) 224 assertInCode(t, `AbcLinuxGetMultipleHandler abc_linux.GetMultipleHandler`, api) 225 assertInCode(t, `GetNotagHandler GetNotagHandler`, api) 226 assertInCode(t, `AbcLinuxGetOtherReservedHandler abc_linux.GetOtherReservedHandler`, api) 227 assertInCode(t, `PlusDonutsGetOtherUnsafeHandler plus_donuts.GetOtherUnsafeHandler`, api) 228 assertInCode(t, `AbcTestGetReservedHandler abc_test.GetReservedHandler`, api) 229 assertInCode(t, `GtlGetTestOverrideHandler gtl.GetTestOverrideHandler`, api) 230 assertInCode(t, `HashTagDonutsGetUnsafeHandler hash_tag_donuts.GetUnsafeHandler`, api) 231 assertInCode(t, `NrAtDonutsGetYetAnotherUnsafeHandler nr_at_donuts.GetYetAnotherUnsafeHandler`, api) 232 assertInCode(t, `ForcedPostTestOverrideHandler forced.PostTestOverrideHandler`, api) 233 assertInCode(t, `Nr12nastyPutTestOverrideHandler nr12nasty.PutTestOverrideHandler`, api) 234 assertInCode(t, `Nr123abcTestIDHandler nr123abc.TestIDHandler`, api) 235 }, 236 }, 237 "packages_flattening": { 238 spec: "../fixtures/bugs/2111/fixture-2111.yaml", 239 prepare: func(_ testing.TB, opts *GenOpts) { 240 opts.SkipTagPackages = true 241 }, 242 verify: func(t testing.TB, target string) { 243 require.True(t, fileExists(target, defaultServerTarget)) 244 245 srvTarget := filepath.Join(target, defaultServerTarget) 246 opsTarget := filepath.Join(srvTarget, defaultOperationsTarget) 247 require.True(t, fileExists(opsTarget, "")) 248 249 for _, fileOrDir := range []string{ 250 "abc_linux", "abc_test", 251 "api", 252 "custom", 253 "hash_tag_donuts", 254 "nr123abc", "nr_at_donuts", "plus_donuts", 255 "strfmt", 256 "forced", 257 "gtl", 258 "nr12nasty", 259 "override", 260 "operationsops", 261 } { 262 assert.Falsef(t, fileExists(opsTarget, fileOrDir), "did not expect %s in %s", fileOrDir, opsTarget) 263 } 264 265 assert.Truef(t, fileExists(opsTarget, "get_notag.go"), "expected %s in %s", "get_notag.go", opsTarget) 266 267 buf, err := ioutil.ReadFile(filepath.Join(srvTarget, "configure_unsafe_tag_names.go")) 268 require.NoError(t, err) 269 code := string(buf) 270 271 cwd := testCwd(t) 272 base := path.Join("github.com", "go-swagger", "go-swagger", 273 filepath.ToSlash(strings.TrimPrefix(target, filepath.Dir(cwd))), 274 ) 275 276 baseImport := path.Join(base, `restapi/operations`) 277 assertRegexpInCode(t, baseImport, code) 278 279 assertInCode(t, `api.GetConflictHandler = operations.GetConflictHandlerFunc(`, code) 280 assertInCode(t, `api.GetAnotherConflictHandler = operations.GetAnotherConflictHandlerFunc(`, code) 281 assertInCode(t, `api.GetNotagHandler = operations.GetNotagHandlerFunc(`, code) 282 283 buf2, err := ioutil.ReadFile(filepath.Join(opsTarget, "unsafe_tag_names_api.go")) 284 require.NoError(t, err) 285 api := string(buf2) 286 287 assertInCode(t, `GetConflictHandler: GetConflictHandlerFunc(func(params GetConflictParams) middleware.Responder {`, api) 288 assertInCode(t, `GetAnotherConflictHandler: GetAnotherConflictHandlerFunc(func(params GetAnotherConflictParams) middleware.Responder {`, api) 289 assertInCode(t, `NotagHandler: GetNotagHandlerFunc(func(params GetNotagParams) middleware.Responder {`, api) 290 291 assertInCode(t, `DeleteTestOverrideHandler DeleteTestOverrideHandler`, api) 292 assertInCode(t, `GetAnotherConflictHandler GetAnotherConflictHandler`, api) 293 assertInCode(t, `GetConflictHandler GetConflictHandler`, api) 294 assertInCode(t, `GetCustomHandler GetCustomHandler`, api) 295 assertInCode(t, `GetMultipleHandler GetMultipleHandler`, api) 296 assertInCode(t, `GetNotagHandler GetNotagHandler`, api) 297 assertInCode(t, `GetOtherReservedHandler GetOtherReservedHandler`, api) 298 assertInCode(t, `GetOtherUnsafeHandler GetOtherUnsafeHandler`, api) 299 assertInCode(t, `GetReservedHandler GetReservedHandler`, api) 300 assertInCode(t, `GetTestOverrideHandler GetTestOverrideHandler`, api) 301 assertInCode(t, `GetUnsafeHandler GetUnsafeHandler`, api) 302 assertInCode(t, `GetYetAnotherUnsafeHandler GetYetAnotherUnsafeHandler`, api) 303 assertInCode(t, `PostTestOverrideHandler PostTestOverrideHandler`, api) 304 assertInCode(t, `PutTestOverrideHandler PutTestOverrideHandler`, api) 305 assertInCode(t, `TestIDHandler TestIDHandler`, api) 306 }, 307 }, 308 "main_package": { 309 spec: "../fixtures/bugs/2111/fixture-2111.yaml", 310 prepare: func(_ testing.TB, opts *GenOpts) { 311 opts.IncludeMain = true 312 opts.MainPackage = "custom-api" 313 opts.SkipTagPackages = true 314 }, 315 verify: func(t testing.TB, target string) { 316 assert.True(t, fileExists(filepath.Join(target, "cmd", "custom-api"), "main.go")) 317 }, 318 }, 319 "external_model": { 320 spec: "../fixtures/bugs/1897/fixture-1897.yaml", 321 prepare: func(t testing.TB, opts *GenOpts) { 322 modelOpts := *opts 323 modelOpts.AcceptDefinitionsOnly = true 324 modelOpts.Spec = "../fixtures/bugs/1897/model.yaml" 325 modelOpts.ModelPackage = "external" 326 modelOpts.Target = filepath.Dir(modelOpts.Spec) 327 328 require.NoError(t, GenerateModels(nil, &modelOpts)) 329 330 t.Logf("generated external model") 331 require.True(t, fileExists(modelOpts.Target, filepath.Join("external"))) 332 require.True(t, fileExists(modelOpts.Target, filepath.Join("external", "error.go"))) 333 334 opts.IncludeMain = true 335 }, 336 verify: func(t testing.TB, target string) { 337 location := filepath.Join(target, "cmd", "repro1897-server") 338 require.True(t, fileExists("", location)) 339 340 t.Log("building generated server") 341 goExecInDir(t, location, "build") 342 }, 343 clean: func() { 344 // remove generated external models 345 _ = os.RemoveAll(filepath.Join("..", "fixtures", "bugs", "1897", "external")) 346 }, 347 }, 348 "external_models_hints": { 349 spec: "../fixtures/enhancements/2224/fixture-2224.yaml", 350 target: "2224-hints", 351 prepare: func(t testing.TB, opts *GenOpts) { 352 modelOpts := *opts 353 modelOpts.AcceptDefinitionsOnly = true 354 modelOpts.Spec = "../fixtures/enhancements/2224/fixture-2224-models.yaml" 355 modelOpts.ModelPackage = "external" 356 modelOpts.Target = filepath.Dir(modelOpts.Spec) 357 358 require.NoError(t, GenerateModels(nil, &modelOpts)) 359 360 t.Logf("generated external model") 361 require.True(t, fileExists(modelOpts.Target, filepath.Join("external"))) 362 363 for _, model := range []string{ 364 "access_point.go", "base.go", 365 "hotspot.go", "hotspot_type.go", 366 "incorrect.go", "json_message.go", 367 "json_object.go", "json_object_with_alias.go", 368 "object_with_embedded.go", "object_with_externals.go", 369 "raw.go", "request.go", 370 "request_pointer.go", "time_as_object.go", "time.go", 371 } { 372 require.True(t, fileExists(modelOpts.Target, filepath.Join("external", model))) 373 } 374 375 opts.IncludeMain = true 376 }, 377 verify: func(t testing.TB, target string) { 378 // generated models (not external) 379 require.True(t, fileExists(target, filepath.Join("models"))) 380 for _, model := range []string{"error.go", "external_with_embed.go"} { 381 require.True(t, fileExists(target, filepath.Join("models", model))) 382 } 383 384 location := filepath.Join(target, "cmd", "external-types-with-hints-server") 385 require.True(t, fileExists("", location)) 386 387 t.Log("building generated server") 388 goExecInDir(t, location, "build") 389 }, 390 clean: func() { 391 // remove generated external models 392 _ = os.RemoveAll(filepath.Join("..", "fixtures", "enhancements", "2224", "external")) 393 }, 394 }, 395 "conflict_name_api_issue_2405_1": { 396 spec: "../examples/todo-list/swagger.yml", 397 target: "2405-1", 398 prepare: func(_ testing.TB, opts *GenOpts) { 399 opts.ServerPackage = "api" 400 opts.IncludeMain = true 401 }, 402 verify: func(t testing.TB, target string) { 403 location := filepath.Join(target, "cmd", "simple-to-do-list-api-server") 404 require.True(t, fileExists("", location)) 405 406 t.Log("building generated server") 407 goExecInDir(t, location, "build") 408 }, 409 }, 410 "conflict_name_api_issue_2405_2": { 411 spec: "../examples/todo-list/swagger.yml", 412 target: "2405-2", 413 prepare: func(_ testing.TB, opts *GenOpts) { 414 opts.ServerPackage = "loads" 415 opts.IncludeMain = true 416 }, 417 verify: func(t testing.TB, target string) { 418 location := filepath.Join(target, "cmd", "simple-to-do-list-api-server") 419 require.True(t, fileExists("", location)) 420 421 t.Log("building generated server") 422 goExecInDir(t, location, "build") 423 }, 424 }, 425 "conflict_name_api_issue_2405_3": { 426 spec: "../fixtures/bugs/2405/fixture-2405.yaml", 427 target: "2405-3", 428 prepare: func(_ testing.TB, opts *GenOpts) { 429 opts.ServerPackage = "server" 430 opts.APIPackage = "api" 431 opts.IncludeMain = true 432 }, 433 verify: func(t testing.TB, target string) { 434 location := filepath.Join(target, "cmd", "simple-to-do-list-api-server") 435 require.True(t, fileExists("", location)) 436 437 t.Log("building generated server") 438 goExecInDir(t, location, "build") 439 }, 440 }, 441 "ext_types_issue_2385": { 442 spec: "../fixtures/bugs/2385/fixture-2385.yaml", 443 target: "2385", 444 prepare: func(t testing.TB, opts *GenOpts) { 445 opts.MainPackage = "nrcodegen-server" 446 opts.IncludeMain = true 447 location := filepath.Join(opts.Target, "models") 448 449 // add some custom model to the generated models 450 addModelsToLocation(t, location, "my_type.go") 451 }, 452 verify: func(_ testing.TB, target string) { 453 location := filepath.Join(target, "cmd", "nrcodegen-server") 454 require.True(t, fileExists("", location)) 455 456 t.Log("building generated server") 457 goExecInDir(t, location, "build") 458 459 location = filepath.Join(target, "models") 460 461 t.Log("building generated models") 462 goExecInDir(t, location, "build") 463 }, 464 }, 465 "ext_types_full_example": { 466 spec: "../examples/external-types/example-external-types.yaml", 467 target: "external-full", 468 prepare: func(_ testing.TB, opts *GenOpts) { 469 opts.MainPackage = "nrcodegen-server" 470 opts.IncludeMain = true 471 opts.ValidateSpec = false // the spec contains AdditionalItems 472 location := filepath.Join(opts.Target, "models") 473 474 // add some custom model to the generated models 475 addModelsToLocation(t, location, "my_type.go") 476 }, 477 verify: func(t testing.TB, target string) { 478 location := filepath.Join(target, "cmd", "nrcodegen-server") 479 require.True(t, fileExists("", location)) 480 481 t.Log("building generated server") 482 goExecInDir(t, location, "build") 483 484 location = filepath.Join(target, "models") 485 486 t.Log("building generated models") 487 goExecInDir(t, location, "build") 488 }, 489 }, 490 } 491 } 492 493 func addModelsToLocation(t testing.TB, location, file string) { 494 // writes some external model to a file to supplement codegen 495 // (test external types) 496 require.NoError(t, os.MkdirAll(location, 0700)) 497 498 require.NoError(t, ioutil.WriteFile(filepath.Join(location, file), []byte(` 499 package models 500 501 import ( 502 "context" 503 "io" 504 "github.com/go-openapi/strfmt" 505 ) 506 507 // MyType ... 508 type MyType string 509 510 // Validate MyType 511 func (MyType) Validate(strfmt.Registry) error { return nil } 512 func (MyType) ContextValidate(context.Context, strfmt.Registry) error { return nil } 513 514 // MyInteger ... 515 type MyInteger int 516 517 // Validate MyInteger 518 func (MyInteger) Validate(strfmt.Registry) error { return nil } 519 func (MyInteger) ContextValidate(context.Context, strfmt.Registry) error { return nil } 520 521 // MyString ... 522 type MyString string 523 524 // Validate MyString 525 func (MyString) Validate(strfmt.Registry) error { return nil } 526 func (MyString) ContextValidate(context.Context, strfmt.Registry) error { return nil } 527 528 // MyOtherType ... 529 type MyOtherType struct{} 530 531 // Validate MyOtherType 532 func (MyOtherType) Validate(strfmt.Registry) error { return nil } 533 func (MyOtherType) ContextValidate(context.Context, strfmt.Registry) error { return nil } 534 535 // MyStreamer ... 536 type MyStreamer io.Reader 537 `), 538 os.ModePerm)) 539 }