CollectionValidatorTest.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Tests\Component\Validator\Constraints;
  11. use Symfony\Component\Validator\ExecutionContext;
  12. use Symfony\Component\Validator\Constraints\Min;
  13. use Symfony\Component\Validator\Constraints\NotBlank;
  14. use Symfony\Component\Validator\Constraints\Collection;
  15. use Symfony\Component\Validator\Constraints\CollectionValidator;
  16. /**
  17. * This class is a hand written simplified version of PHP native `ArrayObject`
  18. * class, to show that it behaves different than PHP native implementation.
  19. */
  20. class TestArrayObject implements \ArrayAccess, \IteratorAggregate, \Countable, \Serializable
  21. {
  22. private $array;
  23. public function __construct(array $array = null)
  24. {
  25. $this->array = (array) ($array ?: array());
  26. }
  27. public function offsetExists($offset)
  28. {
  29. return array_key_exists($offset, $this->array);
  30. }
  31. public function offsetGet($offset)
  32. {
  33. return $this->array[$offset];
  34. }
  35. public function offsetSet($offset, $value)
  36. {
  37. if (null === $offset) {
  38. $this->array[] = $value;
  39. } else {
  40. $this->array[$offset] = $value;
  41. }
  42. }
  43. public function offsetUnset($offset)
  44. {
  45. if (array_key_exists($offset, $this->array)) {
  46. unset($this->array[$offset]);
  47. }
  48. }
  49. public function getIterator()
  50. {
  51. return new \ArrayIterator($this->array);
  52. }
  53. public function count()
  54. {
  55. return count($this->array);
  56. }
  57. public function serialize()
  58. {
  59. return serialize($this->array);
  60. }
  61. public function unserialize($serialized)
  62. {
  63. $this->array = (array) unserialize((string) $serialized);
  64. }
  65. }
  66. class CollectionValidatorTest extends \PHPUnit_Framework_TestCase
  67. {
  68. protected $validator;
  69. protected $walker;
  70. protected $context;
  71. protected function setUp()
  72. {
  73. $this->walker = $this->getMock('Symfony\Component\Validator\GraphWalker', array(), array(), '', false);
  74. $metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface');
  75. $this->context = new ExecutionContext('Root', $this->walker, $metadataFactory);
  76. $this->validator = new CollectionValidator();
  77. $this->validator->initialize($this->context);
  78. }
  79. protected function tearDown()
  80. {
  81. $this->validator = null;
  82. $this->walker = null;
  83. $this->context = null;
  84. }
  85. public function testNullIsValid()
  86. {
  87. $this->assertTrue($this->validator->isValid(null, new Collection(array('fields' => array(
  88. 'foo' => new Min(4),
  89. )))));
  90. }
  91. public function testFieldsAsDefaultOption()
  92. {
  93. $this->assertTrue($this->validator->isValid(array('foo' => 'foobar'), new Collection(array(
  94. 'foo' => new Min(4),
  95. ))));
  96. $this->assertTrue($this->validator->isValid(new \ArrayObject(array('foo' => 'foobar')), new Collection(array(
  97. 'foo' => new Min(4),
  98. ))));
  99. $this->assertTrue($this->validator->isValid(new TestArrayObject(array('foo' => 'foobar')), new Collection(array(
  100. 'foo' => new Min(4),
  101. ))));
  102. }
  103. /**
  104. * @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException
  105. */
  106. public function testThrowsExceptionIfNotTraversable()
  107. {
  108. $this->validator->isValid('foobar', new Collection(array('fields' => array(
  109. 'foo' => new Min(4),
  110. ))));
  111. }
  112. /**
  113. * @dataProvider getValidArguments
  114. */
  115. public function testWalkSingleConstraint($array)
  116. {
  117. $this->context->setGroup('MyGroup');
  118. $this->context->setPropertyPath('foo');
  119. $constraint = new Min(4);
  120. foreach ($array as $key => $value) {
  121. $this->walker->expects($this->once())
  122. ->method('walkConstraint')
  123. ->with($this->equalTo($constraint), $this->equalTo($value), $this->equalTo('MyGroup'), $this->equalTo('foo['.$key.']'));
  124. }
  125. $this->assertTrue($this->validator->isValid($array, new Collection(array(
  126. 'fields' => array(
  127. 'foo' => $constraint,
  128. ),
  129. ))));
  130. }
  131. /**
  132. * @dataProvider getValidArguments
  133. */
  134. public function testWalkMultipleConstraints($array)
  135. {
  136. $this->context->setGroup('MyGroup');
  137. $this->context->setPropertyPath('foo');
  138. $constraint = new Min(4);
  139. // can only test for twice the same constraint because PHPUnits mocking
  140. // can't test method calls with different arguments
  141. $constraints = array($constraint, $constraint);
  142. foreach ($array as $key => $value) {
  143. $this->walker->expects($this->exactly(2))
  144. ->method('walkConstraint')
  145. ->with($this->equalTo($constraint), $this->equalTo($value), $this->equalTo('MyGroup'), $this->equalTo('foo['.$key.']'));
  146. }
  147. $this->assertTrue($this->validator->isValid($array, new Collection(array(
  148. 'fields' => array(
  149. 'foo' => $constraints,
  150. )
  151. ))));
  152. }
  153. /**
  154. * @dataProvider getArgumentsWithExtraFields
  155. */
  156. public function testExtraFieldsDisallowed($array)
  157. {
  158. $this->assertFalse($this->validator->isValid($array, new Collection(array(
  159. 'fields' => array(
  160. 'foo' => new Min(4),
  161. ),
  162. ))));
  163. }
  164. // bug fix
  165. public function testNullNotConsideredExtraField()
  166. {
  167. $array = array(
  168. 'foo' => null,
  169. );
  170. $collection = new Collection(array(
  171. 'fields' => array(
  172. 'foo' => new Min(4),
  173. ),
  174. ));
  175. $this->assertTrue($this->validator->isValid($array, $collection));
  176. $this->assertTrue($this->validator->isValid(new \ArrayObject($array), $collection));
  177. $this->assertTrue($this->validator->isValid(new TestArrayObject($array), $collection));
  178. }
  179. public function testExtraFieldsAllowed()
  180. {
  181. $array = array(
  182. 'foo' => 5,
  183. 'bar' => 6,
  184. );
  185. $collection = new Collection(array(
  186. 'fields' => array(
  187. 'foo' => new Min(4),
  188. ),
  189. 'allowExtraFields' => true,
  190. ));
  191. $this->assertTrue($this->validator->isValid($array, $collection));
  192. $this->assertTrue($this->validator->isValid(new \ArrayObject($array), $collection));
  193. $this->assertTrue($this->validator->isValid(new TestArrayObject($array), $collection));
  194. }
  195. public function testMissingFieldsDisallowed()
  196. {
  197. $this->assertFalse($this->validator->isValid(array(), new Collection(array(
  198. 'fields' => array(
  199. 'foo' => new Min(4),
  200. ),
  201. ))));
  202. $this->assertFalse($this->validator->isValid(new \ArrayObject(array()), new Collection(array(
  203. 'fields' => array(
  204. 'foo' => new Min(4),
  205. ),
  206. ))));
  207. $this->assertFalse($this->validator->isValid(new TestArrayObject(array()), new Collection(array(
  208. 'fields' => array(
  209. 'foo' => new Min(4),
  210. ),
  211. ))));
  212. }
  213. public function testMissingFieldsAllowed()
  214. {
  215. $this->assertTrue($this->validator->isValid(array(), new Collection(array(
  216. 'fields' => array(
  217. 'foo' => new Min(4),
  218. ),
  219. 'allowMissingFields' => true,
  220. ))));
  221. $this->assertTrue($this->validator->isValid(new \ArrayObject(array()), new Collection(array(
  222. 'fields' => array(
  223. 'foo' => new Min(4),
  224. ),
  225. 'allowMissingFields' => true,
  226. ))));
  227. $this->assertTrue($this->validator->isValid(new TestArrayObject(array()), new Collection(array(
  228. 'fields' => array(
  229. 'foo' => new Min(4),
  230. ),
  231. 'allowMissingFields' => true,
  232. ))));
  233. }
  234. public function testArrayAccessObject()
  235. {
  236. $value = new TestArrayObject();
  237. $value['foo'] = 12;
  238. $value['asdf'] = 'asdfaf';
  239. $this->assertTrue(isset($value['asdf']));
  240. $this->assertTrue(isset($value['foo']));
  241. $this->assertFalse(empty($value['asdf']));
  242. $this->assertFalse(empty($value['foo']));
  243. $result = $this->validator->isValid($value, new Collection(array(
  244. 'fields' => array(
  245. 'foo' => new NotBlank(),
  246. 'asdf' => new NotBlank()
  247. )
  248. )));
  249. $this->assertTrue($result);
  250. }
  251. public function testArrayObject()
  252. {
  253. $value = new \ArrayObject(array());
  254. $value['foo'] = 12;
  255. $value['asdf'] = 'asdfaf';
  256. $this->assertTrue(isset($value['asdf']));
  257. $this->assertTrue(isset($value['foo']));
  258. $this->assertFalse(empty($value['asdf']));
  259. $this->assertFalse(empty($value['foo']));
  260. $result = $this->validator->isValid($value, new Collection(array(
  261. 'fields' => array(
  262. 'foo' => new NotBlank(),
  263. 'asdf' => new NotBlank()
  264. )
  265. )));
  266. $this->assertTrue($result);
  267. }
  268. public function testObjectShouldBeLeftUnchanged()
  269. {
  270. $value = new \ArrayObject(array(
  271. 'foo' => 3
  272. ));
  273. $this->validator->isValid($value, new Collection(array(
  274. 'fields' => array(
  275. 'foo' => new Min(2),
  276. )
  277. )));
  278. $this->assertEquals(array(
  279. 'foo' => 3
  280. ), (array) $value);
  281. }
  282. public function getValidArguments()
  283. {
  284. return array(
  285. // can only test for one entry, because PHPUnits mocking does not allow
  286. // to expect multiple method calls with different arguments
  287. array(array('foo' => 3)),
  288. array(new \ArrayObject(array('foo' => 3))),
  289. array(new TestArrayObject(array('foo' => 3))),
  290. );
  291. }
  292. public function getArgumentsWithExtraFields()
  293. {
  294. return array(
  295. array(array(
  296. 'foo' => 5,
  297. 'bar' => 6,
  298. )),
  299. array(new \ArrayObject(array(
  300. 'foo' => 5,
  301. 'bar' => 6,
  302. ))),
  303. array(new TestArrayObject(array(
  304. 'foo' => 5,
  305. 'bar' => 6,
  306. )))
  307. );
  308. }
  309. }