菜谱项目

MongoDbSessionHandlerTest.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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. // defining the timeout before the actual method call
  83. // allows to test for "greater than" values in the $criteria
  84. $testTimeout = time() + 1;
  85. $collection->expects($this->once())
  86. ->method('findOne')
  87. ->will($this->returnCallback(function ($criteria) use ($testTimeout) {
  88. $this->assertArrayHasKey($this->options['id_field'], $criteria);
  89. $this->assertEquals($criteria[$this->options['id_field']], 'foo');
  90. $this->assertArrayHasKey($this->options['expiry_field'], $criteria);
  91. $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
  92. if (phpversion('mongodb')) {
  93. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
  94. $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
  95. } else {
  96. $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
  97. $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
  98. }
  99. $fields = array(
  100. $this->options['id_field'] => 'foo',
  101. );
  102. if (phpversion('mongodb')) {
  103. $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
  104. $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
  105. } else {
  106. $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
  107. $fields[$this->options['id_field']] = new \MongoDate();
  108. }
  109. return $fields;
  110. }));
  111. $this->assertEquals('bar', $this->storage->read('foo'));
  112. }
  113. public function testWrite()
  114. {
  115. $collection = $this->createMongoCollectionMock();
  116. $this->mongo->expects($this->once())
  117. ->method('selectCollection')
  118. ->with($this->options['database'], $this->options['collection'])
  119. ->will($this->returnValue($collection));
  120. $data = array();
  121. $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
  122. $collection->expects($this->once())
  123. ->method($methodName)
  124. ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
  125. $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
  126. if (phpversion('mongodb')) {
  127. $this->assertEquals(array('upsert' => true), $options);
  128. } else {
  129. $this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
  130. }
  131. $data = $updateData['$set'];
  132. }));
  133. $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
  134. $this->assertTrue($this->storage->write('foo', 'bar'));
  135. if (phpversion('mongodb')) {
  136. $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
  137. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
  138. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
  139. $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000));
  140. } else {
  141. $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
  142. $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
  143. $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
  144. $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
  145. }
  146. }
  147. public function testWriteWhenUsingExpiresField()
  148. {
  149. $this->options = array(
  150. 'id_field' => '_id',
  151. 'data_field' => 'data',
  152. 'time_field' => 'time',
  153. 'database' => 'sf2-test',
  154. 'collection' => 'session-test',
  155. 'expiry_field' => 'expiresAt',
  156. );
  157. $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
  158. $collection = $this->createMongoCollectionMock();
  159. $this->mongo->expects($this->once())
  160. ->method('selectCollection')
  161. ->with($this->options['database'], $this->options['collection'])
  162. ->will($this->returnValue($collection));
  163. $data = array();
  164. $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
  165. $collection->expects($this->once())
  166. ->method($methodName)
  167. ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
  168. $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
  169. if (phpversion('mongodb')) {
  170. $this->assertEquals(array('upsert' => true), $options);
  171. } else {
  172. $this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
  173. }
  174. $data = $updateData['$set'];
  175. }));
  176. $this->assertTrue($this->storage->write('foo', 'bar'));
  177. if (phpversion('mongodb')) {
  178. $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
  179. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
  180. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
  181. } else {
  182. $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
  183. $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
  184. $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
  185. }
  186. }
  187. public function testReplaceSessionData()
  188. {
  189. $collection = $this->createMongoCollectionMock();
  190. $this->mongo->expects($this->once())
  191. ->method('selectCollection')
  192. ->with($this->options['database'], $this->options['collection'])
  193. ->will($this->returnValue($collection));
  194. $data = array();
  195. $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
  196. $collection->expects($this->exactly(2))
  197. ->method($methodName)
  198. ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
  199. $data = $updateData;
  200. }));
  201. $this->storage->write('foo', 'bar');
  202. $this->storage->write('foo', 'foobar');
  203. if (phpversion('mongodb')) {
  204. $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
  205. } else {
  206. $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
  207. }
  208. }
  209. public function testDestroy()
  210. {
  211. $collection = $this->createMongoCollectionMock();
  212. $this->mongo->expects($this->once())
  213. ->method('selectCollection')
  214. ->with($this->options['database'], $this->options['collection'])
  215. ->will($this->returnValue($collection));
  216. $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
  217. $collection->expects($this->once())
  218. ->method($methodName)
  219. ->with(array($this->options['id_field'] => 'foo'));
  220. $this->assertTrue($this->storage->destroy('foo'));
  221. }
  222. public function testGc()
  223. {
  224. $collection = $this->createMongoCollectionMock();
  225. $this->mongo->expects($this->once())
  226. ->method('selectCollection')
  227. ->with($this->options['database'], $this->options['collection'])
  228. ->will($this->returnValue($collection));
  229. $methodName = phpversion('mongodb') ? 'deleteMany' : 'remove';
  230. $collection->expects($this->once())
  231. ->method($methodName)
  232. ->will($this->returnCallback(function ($criteria) {
  233. if (phpversion('mongodb')) {
  234. $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
  235. $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000));
  236. } else {
  237. $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
  238. $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
  239. }
  240. }));
  241. $this->assertTrue($this->storage->gc(1));
  242. }
  243. public function testGetConnection()
  244. {
  245. $method = new \ReflectionMethod($this->storage, 'getMongo');
  246. $method->setAccessible(true);
  247. if (phpversion('mongodb')) {
  248. $mongoClass = 'MongoDB\Client';
  249. } else {
  250. $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
  251. }
  252. $this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
  253. }
  254. private function createMongoCollectionMock()
  255. {
  256. $collectionClass = 'MongoCollection';
  257. if (phpversion('mongodb')) {
  258. $collectionClass = 'MongoDB\Collection';
  259. }
  260. $collection = $this->getMockBuilder($collectionClass)
  261. ->disableOriginalConstructor()
  262. ->getMock();
  263. return $collection;
  264. }
  265. }