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