validators_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. // Copyright 2019 Yunion
  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. package validators
  15. // TODO
  16. //
  17. // - strict type, no implicit conversion
  18. // - test model validator
  19. // - invalid default for string choice, range
  20. import (
  21. "context"
  22. "net"
  23. "reflect"
  24. "testing"
  25. "yunion.io/x/jsonutils"
  26. "yunion.io/x/onecloud/pkg/util/choices"
  27. )
  28. func TestURLPathRegexp(t *testing.T) {
  29. cases := []struct {
  30. in string
  31. match bool
  32. }{
  33. {in: "", match: true},
  34. {in: "/", match: true},
  35. {in: "/p", match: true},
  36. {in: "/p/", match: true},
  37. {in: "p", match: false},
  38. {in: "p/", match: false},
  39. {in: "/p?", match: false},
  40. {in: "/p#", match: false},
  41. }
  42. for _, c := range cases {
  43. got := regexpURLPath.MatchString(c.in)
  44. if got != c.match {
  45. t.Errorf("%q match, want %v, got %v", c.in, c.match, got)
  46. }
  47. }
  48. }
  49. func TestRegHostPort(t *testing.T) {
  50. inputs := []string{
  51. "www.yunion.cn",
  52. "www.yunion.cn:9000",
  53. }
  54. for _, in := range inputs {
  55. if !regHostPort.Match([]byte(in)) {
  56. t.Errorf("should match: %q", in)
  57. }
  58. }
  59. }
  60. type C struct {
  61. Name string
  62. In string
  63. Out string
  64. Optional bool
  65. Default interface{}
  66. ValueWant interface{}
  67. Err ErrType
  68. }
  69. func testS(t *testing.T, v IValidator, c *C) {
  70. returnHttpError = false
  71. j, _ := jsonutils.ParseString(c.In)
  72. jd := j.(*jsonutils.JSONDict)
  73. err := v.Validate(context.Background(), jd)
  74. if err != nil {
  75. verr, ok := err.(*ValidateError)
  76. if ok {
  77. if verr.ErrType != c.Err {
  78. t.Errorf("error want %q, got %q",
  79. c.Err, verr.ErrType)
  80. }
  81. } else {
  82. t.Errorf("want error type ValidateError")
  83. }
  84. } else {
  85. if c.Err != ERR_SUCCESS {
  86. t.Errorf("expect error: %s", c.Err)
  87. }
  88. }
  89. jWant, _ := jsonutils.ParseString(c.Out)
  90. if !reflect.DeepEqual(j, jWant) {
  91. t.Errorf("json out want %s, got %s",
  92. jWant.String(), j.String())
  93. }
  94. value := v.getValue()
  95. if !reflect.DeepEqual(value, c.ValueWant) {
  96. t.Errorf("value want %#v, got %#v", c.ValueWant, value)
  97. }
  98. }
  99. func TestStringChoicesValidator(t *testing.T) {
  100. choices := choices.NewChoices("choice0", "choice1", "100")
  101. cases := []*C{
  102. {
  103. Name: "missing non-optional",
  104. In: `{}`,
  105. Out: `{}`,
  106. Optional: false,
  107. Err: ERR_MISSING_KEY,
  108. ValueWant: "",
  109. },
  110. {
  111. Name: "missing optional",
  112. In: `{}`,
  113. Out: `{}`,
  114. Optional: true,
  115. ValueWant: "",
  116. },
  117. {
  118. Name: "missing with default",
  119. In: `{}`,
  120. Out: `{s: "choice0"}`,
  121. Default: "choice0",
  122. ValueWant: "choice0",
  123. },
  124. {
  125. Name: "stringified",
  126. In: `{"s": 100}`,
  127. Out: `{s: "100"}`,
  128. ValueWant: "100",
  129. },
  130. {
  131. Name: "stringified invalid choice",
  132. In: `{"s": 101}`,
  133. Out: `{"s": 101}`,
  134. Err: ERR_INVALID_CHOICE,
  135. ValueWant: "",
  136. },
  137. {
  138. Name: "good choice",
  139. In: `{"s": "choice1"}`,
  140. Out: `{"s": "choice1"}`,
  141. ValueWant: "choice1",
  142. },
  143. {
  144. Name: "bad choice",
  145. In: `{"s": "badchoice"}`,
  146. Out: `{"s": "badchoice"}`,
  147. Err: ERR_INVALID_CHOICE,
  148. ValueWant: "",
  149. },
  150. }
  151. for _, c := range cases {
  152. t.Run(c.Name, func(t *testing.T) {
  153. v := NewStringChoicesValidator("s", choices)
  154. if c.Default != nil {
  155. s := c.Default.(string)
  156. v.Default(s)
  157. }
  158. if c.Optional {
  159. v.Optional(true)
  160. }
  161. testS(t, v, c)
  162. })
  163. }
  164. }
  165. func TestIntChoicesValidator(t *testing.T) {
  166. choices := []int64{-1, 0, 100}
  167. cases := []*C{
  168. {
  169. Name: "missing non-optional",
  170. In: `{}`,
  171. Out: `{}`,
  172. Optional: false,
  173. Err: ERR_MISSING_KEY,
  174. ValueWant: int64(0),
  175. },
  176. {
  177. Name: "missing optional",
  178. In: `{}`,
  179. Out: `{}`,
  180. Optional: true,
  181. ValueWant: int64(0),
  182. },
  183. {
  184. Name: "missing with default",
  185. In: `{}`,
  186. Out: `{s: -1}`,
  187. Default: int64(-1),
  188. ValueWant: int64(-1),
  189. },
  190. {
  191. Name: "stringified",
  192. In: `{"s": "100"}`,
  193. Out: `{s: 100}`,
  194. ValueWant: int64(100),
  195. },
  196. {
  197. Name: "stringified invalid choice",
  198. In: `{"s": "101"}`,
  199. Out: `{"s": "101"}`,
  200. Err: ERR_INVALID_CHOICE,
  201. ValueWant: int64(0),
  202. },
  203. {
  204. Name: "good choice",
  205. In: `{"s": 0}`,
  206. Out: `{"s": 0}`,
  207. ValueWant: int64(0),
  208. },
  209. {
  210. Name: "bad choice",
  211. In: `{"s": 101}`,
  212. Out: `{"s": 101}`,
  213. Err: ERR_INVALID_CHOICE,
  214. ValueWant: int64(0),
  215. },
  216. }
  217. for _, c := range cases {
  218. t.Run(c.Name, func(t *testing.T) {
  219. v := NewIntChoicesValidator("s", choices)
  220. if c.Default != nil {
  221. s := c.Default.(int64)
  222. v.Default(s)
  223. }
  224. if c.Optional {
  225. v.Optional(true)
  226. }
  227. testS(t, v, c)
  228. })
  229. }
  230. }
  231. func TestStringMultiChoicesValidator(t *testing.T) {
  232. type MultiChoicesC struct {
  233. *C
  234. KeepDup bool
  235. }
  236. choices := choices.NewChoices("choice0", "choice1")
  237. cases := []*MultiChoicesC{
  238. {
  239. C: &C{
  240. Name: "missing non-optional",
  241. In: `{}`,
  242. Out: `{}`,
  243. Optional: false,
  244. Err: ERR_MISSING_KEY,
  245. ValueWant: "",
  246. },
  247. },
  248. {
  249. C: &C{
  250. Name: "missing optional",
  251. In: `{}`,
  252. Out: `{}`,
  253. Optional: true,
  254. ValueWant: "",
  255. },
  256. },
  257. {
  258. C: &C{
  259. Name: "missing with default",
  260. In: `{}`,
  261. Out: `{s: "choice0,choice1"}`,
  262. Default: "choice0,choice1",
  263. ValueWant: "choice0,choice1",
  264. },
  265. },
  266. {
  267. C: &C{
  268. Name: "good choices",
  269. In: `{"s": "choice0,choice1"}`,
  270. Out: `{"s": "choice0,choice1"}`,
  271. ValueWant: "choice0,choice1",
  272. },
  273. },
  274. {
  275. C: &C{
  276. Name: "keep dup",
  277. In: `{"s": "choice0,choice0,choice1,choice0"}`,
  278. Out: `{"s": "choice0,choice0,choice1,choice0"}`,
  279. ValueWant: "choice0,choice0,choice1,choice0",
  280. },
  281. KeepDup: true,
  282. },
  283. {
  284. C: &C{
  285. Name: "strip dup",
  286. In: `{"s": "choice0,choice0,choice1,choice0"}`,
  287. Out: `{"s": "choice0,choice1"}`,
  288. ValueWant: "choice0,choice1",
  289. },
  290. },
  291. {
  292. C: &C{
  293. Name: "invalid choice",
  294. In: `{"s": "choice0,choicex"}`,
  295. Out: `{"s": "choice0,choicex"}`,
  296. Err: ERR_INVALID_CHOICE,
  297. ValueWant: "",
  298. },
  299. },
  300. }
  301. for _, c := range cases {
  302. t.Run(c.Name, func(t *testing.T) {
  303. v := NewStringMultiChoicesValidator("s", choices).Sep(",").KeepDup(c.KeepDup)
  304. if c.Default != nil {
  305. s := c.Default.(string)
  306. v.Default(s)
  307. }
  308. if c.Optional {
  309. v.Optional(true)
  310. }
  311. testS(t, v, c.C)
  312. })
  313. }
  314. }
  315. func TestBoolValidator(t *testing.T) {
  316. cases := []*C{
  317. {
  318. Name: "missing non-optional",
  319. In: `{}`,
  320. Out: `{}`,
  321. Err: ERR_MISSING_KEY,
  322. ValueWant: false,
  323. },
  324. {
  325. Name: "missing optional",
  326. In: `{}`,
  327. Out: `{}`,
  328. Optional: true,
  329. ValueWant: false,
  330. },
  331. {
  332. Name: "missing with default",
  333. In: `{}`,
  334. Out: `{s: true}`,
  335. Default: true,
  336. ValueWant: true,
  337. },
  338. {
  339. Name: "true",
  340. In: `{s: true}`,
  341. Out: `{s: true}`,
  342. ValueWant: true,
  343. },
  344. {
  345. Name: "false",
  346. In: `{s: false}`,
  347. Out: `{s: false}`,
  348. ValueWant: false,
  349. },
  350. {
  351. Name: `parsed "true"`,
  352. In: `{s: "true"}`,
  353. Out: `{s: true}`,
  354. ValueWant: true,
  355. },
  356. {
  357. Name: `parsed "on"`,
  358. In: `{s: "on"}`,
  359. Out: `{s: true}`,
  360. ValueWant: true,
  361. },
  362. {
  363. Name: `parsed "yes"`,
  364. In: `{s: "yes"}`,
  365. Out: `{s: true}`,
  366. ValueWant: true,
  367. },
  368. {
  369. Name: `parsed "1"`,
  370. In: `{s: "1"}`,
  371. Out: `{s: true}`,
  372. ValueWant: true,
  373. },
  374. {
  375. Name: "parsed invalid",
  376. In: `{s: "abc"}`,
  377. Out: `{s: "abc"}`,
  378. Err: ERR_INVALID_TYPE,
  379. ValueWant: false,
  380. },
  381. }
  382. for _, c := range cases {
  383. t.Run(c.Name, func(t *testing.T) {
  384. v := NewBoolValidator("s")
  385. if c.Default != nil {
  386. i := c.Default.(bool)
  387. v.Default(i)
  388. }
  389. if c.Optional {
  390. v.Optional(true)
  391. }
  392. testS(t, v, c)
  393. })
  394. }
  395. }
  396. func TestRangeValidator(t *testing.T) {
  397. cases := []*C{
  398. {
  399. Name: "missing non-optional",
  400. In: `{}`,
  401. Out: `{}`,
  402. Err: ERR_MISSING_KEY,
  403. ValueWant: int64(0),
  404. },
  405. {
  406. Name: "missing optional",
  407. In: `{}`,
  408. Out: `{}`,
  409. Optional: true,
  410. ValueWant: int64(0),
  411. },
  412. {
  413. Name: "missing with default",
  414. In: `{}`,
  415. Out: `{s: 1}`,
  416. Default: int64(1),
  417. ValueWant: int64(1),
  418. },
  419. {
  420. Name: "parsed",
  421. In: `{s: "100"}`,
  422. Out: `{s: 100}`,
  423. ValueWant: int64(100),
  424. },
  425. {
  426. Name: "parsed invalid int",
  427. In: `{s: "abc"}`,
  428. Out: `{s: "abc"}`,
  429. Err: ERR_INVALID_TYPE,
  430. ValueWant: int64(0),
  431. },
  432. {
  433. Name: "parsed not in range",
  434. In: `{s: "65536"}`,
  435. Out: `{s: "65536"}`,
  436. Err: ERR_NOT_IN_RANGE,
  437. ValueWant: int64(0),
  438. },
  439. {
  440. Name: "in range",
  441. In: `{s: 100}`,
  442. Out: `{s: 100}`,
  443. ValueWant: int64(100),
  444. },
  445. {
  446. Name: "not in range",
  447. In: `{s: 0}`,
  448. Out: `{s: 0}`,
  449. Err: ERR_NOT_IN_RANGE,
  450. ValueWant: int64(0),
  451. },
  452. }
  453. for _, c := range cases {
  454. t.Run(c.Name, func(t *testing.T) {
  455. v := NewPortValidator("s")
  456. if c.Default != nil {
  457. i := c.Default.(int64)
  458. v.Default(i)
  459. }
  460. if c.Optional {
  461. v.Optional(true)
  462. }
  463. testS(t, v, c)
  464. })
  465. }
  466. }
  467. func TestRegexValidator(t *testing.T) {
  468. type RegexC struct {
  469. *C
  470. AllowEmpty bool
  471. }
  472. cases := []*RegexC{
  473. {
  474. C: &C{
  475. Name: "missing non-optional",
  476. In: `{}`,
  477. Out: `{}`,
  478. Err: ERR_MISSING_KEY,
  479. ValueWant: "",
  480. },
  481. },
  482. {
  483. C: &C{
  484. Name: "missing optional",
  485. In: `{}`,
  486. Out: `{}`,
  487. Optional: true,
  488. ValueWant: "",
  489. },
  490. },
  491. {
  492. C: &C{
  493. Name: "missing with default",
  494. In: `{}`,
  495. Out: `{s: "example.com"}`,
  496. Default: "example.com",
  497. ValueWant: "example.com",
  498. },
  499. },
  500. {
  501. C: &C{
  502. Name: "valid",
  503. In: `{s: "a.example.com"}`,
  504. Out: `{s: "a.example.com"}`,
  505. ValueWant: "a.example.com",
  506. },
  507. },
  508. {
  509. C: &C{
  510. Name: "valid (allow empty)",
  511. In: `{s: ""}`,
  512. Out: `{s: ""}`,
  513. ValueWant: "",
  514. },
  515. AllowEmpty: true,
  516. },
  517. {
  518. C: &C{
  519. Name: "invalid",
  520. In: `{s: "/.example.com"}`,
  521. Out: `{s: "/.example.com"}`,
  522. ValueWant: "",
  523. Err: ERR_INVALID_VALUE,
  524. },
  525. },
  526. {
  527. C: &C{
  528. Name: "invalid (disallow empty)",
  529. In: `{s: ""}`,
  530. Out: `{s: ""}`,
  531. ValueWant: "",
  532. Err: ERR_INVALID_VALUE,
  533. },
  534. },
  535. }
  536. for _, c := range cases {
  537. t.Run(c.Name, func(t *testing.T) {
  538. v := NewDomainNameValidator("s")
  539. if c.Default != nil {
  540. i := c.Default.(string)
  541. v.Default(i)
  542. }
  543. if c.Optional {
  544. v.Optional(true)
  545. }
  546. if c.AllowEmpty {
  547. v.AllowEmpty(true)
  548. }
  549. testS(t, v, c.C)
  550. })
  551. }
  552. }
  553. func TestHostPortValidator(t *testing.T) {
  554. type HostPortC struct {
  555. *C
  556. AllowEmpty bool
  557. OptionalPort bool
  558. }
  559. cases := []*HostPortC{
  560. {
  561. C: &C{
  562. Name: "missing non-optional",
  563. In: `{}`,
  564. Out: `{}`,
  565. Err: ERR_MISSING_KEY,
  566. ValueWant: "",
  567. },
  568. },
  569. {
  570. C: &C{
  571. Name: "missing optional",
  572. In: `{}`,
  573. Out: `{}`,
  574. Optional: true,
  575. ValueWant: "",
  576. },
  577. },
  578. {
  579. C: &C{
  580. Name: "missing with default",
  581. In: `{}`,
  582. Out: `{s: "example.com"}`,
  583. Default: "example.com",
  584. ValueWant: "example.com",
  585. },
  586. OptionalPort: true,
  587. },
  588. {
  589. C: &C{
  590. Name: "missing with default (has port)",
  591. In: `{}`,
  592. Out: `{s: "example.com:9000"}`,
  593. Default: "example.com:9000",
  594. ValueWant: "example.com:9000",
  595. },
  596. },
  597. {
  598. C: &C{
  599. Name: "valid",
  600. In: `{s: "a.example.com"}`,
  601. Out: `{s: "a.example.com"}`,
  602. ValueWant: "a.example.com",
  603. },
  604. OptionalPort: true,
  605. },
  606. {
  607. C: &C{
  608. Name: "valid (has port)",
  609. In: `{s: "a.example.com:9000"}`,
  610. Out: `{s: "a.example.com:9000"}`,
  611. ValueWant: "a.example.com:9000",
  612. },
  613. },
  614. {
  615. C: &C{
  616. Name: "valid (allow empty)",
  617. In: `{s: ""}`,
  618. Out: `{s: ""}`,
  619. ValueWant: "",
  620. },
  621. AllowEmpty: true,
  622. },
  623. {
  624. C: &C{
  625. Name: "invalid (domain)",
  626. In: `{s: "/.example.com:9000"}`,
  627. Out: `{s: "/.example.com:9000"}`,
  628. ValueWant: "",
  629. Err: ERR_INVALID_VALUE,
  630. },
  631. },
  632. {
  633. C: &C{
  634. Name: "invalid (port)",
  635. In: `{s: "/.example.com:65536"}`,
  636. Out: `{s: "/.example.com:65536"}`,
  637. ValueWant: "",
  638. Err: ERR_INVALID_VALUE,
  639. },
  640. },
  641. {
  642. C: &C{
  643. Name: "invalid (no port)",
  644. In: `{s: "a.example.com"}`,
  645. Out: `{s: "a.example.com"}`,
  646. ValueWant: "",
  647. Err: ERR_INVALID_VALUE,
  648. },
  649. },
  650. {
  651. C: &C{
  652. Name: "invalid (disallow empty)",
  653. In: `{s: ""}`,
  654. Out: `{s: ""}`,
  655. ValueWant: "",
  656. Err: ERR_INVALID_VALUE,
  657. },
  658. },
  659. }
  660. for _, c := range cases {
  661. t.Run(c.Name, func(t *testing.T) {
  662. v := NewHostPortValidator("s")
  663. if c.Default != nil {
  664. i := c.Default.(string)
  665. v.Default(i)
  666. }
  667. if c.Optional {
  668. v.Optional(true)
  669. }
  670. if c.OptionalPort {
  671. v.OptionalPort(true)
  672. }
  673. if c.AllowEmpty {
  674. v.AllowEmpty(true)
  675. }
  676. testS(t, v, c.C)
  677. })
  678. }
  679. }
  680. func TestIPv4Validator(t *testing.T) {
  681. var nilIP net.IP
  682. localIP := net.IPv4(127, 0, 0, 1).To4()
  683. cases := []*C{
  684. {
  685. Name: "missing non-optional",
  686. In: `{}`,
  687. Out: `{}`,
  688. Err: ERR_MISSING_KEY,
  689. ValueWant: nilIP,
  690. },
  691. {
  692. Name: "missing optional",
  693. In: `{}`,
  694. Out: `{}`,
  695. Optional: true,
  696. ValueWant: nilIP,
  697. },
  698. {
  699. Name: "missing with default",
  700. In: `{}`,
  701. Out: `{s: "127.0.0.1"}`,
  702. Default: localIP,
  703. ValueWant: localIP,
  704. },
  705. {
  706. Name: "valid",
  707. In: `{s: "127.0.0.1"}`,
  708. Out: `{s: "127.0.0.1"}`,
  709. ValueWant: localIP,
  710. },
  711. {
  712. Name: "valid (v4 in v6)",
  713. In: `{s: "::ffff:127.0.0.1"}`,
  714. Out: `{s: "::ffff:127.0.0.1"}`,
  715. ValueWant: localIP,
  716. },
  717. {
  718. Name: "invalid",
  719. In: `{s: "127.0.0"}`,
  720. Out: `{s: "127.0.0"}`,
  721. Err: ERR_INVALID_VALUE,
  722. ValueWant: nilIP,
  723. },
  724. {
  725. Name: "invalid (empty string)",
  726. In: `{s: ""}`,
  727. Out: `{s: ""}`,
  728. Err: ERR_INVALID_VALUE,
  729. ValueWant: nilIP,
  730. },
  731. {
  732. Name: "invalid (wrong type)",
  733. In: `{s: 100}`,
  734. Out: `{s: 100}`,
  735. Err: ERR_INVALID_VALUE,
  736. ValueWant: nilIP,
  737. },
  738. }
  739. for _, c := range cases {
  740. t.Run(c.Name, func(t *testing.T) {
  741. v := NewIPv4AddrValidator("s")
  742. if c.Default != nil {
  743. i := c.Default.(net.IP)
  744. v.Default(i)
  745. }
  746. if c.Optional {
  747. v.Optional(true)
  748. }
  749. testS(t, v, c)
  750. })
  751. }
  752. }
  753. type TestStruct struct {
  754. Name string
  755. F0 string
  756. F1 int
  757. F2 bool
  758. }
  759. type TestVStruct TestStruct
  760. func (v *TestVStruct) Validate(ctx context.Context, data *jsonutils.JSONDict) error {
  761. switch v.Name {
  762. case "bad":
  763. return newInvalidValueError("Name", v.Name)
  764. case "setDefault":
  765. v.Name = "defaultVal"
  766. }
  767. return nil
  768. }
  769. func TestStructValidator(t *testing.T) {
  770. type StructC struct {
  771. *C
  772. Value interface{}
  773. }
  774. cases := []*StructC{
  775. {
  776. C: &C{
  777. Name: "missing non-optional",
  778. In: `{}`,
  779. Out: `{}`,
  780. Err: ERR_MISSING_KEY,
  781. ValueWant: &TestStruct{},
  782. },
  783. Value: &TestStruct{},
  784. },
  785. {
  786. C: &C{
  787. Name: "missing optional",
  788. In: `{}`,
  789. Out: `{}`,
  790. Optional: true,
  791. ValueWant: &TestStruct{},
  792. },
  793. Value: &TestStruct{},
  794. },
  795. {
  796. C: &C{
  797. Name: "valid",
  798. In: `{s: {"F0": "holy", "F1": 100, "F2": true}}`,
  799. Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`,
  800. ValueWant: &TestStruct{
  801. F0: "holy",
  802. F1: 100,
  803. F2: true,
  804. },
  805. },
  806. Value: &TestStruct{},
  807. },
  808. {
  809. C: &C{
  810. Name: "valid (missing fields)",
  811. In: `{s: {"F0": "holy", "F1": 100}}`,
  812. Out: `{s: {"f0": "holy", "f1": 100, "f2": false}}`,
  813. ValueWant: &TestStruct{
  814. F0: "holy",
  815. F1: 100,
  816. F2: false,
  817. },
  818. },
  819. Value: &TestStruct{},
  820. },
  821. {
  822. C: &C{
  823. Name: "valid (more fields)",
  824. In: `{s: {"F0": "holy", "F1": 100, "F2": true, "Foo": "bar"}}`,
  825. Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`,
  826. ValueWant: &TestStruct{
  827. F0: "holy",
  828. F1: 100,
  829. F2: true,
  830. },
  831. },
  832. Value: &TestStruct{},
  833. },
  834. {
  835. C: &C{
  836. Name: "valid (struct says valid)",
  837. In: `{s: {"F0": "holy", "F1": 100, "F2": true}}`,
  838. Out: `{s: {"f0": "holy", "f1": 100, "f2": true}}`,
  839. ValueWant: &TestVStruct{
  840. F0: "holy",
  841. F1: 100,
  842. F2: true,
  843. },
  844. },
  845. Value: &TestVStruct{},
  846. },
  847. {
  848. C: &C{
  849. Name: "valid (with default initialized)",
  850. In: `{s: {Name: "setDefault", "F0": "holy", "F1": 100, "F2": false}}`,
  851. Out: `{s: {name: "defaultVal", "f0": "holy", "f1": 100, "f2": false}}`,
  852. ValueWant: &TestVStruct{
  853. Name: "defaultVal",
  854. F0: "holy",
  855. F1: 100,
  856. F2: false,
  857. },
  858. },
  859. Value: &TestVStruct{},
  860. },
  861. {
  862. C: &C{
  863. Name: "invalid (struct says invalid)",
  864. In: `{s: {Name: "bad", "F0": "holy", "F1": 100, "F2": false}}`,
  865. Out: `{s: {Name: "bad", "F0": "holy", "F1": 100, "F2": false}}`,
  866. ValueWant: &TestVStruct{
  867. Name: "bad",
  868. F0: "holy",
  869. F1: 100,
  870. F2: false,
  871. },
  872. Err: ERR_INVALID_VALUE,
  873. },
  874. Value: &TestVStruct{},
  875. },
  876. }
  877. for _, c := range cases {
  878. t.Run(c.Name, func(t *testing.T) {
  879. v := NewStructValidator("s", c.Value)
  880. if c.Optional {
  881. v.Optional(true)
  882. }
  883. testS(t, v, c.C)
  884. })
  885. }
  886. }