github.com/rzurga/go-swagger@v0.28.1-0.20211109195225-5d1f453ffa3a/generator/types_test.go (about) 1 package generator 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "log" 7 "os" 8 "strconv" 9 "testing" 10 11 "github.com/go-openapi/loads" 12 "github.com/go-openapi/spec" 13 "github.com/go-openapi/swag" 14 "github.com/stretchr/testify/require" 15 ) 16 17 type externalTypeFixture struct { 18 title string 19 schema string 20 expected *externalTypeDefinition 21 knownDefs struct{ tpe, pkg, alias string } 22 resolved resolvedType 23 } 24 25 func makeResolveExternalTypes() []externalTypeFixture { 26 return []externalTypeFixture{ 27 { 28 title: "hint as map", 29 schema: `{ 30 "type": "object", 31 "x-go-type": { 32 "type": "Mytype", 33 "import": { 34 "package": "github.com/fredbi/mymodels", 35 "alias": "external" 36 }, 37 "hints": { 38 "kind": "map" 39 }, 40 "embedded": false 41 } 42 }`, 43 expected: &externalTypeDefinition{ 44 Type: "Mytype", 45 Import: struct { 46 Package string 47 Alias string 48 }{ 49 Package: "github.com/fredbi/mymodels", 50 Alias: "external", 51 }, 52 Hints: struct { 53 Kind string 54 Nullable *bool 55 NoValidation *bool 56 }{ 57 Kind: "map", 58 }, 59 Embedded: false, 60 }, 61 knownDefs: struct{ tpe, pkg, alias string }{ 62 tpe: "external.Mytype", 63 pkg: "github.com/fredbi/mymodels", 64 alias: "external", 65 }, 66 resolved: resolvedType{ 67 GoType: "external.Mytype", 68 IsMap: true, 69 SwaggerType: "object", 70 IsEmptyOmitted: true, 71 Pkg: "github.com/fredbi/mymodels", 72 PkgAlias: "external", 73 }, 74 }, 75 { 76 title: "hint as map, embedded", 77 schema: `{ 78 "type": "object", 79 "x-go-type": { 80 "type": "Mytype", 81 "import": { 82 "package": "github.com/fredbi/mymodels", 83 "alias": "external" 84 }, 85 "hints": { 86 "kind": "map" 87 }, 88 "embedded": true 89 } 90 }`, 91 expected: &externalTypeDefinition{ 92 Type: "Mytype", 93 Import: struct { 94 Package string 95 Alias string 96 }{ 97 Package: "github.com/fredbi/mymodels", 98 Alias: "external", 99 }, 100 Hints: struct { 101 Kind string 102 Nullable *bool 103 NoValidation *bool 104 }{ 105 Kind: "map", 106 }, 107 Embedded: true, 108 }, 109 knownDefs: struct{ tpe, pkg, alias string }{ 110 tpe: "A", 111 pkg: "", 112 alias: "", 113 }, 114 resolved: resolvedType{ 115 GoType: "A", 116 IsMap: true, 117 SwaggerType: "object", 118 IsEmptyOmitted: true, 119 }, 120 }, 121 { 122 title: "hint as array, nullable", 123 schema: `{ 124 "type": "object", 125 "x-go-type": { 126 "type": "Mytype", 127 "import": { 128 "package": "github.com/fredbi/mymodels" 129 }, 130 "hints": { 131 "kind": "array", 132 "nullable": true 133 } 134 } 135 }`, 136 expected: &externalTypeDefinition{ 137 Type: "Mytype", 138 Import: struct { 139 Package string 140 Alias string 141 }{ 142 Package: "github.com/fredbi/mymodels", 143 // Alias: "mymodels", 144 }, 145 Hints: struct { 146 Kind string 147 Nullable *bool 148 NoValidation *bool 149 }{ 150 Kind: "array", 151 Nullable: swag.Bool(true), 152 }, 153 Embedded: false, 154 }, 155 knownDefs: struct{ tpe, pkg, alias string }{tpe: "mymodels.Mytype", pkg: "github.com/fredbi/mymodels", alias: "mymodels"}, 156 resolved: resolvedType{ 157 GoType: "mymodels.Mytype", 158 IsArray: true, 159 SwaggerType: "array", 160 IsEmptyOmitted: false, 161 Pkg: "github.com/fredbi/mymodels", 162 PkgAlias: "mymodels", 163 IsNullable: true, 164 }, 165 }, 166 { 167 title: "hint as map, unaliased", 168 schema: `{ 169 "type": "object", 170 "x-go-type": { 171 "type": "Mytype", 172 "import": { 173 "package": "github.com/fredbi/mymodels" 174 }, 175 "hints": { 176 "kind": "map" 177 } 178 } 179 }`, 180 expected: &externalTypeDefinition{ 181 Type: "Mytype", 182 Import: struct { 183 Package string 184 Alias string 185 }{ 186 Package: "github.com/fredbi/mymodels", 187 // Alias: "mymodels", 188 }, 189 Hints: struct { 190 Kind string 191 Nullable *bool 192 NoValidation *bool 193 }{ 194 Kind: "map", 195 }, 196 }, 197 knownDefs: struct{ tpe, pkg, alias string }{tpe: "mymodels.Mytype", pkg: "github.com/fredbi/mymodels", alias: "mymodels"}, 198 resolved: resolvedType{ 199 GoType: "mymodels.Mytype", 200 IsMap: true, 201 SwaggerType: "object", 202 IsEmptyOmitted: true, 203 Pkg: "github.com/fredbi/mymodels", 204 PkgAlias: "mymodels", 205 }, 206 }, 207 { 208 title: "hint as tuple, unaliased", 209 schema: `{ 210 "type": "object", 211 "x-go-type": { 212 "type": "Mytype", 213 "import": { 214 "package": "github.com/fredbi/mymodels" 215 }, 216 "hints": { 217 "kind": "tuple" 218 } 219 } 220 }`, 221 expected: &externalTypeDefinition{ 222 Type: "Mytype", 223 Import: struct { 224 Package string 225 Alias string 226 }{ 227 Package: "github.com/fredbi/mymodels", 228 // Alias: "mymodels", 229 }, 230 Hints: struct { 231 Kind string 232 Nullable *bool 233 NoValidation *bool 234 }{ 235 Kind: "tuple", 236 }, 237 }, 238 knownDefs: struct{ tpe, pkg, alias string }{tpe: "mymodels.Mytype", pkg: "github.com/fredbi/mymodels", alias: "mymodels"}, 239 resolved: resolvedType{ 240 GoType: "mymodels.Mytype", 241 IsTuple: true, 242 SwaggerType: "array", 243 IsEmptyOmitted: true, 244 Pkg: "github.com/fredbi/mymodels", 245 PkgAlias: "mymodels", 246 }, 247 }, 248 { 249 title: "hint as primitive, unaliased", 250 schema: `{ 251 "type": "number", 252 "x-go-type": { 253 "type": "Mytype", 254 "import": { 255 "package": "github.com/fredbi/mymodels" 256 }, 257 "hints": { 258 "kind": "primitive" 259 } 260 } 261 }`, 262 expected: &externalTypeDefinition{ 263 Type: "Mytype", 264 Import: struct { 265 Package string 266 Alias string 267 }{ 268 Package: "github.com/fredbi/mymodels", 269 // Alias: "mymodels", 270 }, 271 Hints: struct { 272 Kind string 273 Nullable *bool 274 NoValidation *bool 275 }{ 276 Kind: "primitive", 277 }, 278 }, 279 knownDefs: struct{ tpe, pkg, alias string }{tpe: "mymodels.Mytype", pkg: "github.com/fredbi/mymodels", alias: "mymodels"}, 280 resolved: resolvedType{ 281 GoType: "mymodels.Mytype", 282 IsPrimitive: true, 283 SwaggerType: "", 284 IsEmptyOmitted: true, 285 Pkg: "github.com/fredbi/mymodels", 286 PkgAlias: "mymodels", 287 }, 288 }, 289 { 290 title: "default model package", 291 schema: `{ 292 "type": "number", 293 "x-go-type": { 294 "type": "Mytype", 295 "hints": { 296 "kind": "primitive" 297 } 298 } 299 }`, 300 expected: &externalTypeDefinition{ 301 Type: "Mytype", 302 Import: struct { 303 Package string 304 Alias string 305 }{ 306 // Package: "github.com/example/custom", 307 // Alias: "custom", 308 }, 309 Hints: struct { 310 Kind string 311 Nullable *bool 312 NoValidation *bool 313 }{ 314 Kind: "primitive", 315 }, 316 }, 317 knownDefs: struct{ tpe, pkg, alias string }{tpe: "Mytype", pkg: "", alias: ""}, 318 resolved: resolvedType{ 319 GoType: "Mytype", 320 IsPrimitive: true, 321 SwaggerType: "", 322 IsEmptyOmitted: true, 323 Pkg: "", 324 PkgAlias: "", 325 }, 326 }, 327 } 328 } 329 330 func TestShortCircuitResolveExternal(t *testing.T) { 331 defer discardOutput()() 332 333 for i, toPin := range makeResolveExternalTypes() { 334 fixture := toPin 335 var title string 336 if fixture.title == "" { 337 title = strconv.Itoa(i) 338 } else { 339 title = fixture.title 340 } 341 t.Run(title, func(t *testing.T) { 342 jazonDoc := fixture.schema 343 doc, err := loads.Embedded([]byte(jazonDoc), []byte(jazonDoc)) 344 require.NoErrorf(t, err, "fixture %d", i) 345 346 r := newTypeResolver("models", "github.com/example/custom", doc) 347 var schema spec.Schema 348 err = json.Unmarshal([]byte(jazonDoc), &schema) 349 require.NoErrorf(t, err, "fixture %d", i) 350 351 extType, ok := hasExternalType(schema.Extensions) 352 require.Truef(t, ok, "fixture %d", i) 353 require.NotNil(t, extType) 354 355 tpe, pkg, alias := r.knownDefGoType("A", schema, r.goTypeName) 356 require.EqualValuesf(t, fixture.knownDefs, struct{ tpe, pkg, alias string }{tpe, pkg, alias}, "fixture %d", i) 357 358 resolved := r.shortCircuitResolveExternal(tpe, pkg, alias, extType, &schema, false) 359 360 require.EqualValues(t, fixture.expected, extType) 361 362 resolved.Extensions = nil // don't assert this 363 require.EqualValuesf(t, fixture.resolved, resolved, "fixture %d", i) 364 }) 365 } 366 } 367 368 type guardValidationsFixture struct { 369 Title string 370 ResolvedType string 371 Type interface { 372 Validations() spec.SchemaValidations 373 SetValidations(spec.SchemaValidations) 374 } 375 Asserter func(testing.TB, spec.SchemaValidations) 376 } 377 378 func makeGuardValidationFixtures() []guardValidationsFixture { 379 return []guardValidationsFixture{ 380 { 381 Title: "simple schema: guard array", 382 ResolvedType: "array", 383 Type: spec.NewItems(). 384 Typed("number", "int64"). 385 WithValidations(spec.CommonValidations{MinLength: swag.Int64(15), Maximum: swag.Float64(12.00)}). 386 UniqueValues(), 387 Asserter: func(t testing.TB, val spec.SchemaValidations) { 388 require.False(t, val.HasNumberValidations(), "expected no number validations, got: %#v", val) 389 require.False(t, val.HasStringValidations(), "expected no string validations, got: %#v", val) 390 require.True(t, val.HasArrayValidations(), "expected array validations, got: %#v", val) 391 }, 392 }, 393 { 394 Title: "simple schema: guard string", 395 ResolvedType: "string", 396 Type: spec.QueryParam("p1"). 397 Typed("string", "uuid"). 398 WithValidations(spec.CommonValidations{MinItems: swag.Int64(15), Maximum: swag.Float64(12.00)}). 399 WithMinLength(12), 400 Asserter: func(t testing.TB, val spec.SchemaValidations) { 401 require.False(t, val.HasNumberValidations(), "expected no number validations, got: %#v", val) 402 require.False(t, val.HasArrayValidations(), "expected no array validations, got: %#v", val) 403 require.True(t, val.HasStringValidations(), "expected string validations, got: %#v", val) 404 }, 405 }, 406 { 407 Title: "simple schema: guard file (1/3)", 408 ResolvedType: "file", 409 Type: spec.FileParam("p1"). 410 WithValidations(spec.CommonValidations{MinItems: swag.Int64(15), Maximum: swag.Float64(12.00)}). 411 WithMinLength(12), 412 Asserter: func(t testing.TB, val spec.SchemaValidations) { 413 require.False(t, val.HasNumberValidations(), "expected no number validations, got: %#v", val) 414 require.False(t, val.HasArrayValidations(), "expected no array validations, got: %#v", val) 415 require.True(t, val.HasStringValidations(), "expected string validations, got: %#v", val) 416 }, 417 }, 418 { 419 Title: "simple schema: guard file (2/3)", 420 ResolvedType: "file", 421 Type: spec.FileParam("p1"). 422 WithValidations(spec.CommonValidations{ 423 MinItems: swag.Int64(15), 424 Maximum: swag.Float64(12.00), 425 Pattern: "xyz", 426 Enum: []interface{}{"x", 34}, 427 }), 428 Asserter: func(t testing.TB, val spec.SchemaValidations) { 429 require.False(t, val.HasNumberValidations(), "expected no number validations, got: %#v", val) 430 require.False(t, val.HasArrayValidations(), "expected no array validations, got: %#v", val) 431 require.False(t, val.HasStringValidations(), "expected no string validations, got: %#v", val) 432 require.False(t, val.HasEnum(), "expected no enum validations, got: %#v", val) 433 }, 434 }, 435 { 436 Title: "schema: guard object", 437 ResolvedType: "object", 438 Type: spec.RefSchema("#/definitions/nowhere"). 439 WithValidations(spec.SchemaValidations{ 440 CommonValidations: spec.CommonValidations{ 441 MinItems: swag.Int64(15), 442 Maximum: swag.Float64(12.00), 443 }, 444 MinProperties: swag.Int64(10), 445 }). 446 WithMinLength(12), 447 Asserter: func(t testing.TB, val spec.SchemaValidations) { 448 require.False(t, val.HasNumberValidations(), "expected no number validations, got: %#v", val) 449 require.False(t, val.HasArrayValidations(), "expected no array validations, got: %#v", val) 450 require.False(t, val.HasStringValidations(), "expected no string validations, got: %#v", val) 451 require.True(t, val.HasObjectValidations(), "expected object validations, got: %#v", val) 452 }, 453 }, 454 { 455 Title: "simple schema: guard number", 456 ResolvedType: "number", 457 Type: spec.QueryParam("p1"). 458 Typed("number", "double"). 459 WithValidations(spec.CommonValidations{MinItems: swag.Int64(15), MultipleOf: swag.Float64(12.00), Pattern: "xyz"}). 460 WithMinLength(12), 461 Asserter: func(t testing.TB, val spec.SchemaValidations) { 462 require.False(t, val.HasArrayValidations(), "expected no array validations, got: %#v", val) 463 require.False(t, val.HasStringValidations(), "expected no string validations, got: %#v", val) 464 require.True(t, val.HasNumberValidations(), "expected number validations, got: %#v", val) 465 }, 466 }, 467 } 468 } 469 470 func TestGuardValidations(t *testing.T) { 471 log.SetOutput(ioutil.Discard) 472 defer func() { 473 log.SetOutput(os.Stdout) 474 }() 475 476 for _, toPin := range makeGuardValidationFixtures() { 477 testCase := toPin 478 t.Run(testCase.Title, func(t *testing.T) { 479 t.Parallel() 480 input := testCase.Type 481 guardValidations(testCase.ResolvedType, input) 482 if testCase.Asserter != nil { 483 testCase.Asserter(t, input.Validations()) 484 } 485 }) 486 } 487 } 488 489 func makeGuardFormatFixtures() []guardValidationsFixture { 490 return []guardValidationsFixture{ 491 { 492 Title: "schema: guard date format", 493 ResolvedType: "date", 494 Type: spec.StringProperty(). 495 WithValidations(spec.SchemaValidations{ 496 CommonValidations: spec.CommonValidations{ 497 MinLength: swag.Int64(15), 498 Pattern: "xyz", 499 Enum: []interface{}{"x", 34}, 500 }}), 501 Asserter: func(t testing.TB, val spec.SchemaValidations) { 502 require.True(t, val.HasStringValidations(), "expected string validations, got: %#v", val) 503 require.True(t, val.HasEnum()) 504 }, 505 }, 506 { 507 Title: "simple schema: guard binary format", 508 ResolvedType: "binary", 509 Type: spec.StringProperty(). 510 WithValidations(spec.SchemaValidations{ 511 CommonValidations: spec.CommonValidations{ 512 MinLength: swag.Int64(15), 513 Pattern: "xyz", 514 Enum: []interface{}{"x", 34}, 515 }}), 516 Asserter: func(t testing.TB, val spec.SchemaValidations) { 517 require.False(t, val.HasStringValidations(), "expected no string validations, got: %#v", val) 518 require.False(t, val.HasEnum()) 519 }, 520 }, 521 } 522 } 523 524 func TestGuardFormatConflicts(t *testing.T) { 525 defer discardOutput()() 526 527 for _, toPin := range makeGuardFormatFixtures() { 528 testCase := toPin 529 t.Run(testCase.Title, func(t *testing.T) { 530 t.Parallel() 531 input := testCase.Type 532 guardFormatConflicts(testCase.ResolvedType, input) 533 if testCase.Asserter != nil { 534 testCase.Asserter(t, input.Validations()) 535 } 536 }) 537 } 538 }