Failed Conditions
Pull Request — master (#9)
by Chad
02:39
created

tests/MongoCacheTest.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace SubjectivePHPTest\Psr\SimpleCache;
3
4
use DateTime;
5
use DateTimeZone;
6
use MongoDB\BSON\UTCDateTime;
7
use MongoDB\Client;
8
use SubjectivePHP\Psr\SimpleCache\InvalidArgumentException;
9
use SubjectivePHP\Psr\SimpleCache\MongoCache;
10
use SubjectivePHP\Psr\SimpleCache\Serializer\SerializerInterface;
11
12
/**
13
 * Defines unit tests for the MongoCache class.
14
 *
15
 * @coversDefaultClass \SubjectivePHP\Psr\SimpleCache\MongoCache
16
 * @covers ::__construct
17
 * @covers ::<private>
18
 * @covers ::<protected>
19
 */
20
final class MongoCacheTest extends \PHPUnit\Framework\TestCase
21
{
22
    /**
23
     * Mongo Collection to use in tests.
24
     *
25
     * @var MongoDB\Collection
26
     */
27
    private $collection;
28
29
    /**
30
     * Cache instance to us in tests.
31
     *
32
     * @var MongoCache
33
     */
34
    private $cache;
35
36
    /**
37
     * set up each test.
38
     *
39
     * @return void
40
     */
41
    public function setUp()
42
    {
43
        $this->collection = (new Client())->selectDatabase('testing')->selectCollection('cache');
44
        $this->collection->drop();
45
        $this->cache = new MongoCache($this->collection, $this->getSerializer());
46
    }
47
48
    /**
49
     * Verify behavior of get() when the key is not found.
50
     *
51
     * @test
52
     * @covers ::get
53
     *
54
     * @return void
55
     */
56
    public function getNotFound()
57
    {
58
        $default = new \StdClass();
59
        $this->assertSame($default, $this->cache->get('key', $default));
60
    }
61
62
    /**
63
     * Verify basic behavior of get().
64
     *
65
     * @test
66
     * @covers ::get
67
     *
68
     * @return void
69
     */
70
    public function get()
71
    {
72
        $json = json_encode(['status' => 'ok']);
73
        $headers = ['Content-Type' => ['application/json'], 'eTag' => ['"an etag"']];
74
        $this->collection->insertOne(
75
            [
76
                '_id' => 'key',
77
                'timestamp' => 1491782286,
78
                'timezone' => 'America/New_York',
79
            ]
80
        );
81
82
        $dateTime = new DateTime('@1491782286', new DateTimeZone('America/New_York'));
83
        $this->assertEquals($dateTime, $this->cache->get('key'));
84
    }
85
86
    /**
87
     * Verify basic behavior of set().
88
     *
89
     * @test
90
     * @covers ::set
91
     *
92
     * @return void
93
     */
94
    public function set()
95
    {
96
        $ttl = \DateInterval::createFromDateString('1 day');
97
        $dateTime = new DateTime('2017-04-09 20:54:04', new DateTimeZone('Pacific/Honolulu'));
98
        $this->cache->set('key', $dateTime, $ttl);
99
        $expires = new UTCDateTime((new \DateTime('now'))->add($ttl)->getTimestamp() * 1000);
100
        $this->assertDateTimeDocument('key', $expires, $dateTime);
101
    }
102
103
    /**
104
     * Verify behavior of set() with invalid $ttl value.
105
     *
106
     * @test
107
     * @covers ::set
108
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
109
     * @expectedExceptionMessage $ttl must be null, an integer or \DateInterval instance
110
     *
111
     * @return void
112
     */
113
    public function setInvalidTTL()
114
    {
115
        $this->cache->set('key', new DateTime(), new DateTime());
116
    }
117
118
    /**
119
     * Verify behavior of set() with empty $key.
120
     *
121
     * @test
122
     * @covers ::set
123
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
124
     * @expectedExceptionMessage $key must be a valid non empty string
125
     *
126
     * @return void
127
     */
128
    public function setEmptyKey()
129
    {
130
        $this->cache->set('', new DateTime());
131
    }
132
133
    /**
134
     * Verify behavior of set() with non string $key.
135
     *
136
     * @test
137
     * @covers ::set
138
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
139
     * @expectedExceptionMessage $key must be a valid non empty string
140
     *
141
     * @return void
142
     */
143
    public function setNonStringKey()
144
    {
145
        $this->cache->set(new \StdClass(), new DateTime());
146
    }
147
148
    /**
149
     * Verify behavior of set() with string $key containing reserved characters.
150
     *
151
     * @test
152
     * @covers ::set
153
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
154
     * @expectedExceptionMessage Key 'key with {, ) & @' contains unsupported characters
155
     *
156
     * @return void
157
     */
158
    public function setKeyContainsReservedCharacters()
159
    {
160
        $this->cache->set('key with {, ) & @', new DateTime());
161
    }
162
163
    /**
164
     * Verify basic behavior of delete().
165
     *
166
     * @test
167
     * @covers ::delete
168
     *
169
     * @return void
170
     */
171
    public function delete()
172
    {
173
        $this->collection->insertOne(['_id' => 'key1']);
174
        $this->collection->insertOne(['_id' => 'key2']);
175
176
        $this->assertTrue($this->cache->delete('key1'));
177
178
        $actual = $this->collection->find(
179
            [],
180
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
181
        )->toArray();
182
183
        $this->assertEquals([['_id' => 'key2']], $actual);
184
    }
185
186
    /**
187
     * Verify behavior of delete() when mongo exception is thrown.
188
     *
189
     * @test
190
     * @covers ::delete
191
     *
192
     * @return void
193
     */
194 View Code Duplication
    public function deleteMongoException()
195
    {
196
        $mockCollection = $this->getMockBuilder(
197
            '\\MongoDB\\Collection',
198
            ['deleteOne', 'createIndex']
199
        )->disableOriginalConstructor()->getMock();
200
        $mockCollection->method('deleteOne')->will($this->throwException(new \Exception()));
201
        $cache = new MongoCache($mockCollection, $this->getSerializer());
202
        $this->assertFalse($cache->delete('key'));
203
    }
204
205
    /**
206
     * Verify basic behavior of clear().
207
     *
208
     * @test
209
     * @covers ::clear
210
     *
211
     * @return void
212
     */
213
    public function clear()
214
    {
215
        $this->collection->insertOne(['_id' => 'key1']);
216
        $this->collection->insertOne(['_id' => 'key2']);
217
218
        $this->assertTrue($this->cache->clear());
219
220
        $actual = $this->collection->find(
221
            [],
222
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
223
        )->toArray();
224
225
        $this->assertSame([], $actual);
226
    }
227
228
    /**
229
     * Verify behavior of clear() when mongo exception is thrown.
230
     *
231
     * @test
232
     * @covers ::clear
233
     *
234
     * @return void
235
     */
236 View Code Duplication
    public function clearMongoException()
237
    {
238
        $mockCollection = $this->getMockBuilder(
239
            '\\MongoDB\\Collection',
240
            ['deleteMany', 'createIndex']
241
        )->disableOriginalConstructor()->getMock();
242
        $mockCollection->method('deleteMany')->will($this->throwException(new \Exception()));
243
        $cache = new MongoCache($mockCollection, $this->getSerializer());
244
        $this->assertFalse($cache->clear());
245
    }
246
247
    /**
248
     * Verify basic behavior of getMultiple
249
     *
250
     * @test
251
     * @covers ::getMultiple
252
     *
253
     * @return void
254
     */
255
    public function getMultiple()
256
    {
257
        $json = json_encode(['status' => 'ok']);
258
        $headers = ['Content-Type' => ['application/json'], 'eTag' => ['"an etag"']];
259
        $this->collection->insertOne(
260
            [
261
                '_id' => 'key1',
262
                'timestamp' => 1491782286,
263
                'timezone' => 'America/New_York',
264
                'expires' => new UTCDateTime(strtotime('+1 day') * 1000),
265
            ]
266
        );
267
        $this->collection->insertOne(
268
            [
269
                '_id' => 'key3',
270
                'timestamp' => 1491807244,
271
                'timezone' => 'Pacific/Honolulu',
272
                'expires' => new UTCDateTime(strtotime('+1 day') * 1000),
273
            ]
274
        );
275
276
        $default = new \StdClass();
277
278
        $dates = $this->cache->getMultiple(['key1', 'key2', 'key3', 'key4'], $default);
279
280
        $this->assertCount(4, $dates);
281
282
        $this->assertSame('2017-04-09 23:58:06', $dates['key1']->format('Y-m-d H:i:s'));
283
        $this->assertSame($default, $dates['key2']);
284
        $this->assertSame('2017-04-10 06:54:04', $dates['key3']->format('Y-m-d H:i:s'));
285
        $this->assertSame($default, $dates['key4']);
286
    }
287
288
    /**
289
     * Verify basic behavior of setMultiple().
290
     *
291
     * @test
292
     * @covers ::setMultiple
293
     *
294
     * @return void
295
     */
296
    public function setMultple()
297
    {
298
        $dates = [
299
            'key1' => new DateTime(),
300
            'key2' => new DateTime(),
301
        ];
302
303
        $this->assertTrue($this->cache->setMultiple($dates, 86400));
304
        $expires = new UTCDateTime((time() + 86400) * 1000);
305
        $this->assertDateTimeDocument('key1', $expires, $dates['key1']);
306
        $this->assertDateTimeDocument('key2', $expires, $dates['key2']);
307
    }
308
309
    /**
310
     * Verify behavior of setMultiple() when mongo throws an exception.
311
     *
312
     * @test
313
     * @covers ::setMultiple
314
     *
315
     * @return void
316
     */
317
    public function setMultpleMongoException()
318
    {
319
        $mockCollection = $this->getMockBuilder(
320
            '\\MongoDB\\Collection',
321
            ['updateOne', 'createIndex']
322
        )->disableOriginalConstructor()->getMock();
323
        $mockCollection->method('updateOne')->will($this->throwException(new \Exception()));
324
        $cache = new MongoCache($mockCollection, $this->getSerializer());
325
        $responses = ['key1' => new DateTime(), 'key2' => new DateTime()];
326
        $this->assertFalse($cache->setMultiple($responses, 86400));
327
    }
328
329
    /**
330
     * Verify basic behavior of deleteMultiple().
331
     *
332
     * @test
333
     * @covers ::deleteMultiple
334
     *
335
     * @return void
336
     */
337
    public function deleteMultiple()
338
    {
339
        $this->collection->insertOne(['_id' => 'key1']);
340
        $this->collection->insertOne(['_id' => 'key2']);
341
        $this->collection->insertOne(['_id' => 'key3']);
342
343
        $this->assertTrue($this->cache->deleteMultiple(['key1', 'key3']));
344
345
        $actual = $this->collection->find(
346
            [],
347
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
348
        )->toArray();
349
350
        $this->assertEquals([['_id' => 'key2']], $actual);
351
    }
352
353
    /**
354
     * Verify behavior of deleteMultiple() when mongo throws an exception.
355
     *
356
     * @test
357
     * @covers ::deleteMultiple
358
     *
359
     * @return void
360
     */
361 View Code Duplication
    public function deleteMultipleMongoException()
362
    {
363
        $mockCollection = $this->getMockBuilder(
364
            '\\MongoDB\\Collection',
365
            ['deleteMany', 'createIndex']
366
        )->disableOriginalConstructor()->getMock();
367
        $mockCollection->method('deleteMany')->will($this->throwException(new \Exception()));
368
        $cache = new MongoCache($mockCollection, $this->getSerializer());
369
        $this->assertFalse($cache->deleteMultiple(['key1', 'key3']));
370
    }
371
372
    /**
373
     * Verify basic behavior of has().
374
     *
375
     * @test
376
     * @covers ::has
377
     *
378
     * @return void
379
     */
380
    public function has()
381
    {
382
        $this->collection->insertOne(['_id' => 'key1']);
383
        $this->assertTrue($this->cache->has('key1'));
384
        $this->assertFalse($this->cache->has('key2'));
385
    }
386
387
    /**
388
     * Helper method to assert the contents of a mongo document.
389
     *
390
     * @param string      $key      The _id value to assert.
391
     * @param UTCDateTime $expires  The expected expires value.
392
     * @param DateTime    $expected The expected DateTime value.
393
     *
394
     * @return void
395
     */
396
    private function assertDateTimeDocument(string $key, UTCDateTime $expires, DateTime $expected)
397
    {
398
        $actual = $this->collection->findOne(
399
            ['_id' => $key],
400
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
401
        );
402
403
        $this->assertSame($expires->toDateTime()->getTimestamp(), $actual['expires']->toDateTime()->getTimestamp());
404
405
        $this->assertSame(
406
            [
407
                '_id' => $key,
408
                'expires' => $actual['expires'],
409
                'timestamp' => $expected->getTimestamp(),
410
                'timezone' => $expected->getTimeZone()->getName(),
411
            ],
412
            $actual
413
        );
414
    }
415
416
    /**
417
     * Helper method to get a SerializerInterface instance.
418
     *
419
     * @return SerializerInterface
420
     */
421
    private function getSerializer() : SerializerInterface
422
    {
423
        return new class implements SerializerInterface
0 ignored issues
show
This class is not in CamelCase format.

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
424
        {
425
            /**
426
             * @see SerializerInterface::unserialize().
427
             *
428
             * @param mixed $data The serialized data.
429
             *
430
             * @return DateTime
431
             */
432
            public function unserialize($data)
433
            {
434
                return new DateTime("@{$data['timestamp']}", timezone_open($data['timezone']));
435
            }
436
437
            /**
438
             * @see SerializerInterface::serialize().
439
             *
440
             * @param mixed $value The data to serialize.
441
             *
442
             * @return array
443
             */
444
            public function serialize($value)
445
            {
446
                return [
447
                    'timestamp' => $value->getTimestamp(),
448
                    'timezone' => $value->getTimezone()->getName(),
449
                ];
450
            }
451
        };
452
    }
453
}
454