MongoDbSessionHandlerTest.php 12 KB

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