File: //proc/self/root/opt/go/pkg/mod/github.com/go-openapi/
[email protected]/object_validator_test.go
// Copyright 2017 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package validate
import (
"reflect"
"strings"
"testing"
"github.com/go-openapi/spec"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func itemsFixture() map[string]interface{} {
return map[string]interface{}{
"type": "array",
"items": "dummy",
}
}
func expectAllValid(t *testing.T, ov EntityValidator, dataValid, dataInvalid map[string]interface{}) {
res := ov.Validate(dataValid)
assert.Empty(t, res.Errors)
res = ov.Validate(dataInvalid)
assert.Empty(t, res.Errors)
}
func expectOnlyInvalid(t *testing.T, ov EntityValidator, dataValid, dataInvalid map[string]interface{}) {
res := ov.Validate(dataValid)
assert.Empty(t, res.Errors)
res = ov.Validate(dataInvalid)
assert.NotEmpty(t, res.Errors)
}
func TestItemsMustBeTypeArray(t *testing.T) {
ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
dataValid := itemsFixture()
dataInvalid := map[string]interface{}{
"type": "object",
"items": "dummy",
}
expectAllValid(t, ov, dataValid, dataInvalid)
ov.Options.EnableObjectArrayTypeCheck = true
expectOnlyInvalid(t, ov, dataValid, dataInvalid)
}
func TestItemsMustHaveType(t *testing.T) {
ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
dataValid := itemsFixture()
dataInvalid := map[string]interface{}{
"items": "dummy",
}
expectAllValid(t, ov, dataValid, dataInvalid)
ov.Options.EnableObjectArrayTypeCheck = true
expectOnlyInvalid(t, ov, dataValid, dataInvalid)
}
func TestTypeArrayMustHaveItems(t *testing.T) {
ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
dataValid := itemsFixture()
dataInvalid := map[string]interface{}{
"type": "array",
"key": "dummy",
}
expectAllValid(t, ov, dataValid, dataInvalid)
ov.Options.EnableArrayMustHaveItemsCheck = true
expectOnlyInvalid(t, ov, dataValid, dataInvalid)
}
// Test edge cases in object_validator which are difficult
// to simulate with specs
// (this one is a trivial, just to check all methods are filled)
func TestObjectValidator_EdgeCases(t *testing.T) {
s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
s.SetPath("path")
assert.Equal(t, "path", s.Path)
}
func TestObjectValidatorApply(t *testing.T) {
s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
require.True(t, s.Applies(&spec.Schema{}, reflect.Map))
require.False(t, s.Applies(&spec.Response{}, reflect.Map))
require.False(t, s.Applies(&struct{}{}, reflect.Map))
}
func TestObjectValidatorPatternProperties(t *testing.T) {
patternWithValid := spec.SchemaProperties{
"valid": spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
"#(.((garbled": spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
}
patternGarbled := spec.SchemaProperties{
"#(.((garbled": spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
}
t.Run("should ignore invalid regexp in pattern properties", func(t *testing.T) {
s := newObjectValidator("test", "body", nil, nil, nil, nil, nil, patternWithValid, nil, nil, nil)
res := s.Validate(map[string]interface{}{"valid": "test_string"})
require.NotNil(t, res)
require.Empty(t, res.Errors)
})
t.Run("should report forbidden property when invalid regexp in pattern properties", func(t *testing.T) {
s := newObjectValidator("test", "body", nil, nil, nil, nil, nil, patternGarbled, nil, nil, nil)
res := s.Validate(map[string]interface{}{"valid": "test_string"})
require.NotNil(t, res)
require.Empty(t, res.Errors)
})
t.Run("should ignore invalid regexp in pattern properties of additional properties", func(t *testing.T) {
s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
Schema: &spec.Schema{},
Allows: false,
}, patternWithValid, nil, nil, nil)
res := s.Validate(map[string]interface{}{"valid": "test_string"})
require.NotNil(t, res)
require.Empty(t, res.Errors)
})
t.Run("should report forbidden property when invalid regexp in pattern properties of additional properties", func(t *testing.T) {
s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
Schema: &spec.Schema{},
Allows: false,
}, patternGarbled, nil, nil, nil)
res := s.Validate(map[string]interface{}{"valid": "test_string"})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
require.ErrorContains(t, res.Errors[0], "forbidden property")
})
}
func TestObjectValidatorNilData(t *testing.T) {
t.Run("object Validate should NOT panic on nil data", func(t *testing.T) {
s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
require.NotPanics(t, func() {
_ = s.Validate(nil)
})
res := s.Validate(nil)
require.NotNil(t, res)
require.Empty(t, res.Errors)
})
t.Run("object Validate should validate required on nil data", func(t *testing.T) {
s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, nil)
res := s.Validate(nil)
require.NotNil(t, res)
require.NotEmpty(t, res.Errors)
})
t.Run("object Validate should NOT panic on unexpected input", func(t *testing.T) {
s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, nil)
res := s.Validate(map[string]string{"wanted": "not expected"})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
require.ErrorContains(t, res.Errors[0], "expected an object")
})
t.Run("object Validate should NOT panic on nil input (with array type check)", func(t *testing.T) {
s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, &SchemaValidatorOptions{
EnableArrayMustHaveItemsCheck: true,
EnableObjectArrayTypeCheck: true,
})
res := s.Validate(nil)
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
require.ErrorContains(t, res.Errors[0], "wanted is required")
})
}
func TestObjectValidatorWithHeaderProperty(t *testing.T) {
t.Run("should report extra information about forbidden $ref in this context", func(t *testing.T) {
s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
Schema: &spec.Schema{},
Allows: false,
}, nil, nil, nil, nil)
res := s.Validate(map[string]interface{}{
"headers": map[string]interface{}{
"X-Custom": map[string]interface{}{
"$ref": "#/definitions/myHeader",
},
},
})
require.NotNil(t, res)
require.Len(t, res.Errors, 2)
found := 0
for _, err := range res.Errors {
switch {
case strings.Contains(err.Error(), "forbidden property"):
found++
case strings.Contains(err.Error(), "$ref are not allowed in headers"):
found++
}
}
require.Equal(t, 2, found)
})
t.Run("should NOT report extra information when header is not detected", func(t *testing.T) {
s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
Schema: &spec.Schema{},
Allows: false,
}, nil, nil, nil, nil)
t.Run("when key is not headers", func(t *testing.T) {
res := s.Validate(map[string]interface{}{
"Headers": map[string]interface{}{
"X-Custom": map[string]interface{}{
"$ref": "#/definitions/myHeader",
},
},
})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
})
t.Run("when key is not the expected map", func(t *testing.T) {
res := s.Validate(map[string]interface{}{
"headers": map[string]string{
"X-Custom": "#/definitions/myHeader",
},
})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
})
t.Run("when key content not the expected map", func(t *testing.T) {
res := s.Validate(map[string]interface{}{
"headers": map[string]interface{}{
"X-Custom": 1,
},
})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
})
t.Run("when key content not the expected map", func(t *testing.T) {
res := s.Validate(map[string]interface{}{
"headers": map[string]interface{}{
"X-Custom": nil,
},
})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
})
t.Run("when header is not a valid $ref", func(t *testing.T) {
res := s.Validate(map[string]interface{}{
"headers": map[string]interface{}{
"X-Custom": map[string]interface{}{
"$ref": 1,
},
},
})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
})
t.Run("when header is not a $ref", func(t *testing.T) {
res := s.Validate(map[string]interface{}{
"headers": map[string]interface{}{
"X-Custom": map[string]interface{}{
"ref": "#/definitions/myHeader",
},
},
})
require.NotNil(t, res)
require.Len(t, res.Errors, 1)
})
})
}
func TestObjectValidatorWithDefault(t *testing.T) {
/*
maxProperties, minProperties *int64, required []string, properties spec.SchemaProperties,
additionalProperties *spec.SchemaOrBool, patternProperties spec.SchemaProperties,
root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *objectValidator {
*/
t.Run("should accept required populated with a default", func(t *testing.T) {
s := newObjectValidator("test", "body", nil, nil,
[]string{"wanted"},
spec.SchemaProperties{
"wanted": spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "default_value"},
},
},
nil, nil,
nil, nil, nil)
res := s.Validate(nil)
require.NotNil(t, res)
require.Empty(t, res.Errors)
})
}