Expect.structure.phpt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <?php
  2. declare(strict_types=1);
  3. use Nette\Schema\Expect;
  4. use Nette\Schema\Processor;
  5. use Tester\Assert;
  6. require __DIR__ . '/../bootstrap.php';
  7. test('without items', function () {
  8. $schema = Expect::structure([]);
  9. Assert::equal((object) [], (new Processor)->process($schema, []));
  10. checkValidationErrors(function () use ($schema) {
  11. (new Processor)->process($schema, [1, 2, 3]);
  12. }, ["Unexpected item '0'.", "Unexpected item '1'.", "Unexpected item '2'."]);
  13. checkValidationErrors(function () use ($schema) {
  14. (new Processor)->process($schema, ['key' => 'val']);
  15. }, ["Unexpected item 'key'."]);
  16. checkValidationErrors(function () use ($schema) {
  17. (new Processor)->process($schema, 'one');
  18. }, ["The item expects to be array, 'one' given."]);
  19. checkValidationErrors(function () use ($schema) {
  20. (new Processor)->process($schema, true);
  21. }, ['The item expects to be array, true given.']);
  22. checkValidationErrors(function () use ($schema) {
  23. (new Processor)->process($schema, 123);
  24. }, ['The item expects to be array, 123 given.']);
  25. Assert::equal((object) [], (new Processor)->process($schema, null));
  26. });
  27. test('accepts object', function () {
  28. $schema = Expect::structure(['a' => Expect::string()]);
  29. Assert::equal((object) ['a' => null], (new Processor)->process($schema, (object) []));
  30. Assert::equal((object) ['a' => 'foo'], (new Processor)->process($schema, (object) ['a' => 'foo']));
  31. checkValidationErrors(function () use ($schema) {
  32. (new Processor)->process($schema, (object) ['a' => 1]);
  33. }, ["The item 'a' expects to be string, 1 given."]);
  34. $schema = Expect::structure(['a' => Expect::string()->before('strrev')]);
  35. Assert::equal((object) ['a' => 'oof'], (new Processor)->process($schema, (object) ['a' => 'foo']));
  36. Assert::equal(
  37. (object) ['a' => 'rab'],
  38. (new Processor)->processMultiple($schema, [(object) ['a' => 'foo'], (object) ['a' => 'bar']])
  39. );
  40. });
  41. test('scalar items', function () {
  42. $schema = Expect::structure([
  43. 'a' => Expect::string(),
  44. 'b' => Expect::int(),
  45. 'c' => Expect::bool(),
  46. 'd' => Expect::scalar(),
  47. 'e' => Expect::type('string'),
  48. 'f' => Expect::type('int'),
  49. 'g' => Expect::string('abc'),
  50. 'h' => Expect::string(123),
  51. 'i' => Expect::type('string')->default(123),
  52. 'j' => Expect::anyOf(1, 2),
  53. ]);
  54. Assert::equal(
  55. (object) ['a' => null, 'b' => null, 'c' => null, 'd' => null, 'e' => null, 'f' => null, 'g' => 'abc', 'h' => 123, 'i' => 123, 'j' => null],
  56. (new Processor)->process($schema, [])
  57. );
  58. });
  59. test('array items', function () {
  60. $schema = Expect::structure([
  61. 'a' => Expect::array(),
  62. 'b' => Expect::array([]),
  63. 'c' => Expect::arrayOf('string'),
  64. 'd' => Expect::list(),
  65. 'e' => Expect::listOf('string'),
  66. 'f' => Expect::type('array'),
  67. 'g' => Expect::type('list'),
  68. 'h' => Expect::structure([]),
  69. ]);
  70. Assert::equal(
  71. (object) ['a' => [], 'b' => [], 'c' => [], 'd' => [], 'e' => [], 'f' => [], 'g' => [], 'h' => (object) []],
  72. (new Processor)->process($schema, [])
  73. );
  74. });
  75. test('default value must be readonly', function () {
  76. Assert::exception(function () {
  77. $schema = Expect::structure([])->default([]);
  78. }, Nette\InvalidStateException::class);
  79. });
  80. test('with indexed item', function () {
  81. $schema = Expect::structure([
  82. 'key1' => Expect::string(),
  83. 'key2' => Expect::string(),
  84. Expect::string(),
  85. 'arr' => Expect::arrayOf('string'),
  86. ]);
  87. $processor = new Processor;
  88. Assert::equal(
  89. (object) [
  90. 'key1' => null,
  91. 'key2' => null,
  92. null,
  93. 'arr' => [],
  94. ],
  95. $processor->process($schema, [])
  96. );
  97. Assert::equal(
  98. (object) [
  99. 'key1' => null,
  100. 'key2' => null,
  101. null,
  102. 'arr' => [],
  103. ],
  104. $processor->processMultiple($schema, [])
  105. );
  106. checkValidationErrors(function () use ($processor, $schema) {
  107. $processor->process($schema, [1, 2, 3]);
  108. }, [
  109. "Unexpected item '1'.",
  110. "Unexpected item '2'.",
  111. "The item '0' expects to be string, 1 given.",
  112. ]);
  113. Assert::equal(
  114. (object) [
  115. 'key1' => 'newval',
  116. 'key2' => null,
  117. 'newval3',
  118. 'arr' => [],
  119. ],
  120. $processor->process($schema, ['key1' => 'newval', 'newval3'])
  121. );
  122. checkValidationErrors(function () use ($processor, $schema) {
  123. $processor->processMultiple($schema, [['key1' => 'newval', 'newval3'], ['key2' => 'newval', 'newval4']]);
  124. }, ["Unexpected item '1'."]);
  125. });
  126. test('with indexed item & otherItems', function () {
  127. $schema = Expect::structure([
  128. 'key1' => Expect::string(),
  129. 'key2' => Expect::string(),
  130. Expect::string(),
  131. 'arr' => Expect::arrayOf('string'),
  132. ])->otherItems('scalar');
  133. $processor = new Processor;
  134. Assert::equal(
  135. (object) [
  136. 'key1' => null,
  137. 'key2' => null,
  138. null,
  139. 'arr' => [],
  140. ],
  141. $processor->process($schema, [])
  142. );
  143. Assert::equal(
  144. (object) [
  145. 'key1' => null,
  146. 'key2' => null,
  147. null,
  148. 'arr' => [],
  149. ],
  150. $processor->processMultiple($schema, [])
  151. );
  152. checkValidationErrors(function () use ($processor, $schema) {
  153. $processor->process($schema, [1, 2, 3]);
  154. }, ["The item '0' expects to be string, 1 given."]);
  155. Assert::equal(
  156. (object) [
  157. 'key1' => 'newval',
  158. 'key2' => null,
  159. 'newval3',
  160. 'arr' => [],
  161. ],
  162. $processor->process($schema, ['key1' => 'newval', 'newval3'])
  163. );
  164. Assert::equal(
  165. (object) [
  166. 'key1' => 'newval1',
  167. 'key2' => 'newval2',
  168. 'newval3',
  169. 'arr' => [],
  170. 'newval4',
  171. ],
  172. $processor->processMultiple($schema, [['key1' => 'newval', 'newval3'], ['key1' => 'newval1', 'key2' => 'newval2', 'newval4']])
  173. );
  174. });
  175. test('item with default value', function () {
  176. $schema = Expect::structure([
  177. 'b' => Expect::string(123),
  178. ]);
  179. Assert::equal((object) ['b' => 123], (new Processor)->process($schema, []));
  180. checkValidationErrors(function () use ($schema) {
  181. (new Processor)->process($schema, [1, 2, 3]);
  182. }, [
  183. "Unexpected item '0', did you mean 'b'?",
  184. "Unexpected item '1', did you mean 'b'?",
  185. "Unexpected item '2', did you mean 'b'?",
  186. ]);
  187. Assert::equal((object) ['b' => 123], (new Processor)->process($schema, []));
  188. checkValidationErrors(function () use ($schema) {
  189. (new Processor)->process($schema, ['b' => 123]);
  190. }, ["The item 'b' expects to be string, 123 given."]);
  191. checkValidationErrors(function () use ($schema) {
  192. (new Processor)->process($schema, ['b' => null]);
  193. }, ["The item 'b' expects to be string, null given."]);
  194. Assert::equal((object) ['b' => 'val'], (new Processor)->process($schema, ['b' => 'val']));
  195. });
  196. test('item without default value', function () {
  197. $schema = Expect::structure([
  198. 'b' => Expect::string(),
  199. ]);
  200. Assert::equal((object) ['b' => null], (new Processor)->process($schema, []));
  201. checkValidationErrors(function () use ($schema) {
  202. (new Processor)->process($schema, ['b' => 123]);
  203. }, ["The item 'b' expects to be string, 123 given."]);
  204. checkValidationErrors(function () use ($schema) {
  205. (new Processor)->process($schema, ['b' => null]);
  206. }, ["The item 'b' expects to be string, null given."]);
  207. Assert::equal((object) ['b' => 'val'], (new Processor)->process($schema, ['b' => 'val']));
  208. });
  209. test('required item', function () {
  210. $schema = Expect::structure([
  211. 'b' => Expect::string()->required(),
  212. 'c' => Expect::array()->required(),
  213. ]);
  214. checkValidationErrors(function () use ($schema) {
  215. (new Processor)->process($schema, []);
  216. }, [
  217. "The mandatory item 'b' is missing.",
  218. "The mandatory item 'c' is missing.",
  219. ]);
  220. checkValidationErrors(function () use ($schema) {
  221. (new Processor)->process($schema, ['b' => 'val']);
  222. }, ["The mandatory item 'c' is missing."]);
  223. Assert::equal(
  224. (object) ['b' => 'val', 'c' => [1, 2, 3]],
  225. (new Processor)->process($schema, ['b' => 'val', 'c' => [1, 2, 3]])
  226. );
  227. });
  228. test('other items', function () {
  229. $schema = Expect::structure([
  230. 'key' => Expect::string(),
  231. ])->otherItems(Expect::string());
  232. Assert::equal((object) ['key' => null], (new Processor)->process($schema, []));
  233. Assert::equal((object) ['key' => null, 'other' => 'foo'], (new Processor)->process($schema, ['other' => 'foo']));
  234. checkValidationErrors(function () use ($schema) {
  235. (new Processor)->process($schema, ['other' => 123]);
  236. }, ["The item 'other' expects to be string, 123 given."]);
  237. });
  238. test('structure items', function () {
  239. $schema = Expect::structure([
  240. 'a' => Expect::structure([
  241. 'x' => Expect::string('defval'),
  242. ]),
  243. 'b' => Expect::structure([
  244. 'y' => Expect::string()->required(),
  245. ]),
  246. ]);
  247. checkValidationErrors(function () use ($schema) {
  248. (new Processor)->process($schema, []);
  249. }, ["The mandatory item 'b\u{a0}›\u{a0}y' is missing."]);
  250. checkValidationErrors(function () use ($schema) {
  251. (new Processor)->process($schema, [1, 2, 3]);
  252. }, [
  253. "Unexpected item '0', did you mean 'a'?",
  254. "Unexpected item '1', did you mean 'a'?",
  255. "Unexpected item '2', did you mean 'a'?",
  256. "The mandatory item 'b\u{a0}›\u{a0}y' is missing.",
  257. ]);
  258. checkValidationErrors(function () use ($schema) {
  259. (new Processor)->process($schema, ['a' => 'val']);
  260. }, [
  261. "The item 'a' expects to be array, 'val' given.",
  262. "The mandatory item 'b\u{a0}›\u{a0}y' is missing.",
  263. ]);
  264. checkValidationErrors(function () use ($schema) {
  265. (new Processor)->process($schema, ['a' => null]);
  266. }, ["The mandatory item 'b\u{a0}›\u{a0}y' is missing."]);
  267. checkValidationErrors(function () use ($schema) {
  268. (new Processor)->process($schema, ['b' => 123]);
  269. }, ["The item 'b' expects to be array, 123 given."]);
  270. checkValidationErrors(function () use ($schema) {
  271. (new Processor)->process($schema, ['b' => null]);
  272. }, ["The mandatory item 'b\u{a0}›\u{a0}y' is missing."]);
  273. checkValidationErrors(function () use ($schema) {
  274. (new Processor)->process($schema, ['b' => 'val']);
  275. }, ["The item 'b' expects to be array, 'val' given."]);
  276. checkValidationErrors(function () use ($schema) {
  277. (new Processor)->process($schema, ['b' => ['x' => 'val']]);
  278. }, [
  279. "Unexpected item 'b\u{a0}›\u{a0}x', did you mean 'y'?",
  280. "The mandatory item 'b\u{a0}›\u{a0}y' is missing.",
  281. ]);
  282. checkValidationErrors(function () use ($schema) {
  283. (new Processor)->process($schema, ['b' => ['x1' => 'val', 'x2' => 'val']]);
  284. }, [
  285. "Unexpected item 'b\u{a0}›\u{a0}x1'.",
  286. "Unexpected item 'b\u{a0}›\u{a0}x2'.",
  287. "The mandatory item 'b\u{a0}›\u{a0}y' is missing.",
  288. ]);
  289. checkValidationErrors(function () use ($schema) {
  290. (new Processor)->process($schema, ['b' => ['y' => 123]]);
  291. }, ["The item 'b\u{a0}›\u{a0}y' expects to be string, 123 given."]);
  292. Assert::equal(
  293. (object) ['a' => (object) ['x' => 'defval'], 'b' => (object) ['y' => 'val']],
  294. (new Processor)->process($schema, ['b' => ['y' => 'val']])
  295. );
  296. });
  297. test('processing', function () {
  298. $schema = Expect::structure([
  299. 'a' => Expect::structure([
  300. 'x' => Expect::string('defval'),
  301. ]),
  302. 'b' => Expect::structure([
  303. 'y' => Expect::string()->required(),
  304. ]),
  305. ]);
  306. Assert::equal(
  307. (object) ['a' => (object) ['x' => 'defval'], 'b' => (object) ['y' => 'newval']],
  308. (new Processor)->process($schema, ['b' => ['y' => 'newval']])
  309. );
  310. Assert::equal(
  311. (object) ['a' => (object) ['x' => 'newval'], 'b' => (object) ['y' => 'newval']],
  312. (new Processor)->processMultiple($schema, [['a' => ['x' => 'newval']], ['b' => ['y' => 'newval']]])
  313. );
  314. Assert::equal(
  315. (object) ['a' => (object) ['x' => 'defval'], 'b' => (object) ['y' => 'newval']],
  316. (new Processor)->processMultiple($schema, [null, ['b' => ['y' => 'newval']]])
  317. );
  318. Assert::equal(
  319. (object) ['a' => (object) ['x' => 'defval'], 'b' => (object) ['y' => 'newval']],
  320. (new Processor)->processMultiple($schema, [['b' => ['y' => 'newval']], null])
  321. );
  322. });
  323. test('processing without default values', function () {
  324. $schema = Expect::structure([
  325. 'a' => Expect::string(), // implicit default
  326. 'b' => Expect::string('hello'), // explicit default
  327. 'c' => Expect::string()->nullable(),
  328. 'd' => Expect::string()->required(),
  329. ]);
  330. $processor = new Processor;
  331. $processor->skipDefaults();
  332. checkValidationErrors(function () use ($schema, $processor) {
  333. $processor->process($schema, []);
  334. }, ["The mandatory item 'd' is missing."]);
  335. Assert::equal(
  336. (object) ['d' => 'newval'],
  337. $processor->process($schema, ['d' => 'newval'])
  338. );
  339. });
  340. test('optional structure', function () {
  341. $schema = Expect::structure([
  342. 'req' => Expect::string()->required(),
  343. 'optional' => Expect::structure([
  344. 'req' => Expect::string()->required(),
  345. 'foo' => Expect::string(),
  346. ])->required(false),
  347. ]);
  348. $processor = new Processor;
  349. Assert::equal(
  350. (object) [
  351. 'req' => 'hello',
  352. 'optional' => null,
  353. ],
  354. $processor->process($schema, ['req' => 'hello'])
  355. );
  356. checkValidationErrors(function () use ($schema, $processor) {
  357. $processor->process($schema, ['req' => 'hello', 'optional' => ['foo' => 'Foo']]);
  358. }, ["The mandatory item 'optional\u{a0}›\u{a0}req' is missing."]);
  359. });
  360. test('deprecated item', function () {
  361. $schema = Expect::structure([
  362. 'b' => Expect::string()->deprecated('depr %path%'),
  363. ]);
  364. $processor = new Processor;
  365. Assert::equal(
  366. (object) ['b' => 'val'],
  367. $processor->process($schema, ['b' => 'val'])
  368. );
  369. Assert::same(["depr 'b'"], $processor->getWarnings());
  370. });
  371. test('deprecated other items', function () {
  372. $schema = Expect::structure([
  373. 'key' => Expect::string(),
  374. ])->otherItems(Expect::string()->deprecated());
  375. $processor = new Processor;
  376. Assert::equal((object) ['key' => null], $processor->process($schema, []));
  377. Assert::same([], $processor->getWarnings());
  378. Assert::equal((object) ['key' => null, 'other' => 'foo'], $processor->process($schema, ['other' => 'foo']));
  379. Assert::same(["The item 'other' is deprecated."], $processor->getWarnings());
  380. });
  381. test('processing without default values skipped on structure', function () {
  382. $schema = Expect::structure([
  383. 'foo1' => Expect::structure([
  384. 'bar' => Expect::string()->default('baz'),
  385. ])->skipDefaults()->castTo('array'),
  386. 'foo2' => Expect::structure([
  387. 'bar' => Expect::string()->default('baz'),
  388. ])->castTo('array'),
  389. ])->castTo('array');
  390. $processor = new Processor;
  391. Assert::equal(
  392. [
  393. 'foo1' => [],
  394. 'foo2' => ['bar' => 'baz'],
  395. ],
  396. $processor->process($schema, [])
  397. );
  398. });