MongoDbSessionHandlerTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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\Component\HttpFoundation\Tests\Session\Storage\Handler;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
  13. /**
  14. * @author Markus Bachmann <markus.bachmann@bachi.biz>
  15. * @group time-sensitive
  16. */
  17. class MongoDbSessionHandlerTest extends TestCase
  18. {
  19. /**
  20. * @var \PHPUnit_Framework_MockObject_MockObject
  21. */
  22. private $mongo;
  23. private $storage;
  24. public $options;
  25. protected function setUp()
  26. {
  27. parent::setUp();
  28. if (!extension_loaded('mongo') && !extension_loaded('mongodb')) {
  29. $this->markTestSkipped('The Mongo or MongoDB extension is required.');
  30. }
  31. if (phpversion('mongodb')) {
  32. $mongoClass = 'MongoDB\Client';
  33. } else {
  34. $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
  35. }
  36. $this->mongo = $this->getMockBuilder($mongoClass)
  37. ->disableOriginalConstructor()
  38. ->getMock();
  39. $this->options = array(
  40. 'id_field' => '_id',
  41. 'data_field' => 'data',
  42. 'time_field' => 'time',
  43. 'expiry_field' => 'expires_at',
  44. 'database' => 'sf2-test',
  45. 'collection' => 'session-test',
  46. );
  47. $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
  48. }
  49. /**
  50. * @expectedException \InvalidArgumentException
  51. */
  52. public function testConstructorShouldThrowExceptionForInvalidMongo()
  53. {
  54. new MongoDbSessionHandler(new \stdClass(), $this->options);
  55. }
  56. /**
  57. * @expectedException \InvalidArgumentException
  58. */
  59. public function testConstructorShouldThrowExceptionForMissingOptions()
  60. {
  61. new MongoDbSessionHandler($this->mongo, array());
  62. }
  63. public function testOpenMethodAlwaysReturnTrue()
  64. {
  65. $this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true');
  66. }
  67. public function testCloseMethodAlwaysReturnTrue()
  68. {
  69. $this->assertTrue($this->storage->close(), 'The "close" method should always return true');
  70. }
  71. public function testRead()
  72. {
  73. $collection = $this->createMongoCollectionMock();
  74. $this->mongo->expects($this->once())
  75. ->method('selectCollection')
  76. ->with($this->options['database'], $this->options['collection'])
  77. ->will($this->returnValue($collection));
  78. // defining the timeout before the actual method call
  79. // allows to test for "greater than" values in the $criteria
  80. $testTimeout = time() + 1;
  81. $collection->expects($this->once())
  82. ->method('findOne')
  83. ->will($this->returnCallback(function ($criteria) use ($testTimeout) {
  84. $this->assertArrayHasKey($this->options['id_field'], $criteria);
  85. $this->assertEquals($criteria[$this->options['id_field']], 'foo');
  86. $this->assertArrayHasKey($this->options['expiry_field'], $criteria);
  87. $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
  88. if (phpversion('mongodb')) {
  89. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
  90. $this->assertGreaterThanOrEqual(round(((int) $criteria[$this->options['expiry_field']]['$gte']) / 1000), $testTimeout);
  91. } else {
  92. $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
  93. $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
  94. }
  95. $fields = array(
  96. $this->options['id_field'] => 'foo',
  97. );
  98. if (phpversion('mongodb')) {
  99. $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
  100. $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
  101. } else {
  102. $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
  103. $fields[$this->options['id_field']] = new \MongoDate();
  104. }
  105. return $fields;
  106. }));
  107. $this->assertEquals('bar', $this->storage->read('foo'));
  108. }
  109. public function testWrite()
  110. {
  111. $collection = $this->createMongoCollectionMock();
  112. $this->mongo->expects($this->once())
  113. ->method('selectCollection')
  114. ->with($this->options['database'], $this->options['collection'])
  115. ->will($this->returnValue($collection));
  116. $data = array();
  117. $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
  118. $collection->expects($this->once())
  119. ->method($methodName)
  120. ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
  121. $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
  122. if (phpversion('mongodb')) {
  123. $this->assertEquals(array('upsert' => true), $options);
  124. } else {
  125. $this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
  126. }
  127. $data = $updateData['$set'];
  128. }));
  129. $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
  130. $this->assertTrue($this->storage->write('foo', 'bar'));
  131. if (phpversion('mongodb')) {
  132. $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
  133. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
  134. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
  135. $this->assertGreaterThanOrEqual($expectedExpiry, round(((int) $data[$this->options['expiry_field']]) / 1000));
  136. } else {
  137. $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
  138. $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
  139. $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
  140. $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
  141. }
  142. }
  143. public function testWriteWhenUsingExpiresField()
  144. {
  145. $this->options = array(
  146. 'id_field' => '_id',
  147. 'data_field' => 'data',
  148. 'time_field' => 'time',
  149. 'database' => 'sf2-test',
  150. 'collection' => 'session-test',
  151. 'expiry_field' => 'expiresAt',
  152. );
  153. $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
  154. $collection = $this->createMongoCollectionMock();
  155. $this->mongo->expects($this->once())
  156. ->method('selectCollection')
  157. ->with($this->options['database'], $this->options['collection'])
  158. ->will($this->returnValue($collection));
  159. $data = array();
  160. $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
  161. $collection->expects($this->once())
  162. ->method($methodName)
  163. ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
  164. $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
  165. if (phpversion('mongodb')) {
  166. $this->assertEquals(array('upsert' => true), $options);
  167. } else {
  168. $this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
  169. }
  170. $data = $updateData['$set'];
  171. }));
  172. $this->assertTrue($this->storage->write('foo', 'bar'));
  173. if (phpversion('mongodb')) {
  174. $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
  175. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
  176. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
  177. } else {
  178. $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
  179. $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
  180. $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
  181. }
  182. }
  183. public function testReplaceSessionData()
  184. {
  185. $collection = $this->createMongoCollectionMock();
  186. $this->mongo->expects($this->once())
  187. ->method('selectCollection')
  188. ->with($this->options['database'], $this->options['collection'])
  189. ->will($this->returnValue($collection));
  190. $data = array();
  191. $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
  192. $collection->expects($this->exactly(2))
  193. ->method($methodName)
  194. ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
  195. $data = $updateData;
  196. }));
  197. $this->storage->write('foo', 'bar');
  198. $this->storage->write('foo', 'foobar');
  199. if (phpversion('mongodb')) {
  200. $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
  201. } else {
  202. $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
  203. }
  204. }
  205. public function testDestroy()
  206. {
  207. $collection = $this->createMongoCollectionMock();
  208. $this->mongo->expects($this->once())
  209. ->method('selectCollection')
  210. ->with($this->options['database'], $this->options['collection'])
  211. ->will($this->returnValue($collection));
  212. $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
  213. $collection->expects($this->once())
  214. ->method($methodName)
  215. ->with(array($this->options['id_field'] => 'foo'));
  216. $this->assertTrue($this->storage->destroy('foo'));
  217. }
  218. public function testGc()
  219. {
  220. $collection = $this->createMongoCollectionMock();
  221. $this->mongo->expects($this->once())
  222. ->method('selectCollection')
  223. ->with($this->options['database'], $this->options['collection'])
  224. ->will($this->returnValue($collection));
  225. $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
  226. $collection->expects($this->once())
  227. ->method($methodName)
  228. ->will($this->returnCallback(function ($criteria) {
  229. if (phpversion('mongodb')) {
  230. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
  231. $this->assertGreaterThanOrEqual(time() - 1, round(((int) $criteria[$this->options['expiry_field']]['$lt']) / 1000));
  232. } else {
  233. $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
  234. $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
  235. }
  236. }));
  237. $this->assertTrue($this->storage->gc(1));
  238. }
  239. public function testGetConnection()
  240. {
  241. $method = new \ReflectionMethod($this->storage, 'getMongo');
  242. $method->setAccessible(true);
  243. if (phpversion('mongodb')) {
  244. $mongoClass = 'MongoDB\Client';
  245. } else {
  246. $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
  247. }
  248. $this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
  249. }
  250. private function createMongoCollectionMock()
  251. {
  252. $collectionClass = 'MongoCollection';
  253. if (phpversion('mongodb')) {
  254. $collectionClass = 'MongoDB\Collection';
  255. }
  256. $collection = $this->getMockBuilder($collectionClass)
  257. ->disableOriginalConstructor()
  258. ->getMock();
  259. return $collection;
  260. }
  261. }