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  }