Failed Conditions
Pull Request — master (#9)
by Chad
01:24
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
        $this->collection->insertOne(
73
            [
74
                '_id' => 'key',
75
                'timestamp' => 1491782286,
76
                'timezone' => 'America/New_York',
77
            ]
78
        );
79
80
        $dateTime = new DateTime('@1491782286', new DateTimeZone('America/New_York'));
81
        $this->assertEquals($dateTime, $this->cache->get('key'));
82
    }
83
84
    /**
85
     * Verify basic behavior of set().
86
     *
87
     * @test
88
     * @covers ::set
89
     *
90
     * @return void
91
     */
92
    public function set()
93
    {
94
        $ttl = \DateInterval::createFromDateString('1 day');
95
        $dateTime = new DateTime('2017-04-09 20:54:04', new DateTimeZone('Pacific/Honolulu'));
96
        $this->cache->set('key', $dateTime, $ttl);
97
        $expires = new UTCDateTime((new \DateTime('now'))->add($ttl)->getTimestamp() * 1000);
98
        $this->assertDateTimeDocument('key', $expires, $dateTime);
99
    }
100
101
    /**
102
     * Verify behavior of set() with invalid $ttl value.
103
     *
104
     * @test
105
     * @covers ::set
106
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
107
     * @expectedExceptionMessage $ttl must be null, an integer or \DateInterval instance
108
     *
109
     * @return void
110
     */
111
    public function setInvalidTTL()
112
    {
113
        $this->cache->set('key', new DateTime(), new DateTime());
114
    }
115
116
    /**
117
     * Verify behavior of set() with empty $key.
118
     *
119
     * @test
120
     * @covers ::set
121
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
122
     * @expectedExceptionMessage $key must be a valid non empty string
123
     *
124
     * @return void
125
     */
126
    public function setEmptyKey()
127
    {
128
        $this->cache->set('', new DateTime());
129
    }
130
131
    /**
132
     * Verify behavior of set() with non string $key.
133
     *
134
     * @test
135
     * @covers ::set
136
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
137
     * @expectedExceptionMessage $key must be a valid non empty string
138
     *
139
     * @return void
140
     */
141
    public function setNonStringKey()
142
    {
143
        $this->cache->set(new \StdClass(), new DateTime());
144
    }
145
146
    /**
147
     * Verify behavior of set() with string $key containing reserved characters.
148
     *
149
     * @test
150
     * @covers ::set
151
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
152
     * @expectedExceptionMessage Key 'key with {, ) & @' contains unsupported characters
153
     *
154
     * @return void
155
     */
156
    public function setKeyContainsReservedCharacters()
157
    {
158
        $this->cache->set('key with {, ) & @', new DateTime());
159
    }
160
161
    /**
162
     * Verify basic behavior of delete().
163
     *
164
     * @test
165
     * @covers ::delete
166
     *
167
     * @return void
168
     */
169
    public function delete()
170
    {
171
        $this->collection->insertOne(['_id' => 'key1']);
172
        $this->collection->insertOne(['_id' => 'key2']);
173
174
        $this->assertTrue($this->cache->delete('key1'));
175
176
        $actual = $this->collection->find(
177
            [],
178
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
179
        )->toArray();
180
181
        $this->assertEquals([['_id' => 'key2']], $actual);
182
    }
183
184
    /**
185
     * Verify behavior of delete() when mongo exception is thrown.
186
     *
187
     * @test
188
     * @covers ::delete
189
     *
190
     * @return void
191
     */
192 View Code Duplication
    public function deleteMongoException()
193
    {
194
        $mockCollection = $this->getMockBuilder(
195
            '\\MongoDB\\Collection'
196
        )->disableOriginalConstructor()->getMock();
197
        $mockCollection->method('deleteOne')->will($this->throwException(new \Exception()));
198
        $cache = new MongoCache($mockCollection, $this->getSerializer());
199
        $this->assertFalse($cache->delete('key'));
200
    }
