Failed Conditions
Pull Request — master (#9)
by Chad
01:24
created

tests/MongoCacheTest.php (2 issues)

Severity

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']);
0 ignored issues
show
$json is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
254
        $headers = ['Content-Type' => ['application/json'], 'eTag' => ['"an etag"']];
0 ignored issues
show
$headers is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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
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