ValidatorTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Validation;
  16. use Cake\Validation\ValidationSet;
  17. use Cake\Validation\Validator;
  18. /**
  19. * Tests Validator class
  20. *
  21. */
  22. class ValidatorTest extends \Cake\TestSuite\TestCase {
  23. /**
  24. * Testing you can dynamically add rules to a field
  25. *
  26. * @return void
  27. */
  28. public function testAddingRulesToField() {
  29. $validator = new Validator;
  30. $validator->add('title', 'not-empty', ['rule' => 'notEmpty']);
  31. $set = $validator->field('title');
  32. $this->assertInstanceOf('\Cake\Validation\ValidationSet', $set);
  33. $this->assertCount(1, $set);
  34. $validator->add('title', 'another', ['rule' => 'alphanumeric']);
  35. $this->assertCount(2, $set);
  36. $validator->add('body', 'another', ['rule' => 'crazy']);
  37. $this->assertCount(1, $validator->field('body'));
  38. $this->assertCount(2, $validator);
  39. }
  40. /**
  41. * Tests that calling field will create a default validation set for it
  42. *
  43. * @return void
  44. */
  45. public function testFieldDefault() {
  46. $validator = new Validator;
  47. $this->assertFalse($validator->hasField('foo'));
  48. $field = $validator->field('foo');
  49. $this->assertInstanceOf('\Cake\Validation\ValidationSet', $field);
  50. $this->assertCount(0, $field);
  51. $this->assertTrue($validator->hasField('foo'));
  52. }
  53. /**
  54. * Tests that field method can be used as a setter
  55. *
  56. * @return void
  57. */
  58. public function testFieldSetter() {
  59. $validator = new Validator;
  60. $validationSet = new ValidationSet;
  61. $validator->field('thing', $validationSet);
  62. $this->assertSame($validationSet, $validator->field('thing'));
  63. }
  64. /**
  65. * Tests the remove method
  66. *
  67. * @return void
  68. */
  69. public function testRemove() {
  70. $validator = new Validator;
  71. $validator->add('title', 'not-empty', ['rule' => 'notEmpty']);
  72. $validator->add('title', 'foo', ['rule' => 'bar']);
  73. $this->assertCount(2, $validator->field('title'));
  74. $validator->remove('title');
  75. $this->assertCount(0, $validator->field('title'));
  76. $validator->remove('title');
  77. $validator->add('title', 'not-empty', ['rule' => 'notEmpty']);
  78. $validator->add('title', 'foo', ['rule' => 'bar']);
  79. $this->assertCount(2, $validator->field('title'));
  80. $validator->remove('title', 'foo');
  81. $this->assertCount(1, $validator->field('title'));
  82. $this->assertNull($validator->field('title')->rule('foo'));
  83. }
  84. /**
  85. * Tests the validatePresence method
  86. *
  87. * @return void
  88. */
  89. public function testValidatePresence() {
  90. $validator = new Validator;
  91. $this->assertSame($validator, $validator->validatePresence('title'));
  92. $this->assertTrue($validator->field('title')->isPresenceRequired());
  93. $validator->validatePresence('title', false);
  94. $this->assertFalse($validator->field('title')->isPresenceRequired());
  95. $validator->validatePresence('title', 'create');
  96. $this->assertEquals('create', $validator->field('title')->isPresenceRequired());
  97. $validator->validatePresence('title', 'update');
  98. $this->assertEquals('update', $validator->field('title')->isPresenceRequired());
  99. }
  100. /**
  101. * Tests the isPresenceRequired method
  102. *
  103. * @return void
  104. */
  105. public function testIsPresenceRequired() {
  106. $validator = new Validator;
  107. $this->assertSame($validator, $validator->validatePresence('title'));
  108. $this->assertTrue($validator->isPresenceRequired('title', true));
  109. $this->assertTrue($validator->isPresenceRequired('title', false));
  110. $validator->validatePresence('title', false);
  111. $this->assertFalse($validator->isPresenceRequired('title', true));
  112. $this->assertFalse($validator->isPresenceRequired('title', false));
  113. $validator->validatePresence('title', 'create');
  114. $this->assertTrue($validator->isPresenceRequired('title', true));
  115. $this->assertFalse($validator->isPresenceRequired('title', false));
  116. $validator->validatePresence('title', 'update');
  117. $this->assertTrue($validator->isPresenceRequired('title', false));
  118. $this->assertFalse($validator->isPresenceRequired('title', true));
  119. }
  120. /**
  121. * Tests errors generated when a field presence is required
  122. *
  123. * @return void
  124. */
  125. public function testErrorsWithPresenceRequired() {
  126. $validator = new Validator;
  127. $validator->validatePresence('title');
  128. $errors = $validator->errors(['foo' => 'something']);
  129. $expected = ['title' => ['This field is required']];
  130. $this->assertEquals($expected, $errors);
  131. $this->assertEmpty($validator->errors(['title' => 'bar']));
  132. $validator->validatePresence('title', false);
  133. $this->assertEmpty($validator->errors(['foo' => 'bar']));
  134. }
  135. /**
  136. * Tests custom error messages generated when a field presence is required
  137. *
  138. * @return void
  139. */
  140. public function testCustomErrorsWithPresenceRequired() {
  141. $validator = new Validator;
  142. $validator->validatePresence('title', true, 'Custom message');
  143. $errors = $validator->errors(['foo' => 'something']);
  144. $expected = ['title' => ['Custom message']];
  145. $this->assertEquals($expected, $errors);
  146. }
  147. /**
  148. * Tests the allowEmpty method
  149. *
  150. * @return void
  151. */
  152. public function testAllowEmpty() {
  153. $validator = new Validator;
  154. $this->assertSame($validator, $validator->allowEmpty('title'));
  155. $this->assertTrue($validator->field('title')->isEmptyAllowed());
  156. $validator->allowEmpty('title', 'create');
  157. $this->assertEquals('create', $validator->field('title')->isEmptyAllowed());
  158. $validator->allowEmpty('title', 'update');
  159. $this->assertEquals('update', $validator->field('title')->isEmptyAllowed());
  160. }
  161. /**
  162. * Test the notEmpty() method.
  163. *
  164. * @return void
  165. */
  166. public function testNotEmpty() {
  167. $validator = new Validator;
  168. $validator->notEmpty('title');
  169. $this->assertFalse($validator->field('title')->isEmptyAllowed());
  170. $validator->allowEmpty('title');
  171. $this->assertTrue($validator->field('title')->isEmptyAllowed());
  172. }
  173. /**
  174. * Test the notEmpty() method.
  175. *
  176. * @return void
  177. */
  178. public function testNotEmptyModes() {
  179. $validator = new Validator;
  180. $validator->notEmpty('title', 'Need a title', 'create');
  181. $this->assertFalse($validator->isEmptyAllowed('title', true));
  182. $this->assertTrue($validator->isEmptyAllowed('title', false));
  183. $validator->notEmpty('title', 'Need a title', 'update');
  184. $this->assertTrue($validator->isEmptyAllowed('title', true));
  185. $this->assertFalse($validator->isEmptyAllowed('title', false));
  186. $validator->notEmpty('title', 'Need a title');
  187. $this->assertFalse($validator->isEmptyAllowed('title', true));
  188. $this->assertFalse($validator->isEmptyAllowed('title', false));
  189. $validator->notEmpty('title');
  190. $this->assertFalse($validator->isEmptyAllowed('title', true));
  191. $this->assertFalse($validator->isEmptyAllowed('title', false));
  192. }
  193. /**
  194. * Test interactions between notEmpty() and isAllowed().
  195. *
  196. * @return void
  197. */
  198. public function testNotEmptyAndIsAllowed() {
  199. $validator = new Validator;
  200. $validator->allowEmpty('title')
  201. ->notEmpty('title', 'Need it', 'update');
  202. $this->assertTrue($validator->isEmptyAllowed('title', true));
  203. $this->assertFalse($validator->isEmptyAllowed('title', false));
  204. $validator->allowEmpty('title')
  205. ->notEmpty('title');
  206. $this->assertFalse($validator->isEmptyAllowed('title', true));
  207. $this->assertFalse($validator->isEmptyAllowed('title', false));
  208. $validator->notEmpty('title')
  209. ->allowEmpty('title', 'create');
  210. $this->assertTrue($validator->isEmptyAllowed('title', true));
  211. $this->assertFalse($validator->isEmptyAllowed('title', false));
  212. }
  213. /**
  214. * Tests the allowEmpty method when passing a callback
  215. *
  216. * @return void
  217. */
  218. public function testAllowEmptyCallback() {
  219. $validator = new Validator;
  220. $allow = true;
  221. $validator->allowEmpty('title', function($context) use (&$allow) {
  222. $this->assertEquals([], $context['data']);
  223. $this->assertEquals([], $context['providers']);
  224. $this->assertTrue($context['newRecord']);
  225. return $allow;
  226. });
  227. $this->assertTrue($validator->isEmptyAllowed('title', true));
  228. $allow = false;
  229. $this->assertFalse($validator->isEmptyAllowed('title', true));
  230. }
  231. /**
  232. * Tests the notEmpty method when passing a callback
  233. *
  234. * @return void
  235. */
  236. public function testNotEmptyCallback() {
  237. $validator = new Validator;
  238. $prevent = true;
  239. $validator->notEmpty('title', 'error message', function($context) use (&$prevent) {
  240. $this->assertEquals([], $context['data']);
  241. $this->assertEquals([], $context['providers']);
  242. $this->assertFalse($context['newRecord']);
  243. return $prevent;
  244. });
  245. $this->assertFalse($validator->isEmptyAllowed('title', false));
  246. $prevent = false;
  247. $this->assertTrue($validator->isEmptyAllowed('title', false));
  248. }
  249. /**
  250. * Tests the isEmptyAllowed method
  251. *
  252. * @return void
  253. */
  254. public function testIsEmptyAllowed() {
  255. $validator = new Validator;
  256. $this->assertSame($validator, $validator->allowEmpty('title'));
  257. $this->assertTrue($validator->isEmptyAllowed('title', true));
  258. $this->assertTrue($validator->isEmptyAllowed('title', false));
  259. $validator->notEmpty('title');
  260. $this->assertFalse($validator->isEmptyAllowed('title', true));
  261. $this->assertFalse($validator->isEmptyAllowed('title', false));
  262. $validator->allowEmpty('title', 'create');
  263. $this->assertTrue($validator->isEmptyAllowed('title', true));
  264. $this->assertFalse($validator->isEmptyAllowed('title', false));
  265. $validator->allowEmpty('title', 'update');
  266. $this->assertTrue($validator->isEmptyAllowed('title', false));
  267. $this->assertFalse($validator->isEmptyAllowed('title', true));
  268. }
  269. /**
  270. * Tests errors generated when a field is not allowed to be empty
  271. *
  272. * @return void
  273. */
  274. public function testErrorsWithEmptyNotAllowed() {
  275. $validator = new Validator;
  276. $validator->notEmpty('title');
  277. $errors = $validator->errors(['title' => '']);
  278. $expected = ['title' => ['This field cannot be left empty']];
  279. $this->assertEquals($expected, $errors);
  280. $errors = $validator->errors(['title' => []]);
  281. $expected = ['title' => ['This field cannot be left empty']];
  282. $this->assertEquals($expected, $errors);
  283. $errors = $validator->errors(['title' => null]);
  284. $expected = ['title' => ['This field cannot be left empty']];
  285. $this->assertEquals($expected, $errors);
  286. $errors = $validator->errors(['title' => 0]);
  287. $this->assertEmpty($errors);
  288. $errors = $validator->errors(['title' => '0']);
  289. $this->assertEmpty($errors);
  290. $errors = $validator->errors(['title' => false]);
  291. $this->assertEmpty($errors);
  292. }
  293. /**
  294. * Tests custom error mesages generated when a field is not allowed to be empty
  295. *
  296. * @return void
  297. */
  298. public function testCustomErrorsWithEmptyNotAllowed() {
  299. $validator = new Validator;
  300. $validator->notEmpty('title', 'Custom message');
  301. $errors = $validator->errors(['title' => '']);
  302. $expected = ['title' => ['Custom message']];
  303. $this->assertEquals($expected, $errors);
  304. }
  305. /**
  306. * Tests errors generated when a field is allowed to be empty
  307. *
  308. * @return void
  309. */
  310. public function testErrorsWithEmptyAllowed() {
  311. $validator = new Validator;
  312. $validator->allowEmpty('title');
  313. $errors = $validator->errors(['title' => '']);
  314. $this->assertEmpty($errors);
  315. $errors = $validator->errors(['title' => []]);
  316. $this->assertEmpty($errors);
  317. $errors = $validator->errors(['title' => null]);
  318. $this->assertEmpty($errors);
  319. $errors = $validator->errors(['title' => 0]);
  320. $this->assertEmpty($errors);
  321. $errors = $validator->errors(['title' => '0']);
  322. $this->assertEmpty($errors);
  323. $errors = $validator->errors(['title' => false]);
  324. $this->assertEmpty($errors);
  325. }
  326. /**
  327. * Test the provider() method
  328. *
  329. * @return void
  330. */
  331. public function testProvider() {
  332. $validator = new Validator;
  333. $object = new \stdClass;
  334. $this->assertSame($validator, $validator->provider('foo', $object));
  335. $this->assertSame($object, $validator->provider('foo'));
  336. $this->assertNull($validator->provider('bar'));
  337. $another = new \stdClass;
  338. $this->assertSame($validator, $validator->provider('bar', $another));
  339. $this->assertSame($another, $validator->provider('bar'));
  340. $this->assertEquals(new \Cake\Validation\RulesProvider, $validator->provider('default'));
  341. }
  342. /**
  343. * Tests errors() method when using validators from the default provider, this proves
  344. * that it returns a default validation message and the custom one set in the rule
  345. *
  346. * @return void
  347. */
  348. public function testErrorsFromDefaultProvider() {
  349. $validator = new Validator;
  350. $validator
  351. ->add('email', 'alpha', ['rule' => 'alphanumeric'])
  352. ->add('email', 'notEmpty', ['rule' => 'notEmpty'])
  353. ->add('email', 'email', ['rule' => 'email', 'message' => 'Y u no write email?']);
  354. $errors = $validator->errors(['email' => 'not an email!']);
  355. $expected = [
  356. 'email' => [
  357. 'alpha' => 'The provided value is invalid',
  358. 'email' => 'Y u no write email?'
  359. ]
  360. ];
  361. $this->assertEquals($expected, $errors);
  362. }
  363. /**
  364. * Tests using validation methods from different providers and returning the error
  365. * as a string
  366. *
  367. * @return void
  368. */
  369. public function testErrorsFromCustomProvider() {
  370. $validator = new Validator;
  371. $validator
  372. ->add('email', 'alpha', ['rule' => 'alphanumeric'])
  373. ->add('title', 'cool', ['rule' => 'isCool', 'provider' => 'thing']);
  374. $thing = $this->getMock('\stdClass', ['isCool']);
  375. $thing->expects($this->once())->method('isCool')
  376. ->will($this->returnCallback(function($data, $context) use ($thing) {
  377. $this->assertEquals('bar', $data);
  378. $expected = [
  379. 'default' => new \Cake\Validation\RulesProvider,
  380. 'thing' => $thing
  381. ];
  382. $expected = [
  383. 'newRecord' => true,
  384. 'providers' => $expected,
  385. 'data' => [
  386. 'email' => '!',
  387. 'title' => 'bar'
  388. ],
  389. 'field' => 'title'
  390. ];
  391. $this->assertEquals($expected, $context);
  392. return "That ain't cool, yo";
  393. }));
  394. $validator->provider('thing', $thing);
  395. $errors = $validator->errors(['email' => '!', 'title' => 'bar']);
  396. $expected = [
  397. 'email' => ['alpha' => 'The provided value is invalid'],
  398. 'title' => ['cool' => "That ain't cool, yo"]
  399. ];
  400. $this->assertEquals($expected, $errors);
  401. }
  402. /**
  403. * Tests that it is possible to pass extra arguments to the validation function
  404. * and it still gets the providers as last argument
  405. *
  406. * @return void
  407. */
  408. public function testMethodsWithExtraArguments() {
  409. $validator = new Validator;
  410. $validator->add('title', 'cool', [
  411. 'rule' => ['isCool', 'and', 'awesome'],
  412. 'provider' => 'thing'
  413. ]);
  414. $thing = $this->getMock('\stdClass', ['isCool']);
  415. $thing->expects($this->once())->method('isCool')
  416. ->will($this->returnCallback(function($data, $a, $b, $context) use ($thing) {
  417. $this->assertEquals('bar', $data);
  418. $this->assertEquals('and', $a);
  419. $this->assertEquals('awesome', $b);
  420. $expected = [
  421. 'default' => new \Cake\Validation\RulesProvider,
  422. 'thing' => $thing
  423. ];
  424. $expected = [
  425. 'newRecord' => true,
  426. 'providers' => $expected,
  427. 'data' => [
  428. 'email' => '!',
  429. 'title' => 'bar'
  430. ],
  431. 'field' => 'title'
  432. ];
  433. $this->assertEquals($expected, $context);
  434. return "That ain't cool, yo";
  435. }));
  436. $validator->provider('thing', $thing);
  437. $errors = $validator->errors(['email' => '!', 'title' => 'bar']);
  438. $expected = [
  439. 'title' => ['cool' => "That ain't cool, yo"]
  440. ];
  441. $this->assertEquals($expected, $errors);
  442. }
  443. /**
  444. * Tests that it is possible to use a closure as a rule
  445. *
  446. * @return void
  447. */
  448. public function testUsingClosureAsRule() {
  449. $validator = new Validator;
  450. $validator->add('name', 'myRule', [
  451. 'rule' => function($data, $provider) {
  452. $this->assertEquals('foo', $data);
  453. return 'You fail';
  454. }
  455. ]);
  456. $expected = ['name' => ['myRule' => 'You fail']];
  457. $this->assertEquals($expected, $validator->errors(['name' => 'foo']));
  458. }
  459. /**
  460. * Tests that setting last to a rule will stop validating the rest of the rules
  461. *
  462. * @return void
  463. */
  464. public function testErrorsWithLastRule() {
  465. $validator = new Validator;
  466. $validator
  467. ->add('email', 'alpha', ['rule' => 'alphanumeric', 'last' => true])
  468. ->add('email', 'email', ['rule' => 'email', 'message' => 'Y u no write email?']);
  469. $errors = $validator->errors(['email' => 'not an email!']);
  470. $expected = [
  471. 'email' => [
  472. 'alpha' => 'The provided value is invalid'
  473. ]
  474. ];
  475. $this->assertEquals($expected, $errors);
  476. }
  477. /**
  478. * Tests it is possible to get validation sets for a field using an array interface
  479. *
  480. * @return void
  481. */
  482. public function testArrayAccessGet() {
  483. $validator = new Validator;
  484. $validator
  485. ->add('email', 'alpha', ['rule' => 'alphanumeric'])
  486. ->add('title', 'cool', ['rule' => 'isCool', 'provider' => 'thing']);
  487. $this->assertSame($validator['email'], $validator->field('email'));
  488. $this->assertSame($validator['title'], $validator->field('title'));
  489. }
  490. /**
  491. * Tests it is possible to check for validation sets for a field using an array inteface
  492. *
  493. * @return void
  494. */
  495. public function testArrayAccessExists() {
  496. $validator = new Validator;
  497. $validator
  498. ->add('email', 'alpha', ['rule' => 'alphanumeric'])
  499. ->add('title', 'cool', ['rule' => 'isCool', 'provider' => 'thing']);
  500. $this->assertTrue(isset($validator['email']));
  501. $this->assertTrue(isset($validator['title']));
  502. $this->assertFalse(isset($validator['foo']));
  503. }
  504. /**
  505. * Tests it is possible to set validation rules for a field using an array inteface
  506. *
  507. * @return void
  508. */
  509. public function testArrayAccessSet() {
  510. $validator = new Validator;
  511. $validator
  512. ->add('email', 'alpha', ['rule' => 'alphanumeric'])
  513. ->add('title', 'cool', ['rule' => 'isCool', 'provider' => 'thing']);
  514. $validator['name'] = $validator->field('title');
  515. $this->assertSame($validator->field('title'), $validator->field('name'));
  516. $validator['name'] = ['alpha' => ['rule' => 'alphanumeric']];
  517. $this->assertEquals($validator->field('email'), $validator->field('email'));
  518. }
  519. /**
  520. * Tests it is possible to unset validation rules
  521. *
  522. * @return void
  523. */
  524. public function testArrayAccessUset() {
  525. $validator = new Validator;
  526. $validator
  527. ->add('email', 'alpha', ['rule' => 'alphanumeric'])
  528. ->add('title', 'cool', ['rule' => 'isCool', 'provider' => 'thing']);
  529. $this->assertTrue(isset($validator['title']));
  530. unset($validator['title']);
  531. $this->assertFalse(isset($validator['title']));
  532. }
  533. /**
  534. * Tests the countable interface
  535. *
  536. * @return void
  537. */
  538. public function testCount() {
  539. $validator = new Validator;
  540. $validator
  541. ->add('email', 'alpha', ['rule' => 'alphanumeric'])
  542. ->add('title', 'cool', ['rule' => 'isCool', 'provider' => 'thing']);
  543. $this->assertCount(2, $validator);
  544. }
  545. /**
  546. * Tests adding rules via alternative syntax
  547. *
  548. * @return void
  549. */
  550. public function testAddMulitple() {
  551. $validator = new Validator;
  552. $validator->add('title', [
  553. 'notEmpty' => [
  554. 'rule' => 'notEmpty'
  555. ],
  556. 'length' => [
  557. 'rule' => ['minLength', 10],
  558. 'message' => 'Titles need to be at least 10 characters long'
  559. ]
  560. ]);
  561. $set = $validator->field('title');
  562. $this->assertInstanceOf('\Cake\Validation\ValidationSet', $set);
  563. $this->assertCount(2, $set);
  564. }
  565. }