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('mongo') && !extension_loaded('mongodb')) {
35 $this->markTestSkipped('The Mongo or MongoDB extension is required.');
38 if (phpversion('mongodb')) {
39 $mongoClass = 'MongoDB\Client';
41 $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
44 $this->mongo = $this->getMockBuilder($mongoClass)
45 ->disableOriginalConstructor()
48 $this->options = array(
50 'data_field' => 'data',
51 'time_field' => 'time',
52 'expiry_field' => 'expires_at',
53 'database' => 'sf2-test',
54 'collection' => 'session-test',
57 $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
61 * @expectedException \InvalidArgumentException
63 public function testConstructorShouldThrowExceptionForInvalidMongo()
65 new MongoDbSessionHandler(new \stdClass(), $this->options);
69 * @expectedException \InvalidArgumentException
71 public function testConstructorShouldThrowExceptionForMissingOptions()
73 new MongoDbSessionHandler($this->mongo, array());
76 public function testOpenMethodAlwaysReturnTrue()
78 $this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true');
81 public function testCloseMethodAlwaysReturnTrue()
83 $this->assertTrue($this->storage->close(), 'The "close" method should always return true');
86 public function testRead()
88 $collection = $this->createMongoCollectionMock();
90 $this->mongo->expects($this->once())
91 ->method('selectCollection')
92 ->with($this->options['database'], $this->options['collection'])
93 ->will($this->returnValue($collection));
97 // defining the timeout before the actual method call
98 // allows to test for "greater than" values in the $criteria
99 $testTimeout = time() + 1;
101 $collection->expects($this->once())
103 ->will($this->returnCallback(function ($criteria) use ($that, $testTimeout) {
104 $that->assertArrayHasKey($that->options['id_field'], $criteria);
105 $that->assertEquals($criteria[$that->options['id_field']], 'foo');
107 $that->assertArrayHasKey($that->options['expiry_field'], $criteria);
108 $that->assertArrayHasKey('$gte', $criteria[$that->options['expiry_field']]);
110 if (phpversion('mongodb')) {
111 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$gte']);
112 $that->assertGreaterThanOrEqual(round(((int) $criteria[$that->options['expiry_field']]['$gte']) / 1000), $testTimeout);
114 $that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$gte']);
115 $that->assertGreaterThanOrEqual($criteria[$that->options['expiry_field']]['$gte']->sec, $testTimeout);
119 $that->options['id_field'] => 'foo',
122 if (phpversion('mongodb')) {
123 $fields[$that->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
124 $fields[$that->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
126 $fields[$that->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
127 $fields[$that->options['id_field']] = new \MongoDate();
133 $this->assertEquals('bar', $this->storage->read('foo'));
136 public function testWrite()
138 $collection = $this->createMongoCollectionMock();
140 $this->mongo->expects($this->once())
141 ->method('selectCollection')
142 ->with($this->options['database'], $this->options['collection'])
143 ->will($this->returnValue($collection));
148 $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
150 $collection->expects($this->once())
151 ->method($methodName)
152 ->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
153 $that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
155 if (phpversion('mongodb')) {
156 $that->assertEquals(array('upsert' => true), $options);
158 $that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
161 $data = $updateData['$set'];
164 $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
165 $this->assertTrue($this->storage->write('foo', 'bar'));
167 if (phpversion('mongodb')) {
168 $that->assertEquals('bar', $data[$that->options['data_field']]->getData());
169 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
170 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
171 $that->assertGreaterThanOrEqual($expectedExpiry, round(((int) $data[$that->options['expiry_field']]) / 1000));
173 $that->assertEquals('bar', $data[$that->options['data_field']]->bin);
174 $that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
175 $that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
176 $that->assertGreaterThanOrEqual($expectedExpiry, $data[$that->options['expiry_field']]->sec);
180 public function testWriteWhenUsingExpiresField()
182 $this->options = array(
184 'data_field' => 'data',
185 'time_field' => 'time',
186 'database' => 'sf2-test',
187 'collection' => 'session-test',
188 'expiry_field' => 'expiresAt',
191 $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
193 $collection = $this->createMongoCollectionMock();
195 $this->mongo->expects($this->once())
196 ->method('selectCollection')
197 ->with($this->options['database'], $this->options['collection'])
198 ->will($this->returnValue($collection));
203 $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
205 $collection->expects($this->once())
206 ->method($methodName)
207 ->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
208 $that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
210 if (phpversion('mongodb')) {
211 $that->assertEquals(array('upsert' => true), $options);
213 $that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
216 $data = $updateData['$set'];
219 $this->assertTrue($this->storage->write('foo', 'bar'));
221 if (phpversion('mongodb')) {
222 $that->assertEquals('bar', $data[$that->options['data_field']]->getData());
223 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
224 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
226 $that->assertEquals('bar', $data[$that->options['data_field']]->bin);
227 $that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
228 $that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
232 public function testReplaceSessionData()
234 $collection = $this->createMongoCollectionMock();
236 $this->mongo->expects($this->once())
237 ->method('selectCollection')
238 ->with($this->options['database'], $this->options['collection'])
239 ->will($this->returnValue($collection));
243 $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
245 $collection->expects($this->exactly(2))
246 ->method($methodName)
247 ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
251 $this->storage->write('foo', 'bar');
252 $this->storage->write('foo', 'foobar');
254 if (phpversion('mongodb')) {
255 $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
257 $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
261 public function testDestroy()
263 $collection = $this->createMongoCollectionMock();
265 $this->mongo->expects($this->once())
266 ->method('selectCollection')
267 ->with($this->options['database'], $this->options['collection'])
268 ->will($this->returnValue($collection));
270 $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
272 $collection->expects($this->once())
273 ->method($methodName)
274 ->with(array($this->options['id_field'] => 'foo'));
276 $this->assertTrue($this->storage->destroy('foo'));
279 public function testGc()
281 $collection = $this->createMongoCollectionMock();
283 $this->mongo->expects($this->once())
284 ->method('selectCollection')
285 ->with($this->options['database'], $this->options['collection'])
286 ->will($this->returnValue($collection));
290 $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
292 $collection->expects($this->once())
293 ->method($methodName)
294 ->will($this->returnCallback(function ($criteria) use ($that) {
295 if (phpversion('mongodb')) {
296 $that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$lt']);
297 $that->assertGreaterThanOrEqual(time() - 1, round(((int) $criteria[$that->options['expiry_field']]['$lt']) / 1000));
299 $that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$lt']);
300 $that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['expiry_field']]['$lt']->sec);
304 $this->assertTrue($this->storage->gc(1));
307 public function testGetConnection()
309 $method = new \ReflectionMethod($this->storage, 'getMongo');
310 $method->setAccessible(true);
312 if (phpversion('mongodb')) {
313 $mongoClass = 'MongoDB\Client';
315 $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
318 $this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
321 private function createMongoCollectionMock()
323 $collectionClass = 'MongoCollection';
324 if (phpversion('mongodb')) {
325 $collectionClass = 'MongoDB\Collection';
328 $collection = $this->getMockBuilder($collectionClass)
329 ->disableOriginalConstructor()