Repository navigation
Expand file tree
/
Copy pathmap_test.go
More file actions
171 lines (162 loc) · 8.31 KB
/
Copy pathmap_test.go
File metadata and controls
171 lines (162 loc) · 8.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package validation
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
func TestMap(t *testing.T) {
var m0 map[string]interface{}
m1 := map[string]interface{}{"A": "abc", "B": "xyz", "c": "abc", "D": (*string)(nil), "F": (*String123)(nil), "H": []string{"abc", "abc"}, "I": map[string]string{"foo": "abc"}}
m2 := map[string]interface{}{"E": String123("xyz"), "F": (*String123)(nil)}
m3 := map[string]interface{}{"M3": Model3{}}
m4 := map[string]interface{}{"M3": Model3{A: "abc"}}
m5 := map[string]interface{}{"A": "internal", "B": ""}
m6 := map[int]string{11: "abc", 22: "xyz"}
tests := []struct {
tag string
model interface{}
rules []*KeyRules
err string
}{
// empty rules
{"t1.1", m1, []*KeyRules{}, ""},
{"t1.2", m1, []*KeyRules{Key("A"), Key("B")}, ""},
// normal rules
{"t2.1", m1, []*KeyRules{Key("A", &validateAbc{}), Key("B", &validateXyz{})}, ""},
{"t2.2", m1, []*KeyRules{Key("A", &validateXyz{}), Key("B", &validateAbc{})}, "A: error xyz; B: error abc."},
{"t2.3", m1, []*KeyRules{Key("A", &validateXyz{}), Key("c", &validateXyz{})}, "A: error xyz; c: error xyz."},
{"t2.4", m1, []*KeyRules{Key("D", Length(0, 5))}, ""},
{"t2.5", m1, []*KeyRules{Key("F", Length(0, 5))}, ""},
{"t2.6", m1, []*KeyRules{Key("H", Each(&validateAbc{})), Key("I", Each(&validateAbc{}))}, ""},
{"t2.7", m1, []*KeyRules{Key("H", Each(&validateXyz{})), Key("I", Each(&validateXyz{}))}, "H: (0: error xyz; 1: error xyz.); I: (foo: error xyz.)."},
{"t2.8", m1, []*KeyRules{Key("I", Map(Key("foo", &validateAbc{})))}, ""},
{"t2.9", m1, []*KeyRules{Key("I", Map(Key("foo", &validateXyz{})))}, "I: (foo: error xyz.)."},
// non-map value
{"t3.1", &m1, []*KeyRules{}, ""},
{"t3.2", nil, []*KeyRules{}, ErrNotMap.Error()},
{"t3.3", m0, []*KeyRules{}, ""},
{"t3.4", &m0, []*KeyRules{}, ""},
{"t3.5", 123, []*KeyRules{}, ErrNotMap.Error()},
// invalid key spec
{"t4.1", m1, []*KeyRules{Key(123)}, "123: key not the correct type."},
{"t4.2", m1, []*KeyRules{Key("X")}, "X: required key is missing."},
{"t4.3", m1, []*KeyRules{Key("X").Optional()}, ""},
// missing key: custom error message on a rule should be honored instead of the generic ErrKeyMissing
{"t4.4", m1, []*KeyRules{Key("X", Required.Error("is required"))}, "X: is required."},
// missing key: a rule that doesn't flag nil as invalid (no Required) still falls back to ErrKeyMissing
{"t4.5", m1, []*KeyRules{Key("X", Length(3, 5))}, "X: required key is missing."},
// missing key: rules are never called (#222 narrowed); only a customized Required/NotNil error replaces ErrKeyMissing
{"t4.6", m1, []*KeyRules{Key("X", Required)}, "X: required key is missing."},
{"t4.7", m1, []*KeyRules{Key("X", By(func(v interface{}) error { _ = v.(string); return nil }))}, "X: required key is missing."},
{"t4.8", m1, []*KeyRules{Key("X", NotNil.Error("must be set"))}, "X: must be set."},
{"t4.9", m1, []*KeyRules{Key("X", Required.ErrorObject(NewError("code", "is mandatory")))}, "X: is mandatory."},
{"t4.10", m1, []*KeyRules{Key("X", Length(3, 5), Required.Error("is required"))}, "X: is required."},
{"t4.11", m1, []*KeyRules{Key("X", NilOrNotEmpty.Error("is required"))}, "X: required key is missing."},
{"t4.12", m1, []*KeyRules{Key("X", Required.When(false).Error("is required"))}, "X: required key is missing."},
{"t4.13", m1, []*KeyRules{Key("X", When(true, Required.Error("is required")))}, "X: required key is missing."},
// non-string keys
{"t5.1", m6, []*KeyRules{Key(11, &validateAbc{}), Key(22, &validateXyz{})}, ""},
{"t5.2", m6, []*KeyRules{Key(11, &validateXyz{}), Key(22, &validateAbc{})}, "11: error xyz; 22: error abc."},
// validatable value
{"t6.1", m2, []*KeyRules{Key("E")}, "E: error 123."},
{"t6.2", m2, []*KeyRules{Key("E", Skip)}, ""},
{"t6.3", m2, []*KeyRules{Key("E", Skip.When(true))}, ""},
{"t6.4", m2, []*KeyRules{Key("E", Skip.When(false))}, "E: error 123."},
// Required, NotNil
{"t7.1", m2, []*KeyRules{Key("F", Required)}, "F: cannot be blank."},
{"t7.2", m2, []*KeyRules{Key("F", NotNil)}, "F: is required."},
{"t7.3", m2, []*KeyRules{Key("F", Skip, Required)}, ""},
{"t7.4", m2, []*KeyRules{Key("F", Skip, NotNil)}, ""},
{"t7.5", m2, []*KeyRules{Key("F", Skip.When(true), Required)}, ""},
{"t7.6", m2, []*KeyRules{Key("F", Skip.When(true), NotNil)}, ""},
{"t7.7", m2, []*KeyRules{Key("F", Skip.When(false), Required)}, "F: cannot be blank."},
{"t7.8", m2, []*KeyRules{Key("F", Skip.When(false), NotNil)}, "F: is required."},
// validatable structs
{"t8.1", m3, []*KeyRules{Key("M3", Skip)}, ""},
{"t8.2", m3, []*KeyRules{Key("M3")}, "M3: (A: error abc.)."},
{"t8.3", m4, []*KeyRules{Key("M3")}, ""},
// internal error
{"t9.1", m5, []*KeyRules{Key("A", &validateAbc{}), Key("B", Required), Key("A", &validateInternalError{})}, "error internal"},
// optional keys
{"t10.1", m2, []*KeyRules{OptionalKey("G"), OptionalKey("H")}, ""},
{"t10.2", m2, []*KeyRules{OptionalKey("G"), Key("H")}, "H: required key is missing."},
}
for _, test := range tests {
err1 := Validate(test.model, Map(test.rules...).AllowExtraKeys())
err2 := ValidateWithContext(context.Background(), test.model, Map(test.rules...).AllowExtraKeys())
assertError(t, test.err, err1, test.tag)
assertError(t, test.err, err2, test.tag)
}
for _, test := range tests {
err1 := Validate(test.model, DynamicMap(test.rules...))
err2 := ValidateWithContext(context.Background(), test.model, DynamicMap(test.rules...))
assertError(t, test.err, err1, test.tag)
assertError(t, test.err, err2, test.tag)
}
a := map[string]interface{}{"Name": "name", "Value": "demo", "Extra": true}
err := Validate(a, Map(
Key("Name", Required),
Key("Value", Required, Length(5, 10)),
))
assert.EqualError(t, err, "Extra: key not expected; Value: the length must be between 5 and 10.")
err = Validate(a, DynamicMap(
Key("Name", Required),
Key("Value", Required, Length(5, 10)),
))
assert.EqualError(t, err, "Value: the length must be between 5 and 10.")
}
func TestMapWithContext(t *testing.T) {
m1 := map[string]interface{}{"A": "abc", "B": "xyz", "c": "abc", "g": "xyz"}
m2 := map[string]interface{}{"A": "internal", "B": ""}
tests := []struct {
tag string
model interface{}
rules []*KeyRules
err string
}{
// normal rules
{"t1.1", m1, []*KeyRules{Key("A", &validateContextAbc{}), Key("B", &validateContextXyz{})}, ""},
{"t1.2", m1, []*KeyRules{Key("A", &validateContextXyz{}), Key("B", &validateContextAbc{})}, "A: error xyz; B: error abc."},
{"t1.3", m1, []*KeyRules{Key("A", &validateContextXyz{}), Key("c", &validateContextXyz{})}, "A: error xyz; c: error xyz."},
{"t1.4", m1, []*KeyRules{Key("g", &validateContextAbc{})}, "g: error abc."},
// skip rule
{"t2.1", m1, []*KeyRules{Key("g", Skip, &validateContextAbc{})}, ""},
{"t2.2", m1, []*KeyRules{Key("g", &validateContextAbc{}, Skip)}, "g: error abc."},
// internal error
{"t3.1", m2, []*KeyRules{Key("A", &validateContextAbc{}), Key("B", Required), Key("A", &validateInternalError{})}, "error internal"},
}
for _, test := range tests {
err := ValidateWithContext(context.Background(), test.model, Map(test.rules...).AllowExtraKeys())
assertError(t, test.err, err, test.tag)
}
a := map[string]interface{}{"Name": "name", "Value": "demo", "Extra": true}
err := ValidateWithContext(context.Background(), a, Map(
Key("Name", Required),
Key("Value", Required, Length(5, 10)),
))
if assert.NotNil(t, err) {
assert.Equal(t, "Extra: key not expected; Value: the length must be between 5 and 10.", err.Error())
}
}
func TestMapMissingKeyErrorCode(t *testing.T) {
m := map[string]interface{}{}
for _, rules := range [][]Rule{nil, {Required}, {NotNil}, {Length(3, 5)}} {
err := Validate(m, Map(Key("X", rules...)))
es, ok := err.(Errors)
if assert.True(t, ok, "%v", rules) {
e, ok := es["X"].(Error)
if assert.True(t, ok, "%v", rules) {
assert.Equal(t, "validation_key_missing", e.Code(), "%v", rules)
}
}
}
}
func TestMapMissingKeySkip(t *testing.T) {
m := map[string]interface{}{}
// a Skip before the custom Required stops the scan: the error stays ErrKeyMissing
err := Validate(m, Map(Key("X", Skip, Required.Error("is required"))))
assert.EqualError(t, err, "X: required key is missing.")
// a Skip that does not apply does not stop it
err = Validate(m, Map(Key("X", Skip.When(false), Required.Error("is required"))))
assert.EqualError(t, err, "X: is required.")
}