open-vault/sdk/framework/field_data_test.go
Anton Averchenkov 9696600e59
Add response schema validation methods & test helpers (#18635)
This pull request adds 3 functions (and corresponding tests):

`testhelpers/response_validation.go`:

  - `ValidateResponse`
  - `ValidateResponseData`
  
field_data.go:

  - `ValidateStrict` (has the "strict" validation logic)

The functions are primarily meant to be used in tests to ensure that the responses are consistent with the defined response schema. An example of how the functions can be used in tests can be found in #18636.

### Background

This PR is part of the ongoing work to add structured responses in Vault OpenAPI (VLT-234)
2023-01-13 14:55:56 -05:00

1275 lines
24 KiB
Go

package framework
import (
"encoding/json"
"net/http"
"reflect"
"testing"
"time"
)
func TestFieldDataGet(t *testing.T) {
cases := map[string]struct {
Schema map[string]*FieldSchema
Raw map[string]interface{}
Key string
Value interface{}
ExpectError bool
}{
"string type, string value": {
map[string]*FieldSchema{
"foo": {Type: TypeString},
},
map[string]interface{}{
"foo": "bar",
},
"foo",
"bar",
false,
},
"string type, int value": {
map[string]*FieldSchema{
"foo": {Type: TypeString},
},
map[string]interface{}{
"foo": 42,
},
"foo",
"42",
false,
},
"string type, unset value": {
map[string]*FieldSchema{
"foo": {Type: TypeString},
},
map[string]interface{}{},
"foo",
"",
false,
},
"string type, unset value with default": {
map[string]*FieldSchema{
"foo": {
Type: TypeString,
Default: "bar",
},
},
map[string]interface{}{},
"foo",
"bar",
false,
},
"lowercase string type, lowercase string value": {
map[string]*FieldSchema{
"foo": {Type: TypeLowerCaseString},
},
map[string]interface{}{
"foo": "bar",
},
"foo",
"bar",
false,
},
"lowercase string type, mixed-case string value": {
map[string]*FieldSchema{
"foo": {Type: TypeLowerCaseString},
},
map[string]interface{}{
"foo": "BaR",
},
"foo",
"bar",
false,
},
"lowercase string type, int value": {
map[string]*FieldSchema{
"foo": {Type: TypeLowerCaseString},
},
map[string]interface{}{
"foo": 42,
},
"foo",
"42",
false,
},
"lowercase string type, unset value": {
map[string]*FieldSchema{
"foo": {Type: TypeLowerCaseString},
},
map[string]interface{}{},
"foo",
"",
false,
},
"lowercase string type, unset value with lowercase default": {
map[string]*FieldSchema{
"foo": {
Type: TypeLowerCaseString,
Default: "bar",
},
},
map[string]interface{}{},
"foo",
"bar",
false,
},
"int type, int value": {
map[string]*FieldSchema{
"foo": {Type: TypeInt},
},
map[string]interface{}{
"foo": 42,
},
"foo",
42,
false,
},
"bool type, bool value": {
map[string]*FieldSchema{
"foo": {Type: TypeBool},
},
map[string]interface{}{
"foo": false,
},
"foo",
false,
false,
},
"map type, map value": {
map[string]*FieldSchema{
"foo": {Type: TypeMap},
},
map[string]interface{}{
"foo": map[string]interface{}{
"child": true,
},
},
"foo",
map[string]interface{}{
"child": true,
},
false,
},
"duration type, string value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": "42",
},
"foo",
42,
false,
},
"duration type, string duration value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": "42m",
},
"foo",
2520,
false,
},
"duration type, int value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": 42,
},
"foo",
42,
false,
},
"duration type, float value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": 42.0,
},
"foo",
42,
false,
},
"duration type, nil value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": nil,
},
"foo",
0,
false,
},
"duration type, 0 value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": 0,
},
"foo",
0,
false,
},
"signed duration type, positive string value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": "42",
},
"foo",
42,
false,
},
"signed duration type, positive string duration value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": "42m",
},
"foo",
2520,
false,
},
"signed duration type, positive int value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": 42,
},
"foo",
42,
false,
},
"signed duration type, positive float value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": 42.0,
},
"foo",
42,
false,
},
"signed duration type, negative string value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": "-42",
},
"foo",
-42,
false,
},
"signed duration type, negative string duration value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": "-42m",
},
"foo",
-2520,
false,
},
"signed duration type, negative int value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": -42,
},
"foo",
-42,
false,
},
"signed duration type, negative float value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": -42.0,
},
"foo",
-42,
false,
},
"signed duration type, nil value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": nil,
},
"foo",
0,
false,
},
"signed duration type, 0 value": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{
"foo": 0,
},
"foo",
0,
false,
},
"slice type, empty slice": {
map[string]*FieldSchema{
"foo": {Type: TypeSlice},
},
map[string]interface{}{
"foo": []interface{}{},
},
"foo",
[]interface{}{},
false,
},
"slice type, filled, mixed slice": {
map[string]*FieldSchema{
"foo": {Type: TypeSlice},
},
map[string]interface{}{
"foo": []interface{}{123, "abc"},
},
"foo",
[]interface{}{123, "abc"},
false,
},
"string slice type, filled slice": {
map[string]*FieldSchema{
"foo": {Type: TypeStringSlice},
},
map[string]interface{}{
"foo": []interface{}{123, "abc"},
},
"foo",
[]string{"123", "abc"},
false,
},
"string slice type, single value": {
map[string]*FieldSchema{
"foo": {Type: TypeStringSlice},
},
map[string]interface{}{
"foo": "abc",
},
"foo",
[]string{"abc"},
false,
},
"string slice type, empty string": {
map[string]*FieldSchema{
"foo": {Type: TypeStringSlice},
},
map[string]interface{}{
"foo": "",
},
"foo",
[]string{},
false,
},
"comma string slice type, empty string": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": "",
},
"foo",
[]string{},
false,
},
"comma string slice type, comma string with one value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": "value1",
},
"foo",
[]string{"value1"},
false,
},
"comma string slice type, comma string with multi value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": "value1,value2,value3",
},
"foo",
[]string{"value1", "value2", "value3"},
false,
},
"comma string slice type, nil string slice value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": "",
},
"foo",
[]string{},
false,
},
"comma string slice type, string slice with one value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": []interface{}{"value1"},
},
"foo",
[]string{"value1"},
false,
},
"comma string slice type, string slice with multi value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": []interface{}{"value1", "value2", "value3"},
},
"foo",
[]string{"value1", "value2", "value3"},
false,
},
"comma string slice type, empty string slice value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": []interface{}{},
},
"foo",
[]string{},
false,
},
"comma int slice type, comma int with one value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": 1,
},
"foo",
[]int{1},
false,
},
"comma int slice type, comma int with multi value slice": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": []int{1, 2, 3},
},
"foo",
[]int{1, 2, 3},
false,
},
"comma int slice type, comma int with multi value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": "1,2,3",
},
"foo",
[]int{1, 2, 3},
false,
},
"comma int slice type, nil int slice value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": "",
},
"foo",
[]int{},
false,
},
"comma int slice type, int slice with one value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": []interface{}{"1"},
},
"foo",
[]int{1},
false,
},
"comma int slice type, int slice with multi value strings": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": []interface{}{"1", "2", "3"},
},
"foo",
[]int{1, 2, 3},
false,
},
"comma int slice type, int slice with multi value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": []interface{}{1, 2, 3},
},
"foo",
[]int{1, 2, 3},
false,
},
"comma int slice type, empty int slice value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": []interface{}{},
},
"foo",
[]int{},
false,
},
"comma int slice type, json number": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaIntSlice},
},
map[string]interface{}{
"foo": json.Number("1"),
},
"foo",
[]int{1},
false,
},
"name string type, valid string": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
},
map[string]interface{}{
"foo": "bar",
},
"foo",
"bar",
false,
},
"name string type, valid value with special characters": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
},
map[string]interface{}{
"foo": "bar.baz-bay123",
},
"foo",
"bar.baz-bay123",
false,
},
"keypair type, valid value map type": {
map[string]*FieldSchema{
"foo": {Type: TypeKVPairs},
},
map[string]interface{}{
"foo": map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": 1,
},
},
"foo",
map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "1",
},
false,
},
"keypair type, list of equal sign delim key pairs type": {
map[string]*FieldSchema{
"foo": {Type: TypeKVPairs},
},
map[string]interface{}{
"foo": []interface{}{"key1=value1", "key2=value2", "key3=1"},
},
"foo",
map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "1",
},
false,
},
"keypair type, single equal sign delim value": {
map[string]*FieldSchema{
"foo": {Type: TypeKVPairs},
},
map[string]interface{}{
"foo": "key1=value1",
},
"foo",
map[string]string{
"key1": "value1",
},
false,
},
"type header, keypair string array": {
map[string]*FieldSchema{
"foo": {Type: TypeHeader},
},
map[string]interface{}{
"foo": []interface{}{"key1:value1", "key2:value2", "key3:1"},
},
"foo",
http.Header{
"Key1": []string{"value1"},
"Key2": []string{"value2"},
"Key3": []string{"1"},
},
false,
},
"type header, b64 string": {
map[string]*FieldSchema{
"foo": {Type: TypeHeader},
},
map[string]interface{}{
"foo": "eyJDb250ZW50LUxlbmd0aCI6IFsiNDMiXSwgIlVzZXItQWdlbnQiOiBbImF3cy1zZGstZ28vMS40LjEyIChnbzEuNy4xOyBsaW51eDsgYW1kNjQpIl0sICJYLVZhdWx0LUFXU0lBTS1TZXJ2ZXItSWQiOiBbInZhdWx0LmV4YW1wbGUuY29tIl0sICJYLUFtei1EYXRlIjogWyIyMDE2MDkzMFQwNDMxMjFaIl0sICJDb250ZW50LVR5cGUiOiBbImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD11dGYtOCJdLCAiQXV0aG9yaXphdGlvbiI6IFsiQVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPWZvby8yMDE2MDkzMC91cy1lYXN0LTEvc3RzL2F3czRfcmVxdWVzdCwgU2lnbmVkSGVhZGVycz1jb250ZW50LWxlbmd0aDtjb250ZW50LXR5cGU7aG9zdDt4LWFtei1kYXRlO3gtdmF1bHQtc2VydmVyLCBTaWduYXR1cmU9YTY5ZmQ3NTBhMzQ0NWM0ZTU1M2UxYjNlNzlkM2RhOTBlZWY1NDA0N2YxZWI0ZWZlOGZmYmM5YzQyOGMyNjU1YiJdLCAiRm9vIjogNDJ9",
},
"foo",
http.Header{
"Content-Length": []string{"43"},
"User-Agent": []string{"aws-sdk-go/1.4.12 (go1.7.1; linux; amd64)"},
"X-Vault-Awsiam-Server-Id": []string{"vault.example.com"},
"X-Amz-Date": []string{"20160930T043121Z"},
"Content-Type": []string{"application/x-www-form-urlencoded; charset=utf-8"},
"Authorization": []string{"AWS4-HMAC-SHA256 Credential=foo/20160930/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-server, Signature=a69fd750a3445c4e553e1b3e79d3da90eef54047f1eb4efe8ffbc9c428c2655b"},
"Foo": []string{"42"},
},
false,
},
"type header, json string": {
map[string]*FieldSchema{
"foo": {Type: TypeHeader},
},
map[string]interface{}{
"foo": `{"hello":"world","bonjour":["monde","dieu"], "Guten Tag": 42, "你好": ["10", 20, 3.14]}`,
},
"foo",
http.Header{
"Hello": []string{"world"},
"Bonjour": []string{"monde", "dieu"},
"Guten Tag": []string{"42"},
"你好": []string{"10", "20", "3.14"},
},
false,
},
"type header, keypair string array with dupe key": {
map[string]*FieldSchema{
"foo": {Type: TypeHeader},
},
map[string]interface{}{
"foo": []interface{}{"key1:value1", "key2:value2", "key3:1", "key3:true"},
},
"foo",
http.Header{
"Key1": []string{"value1"},
"Key2": []string{"value2"},
"Key3": []string{"1", "true"},
},
false,
},
"type header, map string slice": {
map[string]*FieldSchema{
"foo": {Type: TypeHeader},
},
map[string]interface{}{
"foo": map[string][]string{
"key1": {"value1"},
"key2": {"value2"},
"key3": {"1"},
},
},
"foo",
http.Header{
"Key1": []string{"value1"},
"Key2": []string{"value2"},
"Key3": []string{"1"},
},
false,
},
"name string type, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
},
map[string]interface{}{},
"foo",
"",
false,
},
"string type, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeString},
},
map[string]interface{}{},
"foo",
"",
false,
},
"type int, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeInt},
},
map[string]interface{}{},
"foo",
0,
false,
},
"type bool, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeBool},
},
map[string]interface{}{},
"foo",
false,
false,
},
"type map, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeMap},
},
map[string]interface{}{},
"foo",
map[string]interface{}{},
false,
},
"type duration second, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{},
"foo",
0,
false,
},
"type signed duration second, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeSignedDurationSecond},
},
map[string]interface{}{},
"foo",
0,
false,
},
"type slice, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeSlice},
},
map[string]interface{}{},
"foo",
[]interface{}{},
false,
},
"type string slice, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeStringSlice},
},
map[string]interface{}{},
"foo",
[]string{},
false,
},
"type comma string slice, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{},
"foo",
[]string{},
false,
},
"comma string slice type, single JSON number value": {
map[string]*FieldSchema{
"foo": {Type: TypeCommaStringSlice},
},
map[string]interface{}{
"foo": json.Number("123"),
},
"foo",
[]string{"123"},
false,
},
"type kv pair, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeKVPairs},
},
map[string]interface{}{},
"foo",
map[string]string{},
false,
},
"type header, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeHeader},
},
map[string]interface{}{},
"foo",
http.Header{},
false,
},
"float type, positive with decimals, as string": {
map[string]*FieldSchema{
"foo": {Type: TypeFloat},
},
map[string]interface{}{
"foo": "1234567.891234567",
},
"foo",
1234567.891234567,
false,
},
"float type, negative with decimals, as string": {
map[string]*FieldSchema{
"foo": {Type: TypeFloat},
},
map[string]interface{}{
"foo": "-1234567.891234567",
},
"foo",
-1234567.891234567,
false,
},
"float type, positive without decimals": {
map[string]*FieldSchema{
"foo": {Type: TypeFloat},
},
map[string]interface{}{
"foo": 1234567,
},
"foo",
1234567.0,
false,
},
"type float, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeFloat},
},
map[string]interface{}{},
"foo",
0.0,
false,
},
"type float, invalid value": {
map[string]*FieldSchema{
"foo": {Type: TypeFloat},
},
map[string]interface{}{
"foo": "invalid0.0",
},
"foo",
0.0,
true,
},
"type time, not supplied": {
map[string]*FieldSchema{
"foo": {Type: TypeTime},
},
map[string]interface{}{},
"foo",
time.Time{},
false,
},
"type time, string value": {
map[string]*FieldSchema{
"foo": {Type: TypeTime},
},
map[string]interface{}{
"foo": "2021-12-11T09:08:07Z",
},
"foo",
// Comparison uses DeepEqual() so better match exactly,
// can't have a different location.
time.Date(2021, 12, 11, 9, 8, 7, 0, time.UTC),
false,
},
"type time, invalid value": {
map[string]*FieldSchema{
"foo": {Type: TypeTime},
},
map[string]interface{}{
"foo": "2021-13-11T09:08:07+02:00",
},
"foo",
time.Time{},
true,
},
}
for name, tc := range cases {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
data := &FieldData{
Raw: tc.Raw,
Schema: tc.Schema,
}
err := data.Validate()
switch {
case tc.ExpectError && err == nil:
t.Fatalf("expected error")
case tc.ExpectError && err != nil:
return
case !tc.ExpectError && err != nil:
t.Fatal(err)
default:
// Continue if !tc.ExpectError && err == nil
}
actual := data.Get(tc.Key)
if !reflect.DeepEqual(actual, tc.Value) {
t.Fatalf("Expected: %#v\nGot: %#v", tc.Value, actual)
}
})
}
}
func TestFieldDataGet_Error(t *testing.T) {
cases := map[string]struct {
Schema map[string]*FieldSchema
Raw map[string]interface{}
Key string
}{
"name string type, invalid value with invalid characters": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
},
map[string]interface{}{
"foo": "bar baz",
},
"foo",
},
"name string type, invalid value with special characters at beginning": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
},
map[string]interface{}{
"foo": ".barbaz",
},
"foo",
},
"name string type, invalid value with special characters at end": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
},
map[string]interface{}{
"foo": "barbaz-",
},
"foo",
},
"name string type, empty string": {
map[string]*FieldSchema{
"foo": {Type: TypeNameString},
},
map[string]interface{}{
"foo": "",
},
"foo",
},
"keypair type, csv version empty key name": {
map[string]*FieldSchema{
"foo": {Type: TypeKVPairs},
},
map[string]interface{}{
"foo": []interface{}{"=value1", "key2=value2", "key3=1"},
},
"foo",
},
"duration type, negative string value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": "-42",
},
"foo",
},
"duration type, negative string duration value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": "-42m",
},
"foo",
},
"duration type, negative int value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": -42,
},
"foo",
},
"duration type, negative float value": {
map[string]*FieldSchema{
"foo": {Type: TypeDurationSecond},
},
map[string]interface{}{
"foo": -42.0,
},
"foo",
},
}
for name, tc := range cases {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
data := &FieldData{
Raw: tc.Raw,
Schema: tc.Schema,
}
got, _, err := data.GetOkErr(tc.Key)
if err == nil {
t.Fatalf("error expected, none received, got result: %#v", got)
}
})
}
}
func TestFieldDataGetFirst(t *testing.T) {
data := &FieldData{
Raw: map[string]interface{}{
"foo": "bar",
"fizz": "buzz",
},
Schema: map[string]*FieldSchema{
"foo": {Type: TypeNameString},
"fizz": {Type: TypeNameString},
},
}
result, ok := data.GetFirst("foo", "fizz")
if !ok {
t.Fatal("should have found value for foo")
}
if result.(string) != "bar" {
t.Fatal("should have gotten bar for foo")
}
result, ok = data.GetFirst("fizz", "foo")
if !ok {
t.Fatal("should have found value for fizz")
}
if result.(string) != "buzz" {
t.Fatal("should have gotten buzz for fizz")
}
_, ok = data.GetFirst("cats")
if ok {
t.Fatal("shouldn't have gotten anything for cats")
}
}
func TestValidateStrict(t *testing.T) {
cases := map[string]struct {
Schema map[string]*FieldSchema
Raw map[string]interface{}
ExpectError bool
}{
"string type, string value": {
map[string]*FieldSchema{
"foo": {Type: TypeString},
},
map[string]interface{}{
"foo": "bar",
},
false,
},
"string type, int value": {
map[string]*FieldSchema{
"foo": {Type: TypeString},
},
map[string]interface{}{
"foo": 42,
},
false,
},
"string type, unset value": {
map[string]*FieldSchema{
"foo": {Type: TypeString},
},
map[string]interface{}{},
false,
},
"string type, unset required value": {
map[string]*FieldSchema{
"foo": {
Type: TypeString,
Required: true,
},
},
map[string]interface{}{},
true,
},
"value not in schema": {
map[string]*FieldSchema{
"foo": {
Type: TypeString,
Required: true,
},
},
map[string]interface{}{
"foo": 42,
"bar": 43,
},
true,
},
"value not in schema, empty schema": {
map[string]*FieldSchema{},
map[string]interface{}{
"foo": 42,
"bar": 43,
},
true,
},
"value not in schema, nil schema": {
nil,
map[string]interface{}{
"foo": 42,
"bar": 43,
},
false,
},
"type time, invalid value": {
map[string]*FieldSchema{
"foo": {Type: TypeTime},
},
map[string]interface{}{
"foo": "2021-13-11T09:08:07+02:00",
},
true,
},
}
for name, tc := range cases {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
data := &FieldData{
Raw: tc.Raw,
Schema: tc.Schema,
}
err := data.ValidateStrict()
if err == nil && tc.ExpectError == true {
t.Fatalf("expected an error, got nil")
}
if err != nil && tc.ExpectError == false {
t.Fatalf("unexpected error: %v", err)
}
})
}
}