| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909 |
- // Copyright 2019 Yunion
- //
- // 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 validators
- // TODO
- //
- // - strict type, no implicit conversion
- // - test model validator
- // - invalid default for string choice, range
- import (
- "context"
- "net"
- "reflect"
- "testing"
- "yunion.io/x/jsonutils"
- "yunion.io/x/onecloud/pkg/util/choices"
- )
- func TestURLPathRegexp(t *testing.T) {
- cases := []struct {
- in string
- match bool
- }{
- {in: "", match: true},
- {in: "/", match: true},
- {in: "/p", match: true},
- {in: "/p/", match: true},
- {in: "p", match: false},
- {in: "p/", match: false},
- {in: "/p?", match: false},
- {in: "/p#", match: false},
- }
- for _, c := range cases {
- got := regexpURLPath.MatchString(c.in)
- if got != c.match {
- t.Errorf("%q match, want %v, got %v", c.in, c.match, got)
- }
- }
- }
- func TestRegHostPort(t *testing.T) {
- inputs := []string{
- "www.yunion.cn",
- "www.yunion.cn:9000",
- }
- for _, in := range inputs {
- if !regHostPort.Match([]byte(in)) {
- t.Errorf("should match: %q", in)
- }
- }
- }
- type C struct {
- Name string
- In string
- Out string
- Optional bool
- Default interface{}
- ValueWant interface{}
- Err ErrType
- }
- func testS(t *testing.T, v IValidator, c *C) {
- returnHttpError = false
- j, _ := jsonutils.ParseString(c.In)
- jd := j.(*jsonutils.JSONDict)
- err := v.Validate(context.Background(), jd)
- if err != nil {
- verr, ok := err.(*ValidateError)
- if ok {
- if verr.ErrType != c.Err {
- t.Errorf("error want %q, got %q",
- c.Err, verr.ErrType)
- }
- } else {
- t.Errorf("want error type ValidateError")
- }
- } else {
- if c.Err != ERR_SUCCESS {
- t.Errorf("expect error: %s", c.Err)
- }
- }
- jWant, _ := jsonutils.ParseString(c.Out)
- if !reflect.DeepEqual(j, jWant) {
- t.Errorf("json out want %s, got %s",
- jWant.String(), j.String())
- }
- value := v.getValue()
- if !reflect.DeepEqual(value, c.ValueWant) {
- t.Errorf("value want %#v, got %#v", c.ValueWant, value)
- }
- }
- func TestStringChoicesValidator(t *testing.T) {
- choices := choices.NewChoices("choice0", "choice1", "100")
- cases := []*C{
- {
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Optional: false,
- Err: ERR_MISSING_KEY,
- ValueWant: "",
- },
- {
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: "",
- },
- {
- Name: "missing with default",
- In: `{}`,
- Out: `{s: "choice0"}`,
- Default: "choice0",
- ValueWant: "choice0",
- },
- {
- Name: "stringified",
- In: `{"s": 100}`,
- Out: `{s: "100"}`,
- ValueWant: "100",
- },
- {
- Name: "stringified invalid choice",
- In: `{"s": 101}`,
- Out: `{"s": 101}`,
- Err: ERR_INVALID_CHOICE,
- ValueWant: "",
- },
- {
- Name: "good choice",
- In: `{"s": "choice1"}`,
- Out: `{"s": "choice1"}`,
- ValueWant: "choice1",
- },
- {
- Name: "bad choice",
- In: `{"s": "badchoice"}`,
- Out: `{"s": "badchoice"}`,
- Err: ERR_INVALID_CHOICE,
- ValueWant: "",
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewStringChoicesValidator("s", choices)
- if c.Default != nil {
- s := c.Default.(string)
- v.Default(s)
- }
- if c.Optional {
- v.Optional(true)
- }
- testS(t, v, c)
- })
- }
- }
- func TestIntChoicesValidator(t *testing.T) {
- choices := []int64{-1, 0, 100}
- cases := []*C{
- {
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Optional: false,
- Err: ERR_MISSING_KEY,
- ValueWant: int64(0),
- },
- {
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: int64(0),
- },
- {
- Name: "missing with default",
- In: `{}`,
- Out: `{s: -1}`,
- Default: int64(-1),
- ValueWant: int64(-1),
- },
- {
- Name: "stringified",
- In: `{"s": "100"}`,
- Out: `{s: 100}`,
- ValueWant: int64(100),
- },
- {
- Name: "stringified invalid choice",
- In: `{"s": "101"}`,
- Out: `{"s": "101"}`,
- Err: ERR_INVALID_CHOICE,
- ValueWant: int64(0),
- },
- {
- Name: "good choice",
- In: `{"s": 0}`,
- Out: `{"s": 0}`,
- ValueWant: int64(0),
- },
- {
- Name: "bad choice",
- In: `{"s": 101}`,
- Out: `{"s": 101}`,
- Err: ERR_INVALID_CHOICE,
- ValueWant: int64(0),
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewIntChoicesValidator("s", choices)
- if c.Default != nil {
- s := c.Default.(int64)
- v.Default(s)
- }
- if c.Optional {
- v.Optional(true)
- }
- testS(t, v, c)
- })
- }
- }
- func TestStringMultiChoicesValidator(t *testing.T) {
- type MultiChoicesC struct {
- *C
- KeepDup bool
- }
- choices := choices.NewChoices("choice0", "choice1")
- cases := []*MultiChoicesC{
- {
- C: &C{
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Optional: false,
- Err: ERR_MISSING_KEY,
- ValueWant: "",
- },
- },
- {
- C: &C{
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: "",
- },
- },
- {
- C: &C{
- Name: "missing with default",
- In: `{}`,
- Out: `{s: "choice0,choice1"}`,
- Default: "choice0,choice1",
- ValueWant: "choice0,choice1",
- },
- },
- {
- C: &C{
- Name: "good choices",
- In: `{"s": "choice0,choice1"}`,
- Out: `{"s": "choice0,choice1"}`,
- ValueWant: "choice0,choice1",
- },
- },
- {
- C: &C{
- Name: "keep dup",
- In: `{"s": "choice0,choice0,choice1,choice0"}`,
- Out: `{"s": "choice0,choice0,choice1,choice0"}`,
- ValueWant: "choice0,choice0,choice1,choice0",
- },
- KeepDup: true,
- },
- {
- C: &C{
- Name: "strip dup",
- In: `{"s": "choice0,choice0,choice1,choice0"}`,
- Out: `{"s": "choice0,choice1"}`,
- ValueWant: "choice0,choice1",
- },
- },
- {
- C: &C{
- Name: "invalid choice",
- In: `{"s": "choice0,choicex"}`,
- Out: `{"s": "choice0,choicex"}`,
- Err: ERR_INVALID_CHOICE,
- ValueWant: "",
- },
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewStringMultiChoicesValidator("s", choices).Sep(",").KeepDup(c.KeepDup)
- if c.Default != nil {
- s := c.Default.(string)
- v.Default(s)
- }
- if c.Optional {
- v.Optional(true)
- }
- testS(t, v, c.C)
- })
- }
- }
- func TestBoolValidator(t *testing.T) {
- cases := []*C{
- {
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Err: ERR_MISSING_KEY,
- ValueWant: false,
- },
- {
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: false,
- },
- {
- Name: "missing with default",
- In: `{}`,
- Out: `{s: true}`,
- Default: true,
- ValueWant: true,
- },
- {
- Name: "true",
- In: `{s: true}`,
- Out: `{s: true}`,
- ValueWant: true,
- },
- {
- Name: "false",
- In: `{s: false}`,
- Out: `{s: false}`,
- ValueWant: false,
- },
- {
- Name: `parsed "true"`,
- In: `{s: "true"}`,
- Out: `{s: true}`,
- ValueWant: true,
- },
- {
- Name: `parsed "on"`,
- In: `{s: "on"}`,
- Out: `{s: true}`,
- ValueWant: true,
- },
- {
- Name: `parsed "yes"`,
- In: `{s: "yes"}`,
- Out: `{s: true}`,
- ValueWant: true,
- },
- {
- Name: `parsed "1"`,
- In: `{s: "1"}`,
- Out: `{s: true}`,
- ValueWant: true,
- },
- {
- Name: "parsed invalid",
- In: `{s: "abc"}`,
- Out: `{s: "abc"}`,
- Err: ERR_INVALID_TYPE,
- ValueWant: false,
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewBoolValidator("s")
- if c.Default != nil {
- i := c.Default.(bool)
- v.Default(i)
- }
- if c.Optional {
- v.Optional(true)
- }
- testS(t, v, c)
- })
- }
- }
- func TestRangeValidator(t *testing.T) {
- cases := []*C{
- {
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Err: ERR_MISSING_KEY,
- ValueWant: int64(0),
- },
- {
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: int64(0),
- },
- {
- Name: "missing with default",
- In: `{}`,
- Out: `{s: 1}`,
- Default: int64(1),
- ValueWant: int64(1),
- },
- {
- Name: "parsed",
- In: `{s: "100"}`,
- Out: `{s: 100}`,
- ValueWant: int64(100),
- },
- {
- Name: "parsed invalid int",
- In: `{s: "abc"}`,
- Out: `{s: "abc"}`,
- Err: ERR_INVALID_TYPE,
- ValueWant: int64(0),
- },
- {
- Name: "parsed not in range",
- In: `{s: "65536"}`,
- Out: `{s: "65536"}`,
- Err: ERR_NOT_IN_RANGE,
- ValueWant: int64(0),
- },
- {
- Name: "in range",
- In: `{s: 100}`,
- Out: `{s: 100}`,
- ValueWant: int64(100),
- },
- {
- Name: "not in range",
- In: `{s: 0}`,
- Out: `{s: 0}`,
- Err: ERR_NOT_IN_RANGE,
- ValueWant: int64(0),
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewPortValidator("s")
- if c.Default != nil {
- i := c.Default.(int64)
- v.Default(i)
- }
- if c.Optional {
- v.Optional(true)
- }
- testS(t, v, c)
- })
- }
- }
- func TestRegexValidator(t *testing.T) {
- type RegexC struct {
- *C
- AllowEmpty bool
- }
- cases := []*RegexC{
- {
- C: &C{
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Err: ERR_MISSING_KEY,
- ValueWant: "",
- },
- },
- {
- C: &C{
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: "",
- },
- },
- {
- C: &C{
- Name: "missing with default",
- In: `{}`,
- Out: `{s: "example.com"}`,
- Default: "example.com",
- ValueWant: "example.com",
- },
- },
- {
- C: &C{
- Name: "valid",
- In: `{s: "a.example.com"}`,
- Out: `{s: "a.example.com"}`,
- ValueWant: "a.example.com",
- },
- },
- {
- C: &C{
- Name: "valid (allow empty)",
- In: `{s: ""}`,
- Out: `{s: ""}`,
- ValueWant: "",
- },
- AllowEmpty: true,
- },
- {
- C: &C{
- Name: "invalid",
- In: `{s: "/.example.com"}`,
- Out: `{s: "/.example.com"}`,
- ValueWant: "",
- Err: ERR_INVALID_VALUE,
- },
- },
- {
- C: &C{
- Name: "invalid (disallow empty)",
- In: `{s: ""}`,
- Out: `{s: ""}`,
- ValueWant: "",
- Err: ERR_INVALID_VALUE,
- },
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewDomainNameValidator("s")
- if c.Default != nil {
- i := c.Default.(string)
- v.Default(i)
- }
- if c.Optional {
- v.Optional(true)
- }
- if c.AllowEmpty {
- v.AllowEmpty(true)
- }
- testS(t, v, c.C)
- })
- }
- }
- func TestHostPortValidator(t *testing.T) {
- type HostPortC struct {
- *C
- AllowEmpty bool
- OptionalPort bool
- }
- cases := []*HostPortC{
- {
- C: &C{
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Err: ERR_MISSING_KEY,
- ValueWant: "",
- },
- },
- {
- C: &C{
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: "",
- },
- },
- {
- C: &C{
- Name: "missing with default",
- In: `{}`,
- Out: `{s: "example.com"}`,
- Default: "example.com",
- ValueWant: "example.com",
- },
- OptionalPort: true,
- },
- {
- C: &C{
- Name: "missing with default (has port)",
- In: `{}`,
- Out: `{s: "example.com:9000"}`,
- Default: "example.com:9000",
- ValueWant: "example.com:9000",
- },
- },
- {
- C: &C{
- Name: "valid",
- In: `{s: "a.example.com"}`,
- Out: `{s: "a.example.com"}`,
- ValueWant: "a.example.com",
- },
- OptionalPort: true,
- },
- {
- C: &C{
- Name: "valid (has port)",
- In: `{s: "a.example.com:9000"}`,
- Out: `{s: "a.example.com:9000"}`,
- ValueWant: "a.example.com:9000",
- },
- },
- {
- C: &C{
- Name: "valid (allow empty)",
- In: `{s: ""}`,
- Out: `{s: ""}`,
- ValueWant: "",
- },
- AllowEmpty: true,
- },
- {
- C: &C{
- Name: "invalid (domain)",
- In: `{s: "/.example.com:9000"}`,
- Out: `{s: "/.example.com:9000"}`,
- ValueWant: "",
- Err: ERR_INVALID_VALUE,
- },
- },
- {
- C: &C{
- Name: "invalid (port)",
- In: `{s: "/.example.com:65536"}`,
- Out: `{s: "/.example.com:65536"}`,
- ValueWant: "",
- Err: ERR_INVALID_VALUE,
- },
- },
- {
- C: &C{
- Name: "invalid (no port)",
- In: `{s: "a.example.com"}`,
- Out: `{s: "a.example.com"}`,
- ValueWant: "",
- Err: ERR_INVALID_VALUE,
- },
- },
- {
- C: &C{
- Name: "invalid (disallow empty)",
- In: `{s: ""}`,
- Out: `{s: ""}`,
- ValueWant: "",
- Err: ERR_INVALID_VALUE,
- },
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewHostPortValidator("s")
- if c.Default != nil {
- i := c.Default.(string)
- v.Default(i)
- }
- if c.Optional {
- v.Optional(true)
- }
- if c.OptionalPort {
- v.OptionalPort(true)
- }
- if c.AllowEmpty {
- v.AllowEmpty(true)
- }
- testS(t, v, c.C)
- })
- }
- }
- func TestIPv4Validator(t *testing.T) {
- var nilIP net.IP
- localIP := net.IPv4(127, 0, 0, 1).To4()
- cases := []*C{
- {
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Err: ERR_MISSING_KEY,
- ValueWant: nilIP,
- },
- {
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: nilIP,
- },
- {
- Name: "missing with default",
- In: `{}`,
- Out: `{s: "127.0.0.1"}`,
- Default: localIP,
- ValueWant: localIP,
- },
- {
- Name: "valid",
- In: `{s: "127.0.0.1"}`,
- Out: `{s: "127.0.0.1"}`,
- ValueWant: localIP,
- },
- {
- Name: "valid (v4 in v6)",
- In: `{s: "::ffff:127.0.0.1"}`,
- Out: `{s: "::ffff:127.0.0.1"}`,
- ValueWant: localIP,
- },
- {
- Name: "invalid",
- In: `{s: "127.0.0"}`,
- Out: `{s: "127.0.0"}`,
- Err: ERR_INVALID_VALUE,
- ValueWant: nilIP,
- },
- {
- Name: "invalid (empty string)",
- In: `{s: ""}`,
- Out: `{s: ""}`,
- Err: ERR_INVALID_VALUE,
- ValueWant: nilIP,
- },
- {
- Name: "invalid (wrong type)",
- In: `{s: 100}`,
- Out: `{s: 100}`,
- Err: ERR_INVALID_VALUE,
- ValueWant: nilIP,
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewIPv4AddrValidator("s")
- if c.Default != nil {
- i := c.Default.(net.IP)
- v.Default(i)
- }
- if c.Optional {
- v.Optional(true)
- }
- testS(t, v, c)
- })
- }
- }
- type TestStruct struct {
- Name string
- F0 string
- F1 int
- F2 bool
- }
- type TestVStruct TestStruct
- func (v *TestVStruct) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
- switch v.Name {
- case "bad":
- return newInvalidValueError("Name", v.Name)
- case "setDefault":
- v.Name = "defaultVal"
- }
- return nil
- }
- func TestStructValidator(t *testing.T) {
- type StructC struct {
- *C
- Value interface{}
- }
- cases := []*StructC{
- {
- C: &C{
- Name: "missing non-optional",
- In: `{}`,
- Out: `{}`,
- Err: ERR_MISSING_KEY,
- ValueWant: &TestStruct{},
- },
- Value: &TestStruct{},
- },
- {
- C: &C{
- Name: "missing optional",
- In: `{}`,
- Out: `{}`,
- Optional: true,
- ValueWant: &TestStruct{},
- },
- Value: &TestStruct{},
- },
- {
- C: &C{
- Name: "valid",
- In: `{s: {"F0": "holy", "F1": 100, "F2": true}}`,
- Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`,
- ValueWant: &TestStruct{
- F0: "holy",
- F1: 100,
- F2: true,
- },
- },
- Value: &TestStruct{},
- },
- {
- C: &C{
- Name: "valid (missing fields)",
- In: `{s: {"F0": "holy", "F1": 100}}`,
- Out: `{s: {"f0": "holy", "f1": 100, "f2": false}}`,
- ValueWant: &TestStruct{
- F0: "holy",
- F1: 100,
- F2: false,
- },
- },
- Value: &TestStruct{},
- },
- {
- C: &C{
- Name: "valid (more fields)",
- In: `{s: {"F0": "holy", "F1": 100, "F2": true, "Foo": "bar"}}`,
- Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`,
- ValueWant: &TestStruct{
- F0: "holy",
- F1: 100,
- F2: true,
- },
- },
- Value: &TestStruct{},
- },
- {
- C: &C{
- Name: "valid (struct says valid)",
- In: `{s: {"F0": "holy", "F1": 100, "F2": true}}`,
- Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`,
- ValueWant: &TestVStruct{
- F0: "holy",
- F1: 100,
- F2: true,
- },
- },
- Value: &TestVStruct{},
- },
- {
- C: &C{
- Name: "valid (with default initialized)",
- In: `{s: {Name: "setDefault", "F0": "holy", "F1": 100, "F2": false}}`,
- Out: `{s: {name: "defaultVal", "f0": "holy", "f1": 100, "f2": false}}`,
- ValueWant: &TestVStruct{
- Name: "defaultVal",
- F0: "holy",
- F1: 100,
- F2: false,
- },
- },
- Value: &TestVStruct{},
- },
- {
- C: &C{
- Name: "invalid (struct says invalid)",
- In: `{s: {Name: "bad", "F0": "holy", "F1": 100, "F2": false}}`,
- Out: `{s: {Name: "bad", "F0": "holy", "F1": 100, "F2": false}}`,
- ValueWant: &TestVStruct{
- Name: "bad",
- F0: "holy",
- F1: 100,
- F2: false,
- },
- Err: ERR_INVALID_VALUE,
- },
- Value: &TestVStruct{},
- },
- }
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- v := NewStructValidator("s", c.Value)
- if c.Optional {
- v.Optional(true)
- }
- testS(t, v, c.C)
- })
- }
- }
|