201
202
    /**
203
     * Verify basic behavior of clear().
204
     *
205
     * @test
206
     * @covers ::clear
207
     *
208
     * @return void
209
     */
210
    public function clear()
211
    {
212
        $this->collection->insertOne(['_id' => 'key1']);
213
        $this->collection->insertOne(['_id' => 'key2']);
214
215
        $this->assertTrue($this->cache->clear());
216
217
        $actual = $this->collection->find(
218
            [],
219
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
220
        )->toArray();
221
222
        $this->assertSame([], $actual);
223
    }
224
225
    /**
226
     * Verify behavior of clear() when mongo exception is thrown.
227
     *
228
     * @test
229
     * @covers ::clear
230
     *
231
     * @return void
232
     */
233 View Code Duplication
    public function clearMongoException()
234
    {
235
        $mockCollection = $this->getMockBuilder(
236
            '\\MongoDB\\Collection'
237
        )->disableOriginalConstructor()->getMock();
238
        $mockCollection->method('deleteMany')->will($this->throwException(new \Exception()));
239
        $cache = new MongoCache($mockCollection, $this->getSerializer());
240
        $this->assertFalse($cache->clear());
241
    }
242
243
    /**
244
     * Verify basic behavior of getMultiple
245
     *
246
     * @test
247
     * @covers ::getMultiple
248
     *
249
     * @return void
250
     */
251
    public function getMultiple()
252
    {
253
        $json = json_encode(['status' => 'ok']);
254
        $headers = ['Content-Type' => ['application/json'], 'eTag' => ['"an etag"']];
255
        $this->collection->insertOne(
256
            [
257
                '_id' => 'key1',
258
                'timestamp' => 1491782286,
259
                'timezone' => 'America/New_York',
260
                'expires' => new UTCDateTime(strtotime('+1 day') * 1000),
261
            ]
262
        );
263
        $this->collection->insertOne(
264
            [
265
                '_id' => 'key3',
266
                'timestamp' => 1491807244,
267
                'timezone' => 'Pacific/Honolulu',
268
                'expires' => new UTCDateTime(strtotime('+1 day') * 1000),
269
            ]
270
        );
271
272
        $default = new \StdClass();
273
274
        $dates = $this->cache->getMultiple(['key1', 'key2', 'key3', 'key4'], $default);
275
276
        $this->assertCount(4, $dates);
277
278
        $this->assertSame('2017-04-09 23:58:06', $dates['key1']->format('Y-m-d H:i:s'));
279
        $this->assertSame($default, $dates['key2']);
280
        $this->assertSame('2017-04-10 06:54:04', $dates['key3']->format('Y-m-d H:i:s'));
281
        $this->assertSame($default, $dates['key4']);
282
    }
283
284
    /**
285
     * Verify basic behavior of setMultiple().
286
     *
287
     * @test
288
     * @covers ::setMultiple
289
     *
290
     * @return void
291
     */
292
    public function setMultple()
293
    {
294
        $dates = [
295
            'key1' => new DateTime(),
296
            'key2' => new DateTime(),
297
        ];
298
299
        $this->assertTrue($this->cache->setMultiple($dates, 86400));
300
        $expires = new UTCDateTime((time() + 86400) * 1000);
301
        $this->assertDateTimeDocument('key1', $expires, $dates['key1']);
302
        $this->assertDateTimeDocument('key2', $expires, $dates['key2']);
303
    }
304
305
    /**
306
     * Verify behavior of setMultiple() when mongo throws an exception.
307
     *
308
     * @test
309
     * @covers ::setMultiple
310
     *
311
     * @return void
312
     */
313
    public function setMultpleMongoException()
314
    {
315
        $mockCollection = $this->getMockBuilder(
316
            '\\MongoDB\\Collection'
317
        )->disableOriginalConstructor()->getMock();
318
        $mockCollection->method('updateOne')->will($this->throwException(new \Exception()));
319
        $cache = new MongoCache($mockCollection, $this->getSerializer());
320
        $responses = ['key1' => new DateTime(), 'key2' => new DateTime()];
321
        $this->assertFalse($cache->setMultiple($responses, 86400));
322
    }
