4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
18 * @author Markus Bachmann <markus.bachmann@bachi.biz>
19 * @group time-sensitive
21 class MongoDbSessionHandlerTest extends TestCase
24 * @var \PHPUnit_Framework_MockObject_MockObject
30 protected function setUp()
34 if (extension_loaded('mongodb')) {
35 if (!class_exists('MongoDB\Client')) {
36 $this->markTestSkipped('The mongodb/mongodb package is required.');
38 } elseif (!extension_loaded('mongo')) {
39 $this->markTestSkipped('The Mongo or MongoDB extension is required.');
42 if (phpversion('mongodb')) {
43 $mongoClass = 'MongoDB\Client';
45 $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
48 $this->mongo = $this->getMockBuilder($mongoClass)
49 ->disableOriginalConstructor()
52 $this->options = array(
54 'data_field' => 'data',
55 'time_field' => 'time',
56 'expiry_field' => 'expires_at',
57 'database' => 'sf2-test',
58 'collection' => 'session-test',
61 $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
65 * @expectedException \InvalidArgumentException
67 public function testConstructorShouldThrowExceptionForInvalidMongo()
69 new MongoDbSessionHandler(new \stdClass(), $this->options);
73 * @expectedException \InvalidArgumentException
75 public function testConstructorShouldThrowExceptionForMissingOptions()
77 new MongoDbSessionHandler($this->mongo, array());
80 public function testOpenMethodAlwaysReturnTrue()
82 $this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true');
85 public function testCloseMethodAlwaysReturnTrue()
87 $this->assertTrue($this->storage->close(), 'The "close" method should always return true');
90 public function testRead()
92 $collection = $this->createMongoCollectionMock();
94 $this->mongo->expects($this->once())
95 ->method('selectCollection')
96 ->with($this->options['database'], $this->options['collection'])
97 ->will($this->returnValue($collection));
101 // defining the timeout before the actual method call
102 // allows to test for "greater than" values in the $criteria
103 $testTimeout = time() + 1;
105 $collection->expects($this->once())
107 ->will($this->returnCallback(function ($criteria) use ($that, $testTimeout) {
108 $that->assertArrayHasKey($that->options['id_field'], $criteria);
109 $that->assertEquals($criteria[$that->options['id_field']], 'foo');
111 $that->assertArrayHasKey($that->options['expiry_field'], $criteria);
112 $that->assertArrayHasKey('$gte', $criteria[$that->options['expiry_field']]);
114 if (phpversion('mongodb')) {
115 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$gte']);
116 $that->assertGreaterThanOrEqual(round((string) $criteria[$that->options['expiry_field']]['$gte'] / 1000), $testTimeout);
118 $that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$gte']);
119 $that->assertGreaterThanOrEqual($criteria[$that->options['expiry_field']]['$gte']->sec, $testTimeout);
123 $that->options['id_field'] => 'foo',
126 if (phpversion('mongodb')) {
127 $fields[$that->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
128 $fields[$that->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
130 $fields[$that->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
131 $fields[$that->options['id_field']] = new \MongoDate();
137 $this->assertEquals('bar', $this->storage->read('foo'));
140 public function testWrite()
142 $collection = $this->createMongoCollectionMock();
144 $this->mongo->expects($this->once())
145 ->method('selectCollection')
146 ->with($this->options['database'], $this->options['collection'])
147 ->will($this->returnValue($collection));
152 $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
154 $collection->expects($this->once())
155 ->method($methodName)
156 ->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
157 $that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
159 if (phpversion('mongodb')) {
160 $that->assertEquals(array('upsert' => true), $options);
162 $that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
165 $data = $updateData['$set'];
168 $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
169 $this->assertTrue($this->storage->write('foo', 'bar'));
171 if (phpversion('mongodb')) {
172 $that->assertEquals('bar', $data[$that->options['data_field']]->getData());
173 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
174 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
175 $that->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$that->options['expiry_field']] / 1000));
177 $that->assertEquals('bar', $data[$that->options['data_field']]->bin);
178 $that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
179 $that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
180 $that->assertGreaterThanOrEqual($expectedExpiry, $data[$that->options['expiry_field']]->sec);
184 public function testWriteWhenUsingExpiresField()
186 $this->options = array(
188 'data_field' => 'data',
189 'time_field' => 'time',
190 'database' => 'sf2-test',
191 'collection' => 'session-test',
192 'expiry_field' => 'expiresAt',
195 $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
197 $collection = $this->createMongoCollectionMock();
199 $this->mongo->expects($this->once())
200 ->method('selectCollection')
201 ->with($this->options['database'], $this->options['collection'])
202 ->will($this->returnValue($collection));
207 $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
209 $collection->expects($this->once())
210 ->method($methodName)
211 ->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
212 $that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
214 if (phpversion('mongodb')) {
215 $that->assertEquals(array('upsert' => true), $options);
217 $that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
220 $data = $updateData['$set'];
223 $this->assertTrue($this->storage->write('foo', 'bar'));
225 if (phpversion('mongodb')) {
226 $that->assertEquals('bar', $data[$that->options['data_field']]->getData());
227 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
228 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
230 $that->assertEquals('bar', $data[$that->options['data_field']]->bin);
231 $that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
232 $that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
236 public function testReplaceSessionData()
238 $collection = $this->createMongoCollectionMock();
240 $this->mongo->expects($this->once())
241 ->method('selectCollection')
242 ->with($this->options['database'], $this->options['collection'])
243 ->will($this->returnValue($collection));
247 $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
249 $collection->expects($this->exactly(2))
250 ->method($methodName)
251 ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
255 $this->storage->write('foo', 'bar');
256 $this->storage->write('foo', 'foobar');
258 if (phpversion('mongodb')) {
259 $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
261 $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
265 public function testDestroy()
267 $collection = $this->createMongoCollectionMock();
269 $this->mongo->expects($this->once())
270 ->method('selectCollection')
271 ->with($this->options['database'], $this->options['collection'])
272 ->will($this->returnValue($collection));
274 $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
276 $collection->expects($this->once())
277 ->method($methodName)
278 ->with(array($this->options['id_field'] => 'foo'));
280 $this->assertTrue($this->storage->destroy('foo'));
283 public function testGc()
285 $collection = $this->createMongoCollectionMock();
287 $this->mongo->expects($this->once())
288 ->method('selectCollection')
289 ->with($this->options['database'], $this->options['collection'])
290 ->will($this->returnValue($collection));
294 $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
296 $collection->expects($this->once())
297 ->method($methodName)
298 ->will($this->returnCallback(function ($criteria) use ($that) {
299 if (phpversion('mongodb')) {
300 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$lt']);
301 $that->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$that->options['expiry_field']]['$lt'] / 1000));
303 $that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$lt']);
304 $that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['expiry_field']]['$lt']->sec);
308 $this->assertTrue($this->storage->gc(1));
311 public function testGetConnection()
313 $method = new \ReflectionMethod($this->storage, 'getMongo');
314 $method->setAccessible(true);
316 if (phpversion('mongodb')) {
317 $mongoClass = 'MongoDB\Client';
319 $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
322 $this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
325 private function createMongoCollectionMock()
327 $collectionClass = 'MongoCollection';
328 if (phpversion('mongodb')) {
329 $collectionClass = 'MongoDB\Collection';
332 $collection = $this->getMockBuilder($collectionClass)
333 ->disableOriginalConstructor()