...

Source file src/github.com/go-openapi/validate/object_validator_test.go

Documentation: github.com/go-openapi/validate

     1  // Copyright 2017 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package validate
    16  
    17  import (
    18  	"reflect"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/go-openapi/spec"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  func itemsFixture() map[string]interface{} {
    28  	return map[string]interface{}{
    29  		"type":  "array",
    30  		"items": "dummy",
    31  	}
    32  }
    33  
    34  func expectAllValid(t *testing.T, ov EntityValidator, dataValid, dataInvalid map[string]interface{}) {
    35  	res := ov.Validate(dataValid)
    36  	assert.Empty(t, res.Errors)
    37  
    38  	res = ov.Validate(dataInvalid)
    39  	assert.Empty(t, res.Errors)
    40  }
    41  
    42  func expectOnlyInvalid(t *testing.T, ov EntityValidator, dataValid, dataInvalid map[string]interface{}) {
    43  	res := ov.Validate(dataValid)
    44  	assert.Empty(t, res.Errors)
    45  
    46  	res = ov.Validate(dataInvalid)
    47  	assert.NotEmpty(t, res.Errors)
    48  }
    49  
    50  func TestItemsMustBeTypeArray(t *testing.T) {
    51  	ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
    52  	dataValid := itemsFixture()
    53  	dataInvalid := map[string]interface{}{
    54  		"type":  "object",
    55  		"items": "dummy",
    56  	}
    57  	expectAllValid(t, ov, dataValid, dataInvalid)
    58  
    59  	ov.Options.EnableObjectArrayTypeCheck = true
    60  	expectOnlyInvalid(t, ov, dataValid, dataInvalid)
    61  }
    62  
    63  func TestItemsMustHaveType(t *testing.T) {
    64  	ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
    65  	dataValid := itemsFixture()
    66  	dataInvalid := map[string]interface{}{
    67  		"items": "dummy",
    68  	}
    69  	expectAllValid(t, ov, dataValid, dataInvalid)
    70  
    71  	ov.Options.EnableObjectArrayTypeCheck = true
    72  	expectOnlyInvalid(t, ov, dataValid, dataInvalid)
    73  }
    74  
    75  func TestTypeArrayMustHaveItems(t *testing.T) {
    76  	ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
    77  	dataValid := itemsFixture()
    78  	dataInvalid := map[string]interface{}{
    79  		"type": "array",
    80  		"key":  "dummy",
    81  	}
    82  	expectAllValid(t, ov, dataValid, dataInvalid)
    83  
    84  	ov.Options.EnableArrayMustHaveItemsCheck = true
    85  	expectOnlyInvalid(t, ov, dataValid, dataInvalid)
    86  }
    87  
    88  // Test edge cases in object_validator which are difficult
    89  // to simulate with specs
    90  // (this one is a trivial, just to check all methods are filled)
    91  func TestObjectValidator_EdgeCases(t *testing.T) {
    92  	s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
    93  	s.SetPath("path")
    94  	assert.Equal(t, "path", s.Path)
    95  }
    96  
    97  func TestObjectValidatorApply(t *testing.T) {
    98  	s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
    99  	require.True(t, s.Applies(&spec.Schema{}, reflect.Map))
   100  	require.False(t, s.Applies(&spec.Response{}, reflect.Map))
   101  	require.False(t, s.Applies(&struct{}{}, reflect.Map))
   102  }
   103  
   104  func TestObjectValidatorPatternProperties(t *testing.T) {
   105  	patternWithValid := spec.SchemaProperties{
   106  		"valid": spec.Schema{
   107  			SchemaProps: spec.SchemaProps{
   108  				Type: []string{"string"},
   109  			},
   110  		},
   111  		"#(.((garbled": spec.Schema{
   112  			SchemaProps: spec.SchemaProps{
   113  				Type: []string{"string"},
   114  			},
   115  		},
   116  	}
   117  
   118  	patternGarbled := spec.SchemaProperties{
   119  		"#(.((garbled": spec.Schema{
   120  			SchemaProps: spec.SchemaProps{
   121  				Type: []string{"string"},
   122  			},
   123  		},
   124  	}
   125  
   126  	t.Run("should ignore invalid regexp in pattern properties", func(t *testing.T) {
   127  		s := newObjectValidator("test", "body", nil, nil, nil, nil, nil, patternWithValid, nil, nil, nil)
   128  
   129  		res := s.Validate(map[string]interface{}{"valid": "test_string"})
   130  		require.NotNil(t, res)
   131  		require.Empty(t, res.Errors)
   132  	})
   133  
   134  	t.Run("should report forbidden property when invalid regexp in pattern properties", func(t *testing.T) {
   135  		s := newObjectValidator("test", "body", nil, nil, nil, nil, nil, patternGarbled, nil, nil, nil)
   136  
   137  		res := s.Validate(map[string]interface{}{"valid": "test_string"})
   138  		require.NotNil(t, res)
   139  		require.Empty(t, res.Errors)
   140  	})
   141  
   142  	t.Run("should ignore invalid regexp in pattern properties of additional properties", func(t *testing.T) {
   143  		s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
   144  			Schema: &spec.Schema{},
   145  			Allows: false,
   146  		}, patternWithValid, nil, nil, nil)
   147  
   148  		res := s.Validate(map[string]interface{}{"valid": "test_string"})
   149  		require.NotNil(t, res)
   150  		require.Empty(t, res.Errors)
   151  	})
   152  
   153  	t.Run("should report forbidden property when invalid regexp in pattern properties of additional properties", func(t *testing.T) {
   154  		s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
   155  			Schema: &spec.Schema{},
   156  			Allows: false,
   157  		}, patternGarbled, nil, nil, nil)
   158  
   159  		res := s.Validate(map[string]interface{}{"valid": "test_string"})
   160  		require.NotNil(t, res)
   161  		require.Len(t, res.Errors, 1)
   162  		require.ErrorContains(t, res.Errors[0], "forbidden property")
   163  	})
   164  }
   165  
   166  func TestObjectValidatorNilData(t *testing.T) {
   167  	t.Run("object Validate should NOT panic on nil data", func(t *testing.T) {
   168  		s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
   169  		require.NotPanics(t, func() {
   170  			_ = s.Validate(nil)
   171  		})
   172  
   173  		res := s.Validate(nil)
   174  		require.NotNil(t, res)
   175  		require.Empty(t, res.Errors)
   176  	})
   177  
   178  	t.Run("object Validate should validate required on nil data", func(t *testing.T) {
   179  		s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, nil)
   180  		res := s.Validate(nil)
   181  		require.NotNil(t, res)
   182  		require.NotEmpty(t, res.Errors)
   183  	})
   184  
   185  	t.Run("object Validate should NOT panic on unexpected input", func(t *testing.T) {
   186  		s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, nil)
   187  		res := s.Validate(map[string]string{"wanted": "not expected"})
   188  		require.NotNil(t, res)
   189  		require.Len(t, res.Errors, 1)
   190  		require.ErrorContains(t, res.Errors[0], "expected an object")
   191  	})
   192  
   193  	t.Run("object Validate should NOT panic on nil input (with array type check)", func(t *testing.T) {
   194  		s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, &SchemaValidatorOptions{
   195  			EnableArrayMustHaveItemsCheck: true,
   196  			EnableObjectArrayTypeCheck:    true,
   197  		})
   198  		res := s.Validate(nil)
   199  		require.NotNil(t, res)
   200  		require.Len(t, res.Errors, 1)
   201  		require.ErrorContains(t, res.Errors[0], "wanted is required")
   202  	})
   203  }
   204  
   205  func TestObjectValidatorWithHeaderProperty(t *testing.T) {
   206  	t.Run("should report extra information about forbidden $ref in this context", func(t *testing.T) {
   207  		s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
   208  			Schema: &spec.Schema{},
   209  			Allows: false,
   210  		}, nil, nil, nil, nil)
   211  
   212  		res := s.Validate(map[string]interface{}{
   213  			"headers": map[string]interface{}{
   214  				"X-Custom": map[string]interface{}{
   215  					"$ref": "#/definitions/myHeader",
   216  				},
   217  			},
   218  		})
   219  		require.NotNil(t, res)
   220  		require.Len(t, res.Errors, 2)
   221  		found := 0
   222  		for _, err := range res.Errors {
   223  			switch {
   224  			case strings.Contains(err.Error(), "forbidden property"):
   225  				found++
   226  			case strings.Contains(err.Error(), "$ref are not allowed in headers"):
   227  				found++
   228  			}
   229  		}
   230  		require.Equal(t, 2, found)
   231  	})
   232  
   233  	t.Run("should NOT report extra information when header is not detected", func(t *testing.T) {
   234  		s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
   235  			Schema: &spec.Schema{},
   236  			Allows: false,
   237  		}, nil, nil, nil, nil)
   238  
   239  		t.Run("when key is not headers", func(t *testing.T) {
   240  			res := s.Validate(map[string]interface{}{
   241  				"Headers": map[string]interface{}{
   242  					"X-Custom": map[string]interface{}{
   243  						"$ref": "#/definitions/myHeader",
   244  					},
   245  				},
   246  			})
   247  			require.NotNil(t, res)
   248  			require.Len(t, res.Errors, 1)
   249  		})
   250  
   251  		t.Run("when key is not the expected map", func(t *testing.T) {
   252  			res := s.Validate(map[string]interface{}{
   253  				"headers": map[string]string{
   254  					"X-Custom": "#/definitions/myHeader",
   255  				},
   256  			})
   257  			require.NotNil(t, res)
   258  			require.Len(t, res.Errors, 1)
   259  		})
   260  
   261  		t.Run("when key content not the expected map", func(t *testing.T) {
   262  			res := s.Validate(map[string]interface{}{
   263  				"headers": map[string]interface{}{
   264  					"X-Custom": 1,
   265  				},
   266  			})
   267  			require.NotNil(t, res)
   268  			require.Len(t, res.Errors, 1)
   269  		})
   270  
   271  		t.Run("when key content not the expected map", func(t *testing.T) {
   272  			res := s.Validate(map[string]interface{}{
   273  				"headers": map[string]interface{}{
   274  					"X-Custom": nil,
   275  				},
   276  			})
   277  			require.NotNil(t, res)
   278  			require.Len(t, res.Errors, 1)
   279  		})
   280  
   281  		t.Run("when header is not a valid $ref", func(t *testing.T) {
   282  			res := s.Validate(map[string]interface{}{
   283  				"headers": map[string]interface{}{
   284  					"X-Custom": map[string]interface{}{
   285  						"$ref": 1,
   286  					},
   287  				},
   288  			})
   289  			require.NotNil(t, res)
   290  			require.Len(t, res.Errors, 1)
   291  		})
   292  
   293  		t.Run("when header is not a $ref", func(t *testing.T) {
   294  			res := s.Validate(map[string]interface{}{
   295  				"headers": map[string]interface{}{
   296  					"X-Custom": map[string]interface{}{
   297  						"ref": "#/definitions/myHeader",
   298  					},
   299  				},
   300  			})
   301  			require.NotNil(t, res)
   302  			require.Len(t, res.Errors, 1)
   303  		})
   304  	})
   305  }
   306  
   307  func TestObjectValidatorWithDefault(t *testing.T) {
   308  	/*
   309  		maxProperties, minProperties *int64, required []string, properties spec.SchemaProperties,
   310  		additionalProperties *spec.SchemaOrBool, patternProperties spec.SchemaProperties,
   311  		root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *objectValidator {
   312  	*/
   313  	t.Run("should accept required populated with a default", func(t *testing.T) {
   314  		s := newObjectValidator("test", "body", nil, nil,
   315  			[]string{"wanted"},
   316  			spec.SchemaProperties{
   317  				"wanted": spec.Schema{
   318  					SchemaProps: spec.SchemaProps{
   319  						Default: "default_value"},
   320  				},
   321  			},
   322  			nil, nil,
   323  			nil, nil, nil)
   324  		res := s.Validate(nil)
   325  		require.NotNil(t, res)
   326  		require.Empty(t, res.Errors)
   327  	})
   328  }
   329  

View as plain text