323
324
    /**
325
     * Verify basic behavior of deleteMultiple().
326
     *
327
     * @test
328
     * @covers ::deleteMultiple
329
     *
330
     * @return void
331
     */
332
    public function deleteMultiple()
333
    {
334
        $this->collection->insertOne(['_id' => 'key1']);
335
        $this->collection->insertOne(['_id' => 'key2']);
336
        $this->collection->insertOne(['_id' => 'key3']);
337
338
        $this->assertTrue($this->cache->deleteMultiple(['key1', 'key3']));
339
340
        $actual = $this->collection->find(
341
            [],
342
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
343
        )->toArray();
344
345
        $this->assertEquals([['_id' => 'key2']], $actual);
346
    }
347
348
    /**
349
     * Verify behavior of deleteMultiple() when mongo throws an exception.
350
     *
351
     * @test
352
     * @covers ::deleteMultiple
353
     *
354
     * @return void
355
     */
356 View Code Duplication
    public function deleteMultipleMongoException()
357
    {
358
        $mockCollection = $this->getMockBuilder(
359
            '\\MongoDB\\Collection'
360
        )->disableOriginalConstructor()->getMock();
361
        $mockCollection->method('deleteMany')->will($this->throwException(new \Exception()));
362
        $cache = new MongoCache($mockCollection, $this->getSerializer());
363
        $this->assertFalse($cache->deleteMultiple(['key1', 'key3']));
364
    }
365
366
    /**
367
     * Verify basic behavior of has().
368
     *
369
     * @test
370
     * @covers ::has
371
     *
372
     * @return void
373
     */
374
    public function has()
375
    {
376
        $this->collection->insertOne(['_id' => 'key1']);
377
        $this->assertTrue($this->cache->has('key1'));
378
        $this->assertFalse($this->cache->has('key2'));
379
    }
380
381
    /**
382
     * Helper method to assert the contents of a mongo document.
383
     *
384
     * @param string      $key      The _id value to assert.
385
     * @param UTCDateTime $expires  The expected expires value.
386
     * @param DateTime    $expected The expected DateTime value.
387
     *
388
     * @return void
389
     */
390
    private function assertDateTimeDocument(string $key, UTCDateTime $expires, DateTime $expected)
391
    {
392
        $actual = $this->collection->findOne(
393
            ['_id' => $key],
394
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
395
        );
396
397
        $this->assertSame($expires->toDateTime()->getTimestamp(), $actual['expires']->toDateTime()->getTimestamp());
398
399
        $this->assertSame(
400
            [
401
                '_id' => $key,
402
                'expires' => $actual['expires'],
403
                'timestamp' => $expected->getTimestamp(),
404
                'timezone' => $expected->getTimeZone()->getName(),
405
            ],
406
            $actual
407
        );
408
    }
409
410
    /**
411
     * Helper method to get a SerializerInterface instance.
412
     *
413
     * @return SerializerInterface
414
     */
415
    private function getSerializer() : SerializerInterface
416
    {
417
        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...
418
        {
419
            /**
420
             * @see SerializerInterface::unserialize().
421
             *
422
             * @param mixed $data The serialized data.
423
             *
424
             * @return DateTime
425
             */
426
            public function unserialize($data)
427
            {
428
                return new DateTime("@{$data['timestamp']}", timezone_open($data['timezone']));
429
            }
430
431
            /**
432
             * @see SerializerInterface::serialize().
433
             *
434
             * @param mixed $value The data to serialize.
435
             *
436
             * @return array
437
             */
438
            public function serialize($value)
439
            {
440
                return [
441
                    'timestamp' => $value->getTimestamp(),
442
                    'timezone' => $value->getTimezone()->getName(),
443
                ];
444
            }
445
        };
446
    }
447
}
448