Completed
Push — master ( a59e29...dc1e78 )
by Chad
9s
created

MongoCacheTest::useWithoutSerializer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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 MongoDB\Collection;
9
use SubjectivePHP\Psr\SimpleCache\InvalidArgumentException;
10
use SubjectivePHP\Psr\SimpleCache\MongoCache;
11
use SubjectivePHP\Psr\SimpleCache\Serializer\SerializerInterface;
12
13
/**
14
 * Defines unit tests for the MongoCache class.
15
 *
16
 * @coversDefaultClass \SubjectivePHP\Psr\SimpleCache\MongoCache
17
 * @covers ::__construct
18
 * @covers ::<private>
19
 * @covers ::<protected>
20
 */
21
final class MongoCacheTest extends \PHPUnit\Framework\TestCase
22
{
23
    /**
24
     * Mongo Collection to use in tests.
25
     *
26
     * @var Collection
27
     */
28
    private $collection;
29
30
    /**
31
     * Cache instance to us in tests.
32
     *
33
     * @var MongoCache
34
     */
35
    private $cache;
36
37
    /**
38
     * set up each test.
39
     *
40
     * @return void
41
     */
42
    public function setUp()
43
    {
44
        $this->collection = (new Client())->selectDatabase('testing')->selectCollection('cache');
45
        $this->collection->drop();
46
        $this->cache = new MongoCache($this->collection, $this->getSerializer());
47
    }
48
49
    /**
50
     * @test
51
     *
52
     * @return void
53
     */
54
    public function useWithoutSerializer()
55
    {
56
        $cache = new MongoCache($this->collection);
57
        $data = ['a', 'b', 'c'];
58
        $cache->set('foo', $data);
59
        $this->assertSame($data, $cache->get('foo'));
60
    }
61
62
    /**
63
     * Verify behavior of get() when the key is not found.
64
     *
65
     * @test
66
     * @covers ::get
67
     *
68
     * @return void
69
     */
70
    public function getNotFound()
71
    {
72
        $default = new \StdClass();
73
        $this->assertSame($default, $this->cache->get('key', $default));
74
    }
75
76
    /**
77
     * Verify basic behavior of get().
78
     *
79
     * @test
80
     * @covers ::get
81
     *
82
     * @return void
83
     */
84
    public function get()
85
    {
86
        $this->collection->insertOne(
87
            [
88
                '_id' => 'key',
89
                'data' => [
90
                    'timestamp' => 1491782286,
91
                    'timezone' => 'America/New_York',
92
                ],
93
            ]
94
        );
95
96
        $dateTime = new DateTime('@1491782286', new DateTimeZone('America/New_York'));
97
        $this->assertEquals($dateTime, $this->cache->get('key'));
98
    }
99
100
    /**
101
     * Verify basic behavior of set().
102
     *
103
     * @test
104
     * @covers ::set
105
     *
106
     * @return void
107
     */
108
    public function set()
109
    {
110
        $ttl = \DateInterval::createFromDateString('1 day');
111
        $dateTime = new DateTime('2017-04-09 20:54:04', new DateTimeZone('Pacific/Honolulu'));
112
        $this->cache->set('key', $dateTime, $ttl);
113
        $expires = new UTCDateTime((new \DateTime('now'))->add($ttl)->getTimestamp() * 1000);
114
        $this->assertDateTimeDocument('key', $expires, $dateTime);
115
    }
116
117
    /**
118
     * Verify behavior of set() with invalid $ttl value.
119
     *
120
     * @test
121
     * @covers ::set
122
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
123
     * @expectedExceptionMessage $ttl must be null, an integer or \DateInterval instance
124
     *
125
     * @return void
126
     */
127
    public function setInvalidTTL()
128
    {
129
        $this->cache->set('key', new DateTime(), new DateTime());
0 ignored issues
show
Documentation introduced by
new \DateTime() is of type object<DateTime>, but the function expects a null|integer|object<DateInterval>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
130
    }
131
132
    /**
133
     * Verify behavior of set() with empty $key.
134
     *
135
     * @test
136
     * @covers ::set
137
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
138
     * @expectedExceptionMessage $key must be a valid non empty string
139
     *
140
     * @return void
141
     */
142
    public function setEmptyKey()
143
    {
144
        $this->cache->set('', new DateTime());
145
    }
146
147
    /**
148
     * Verify behavior of set() with non string $key.
149
     *
150
     * @test
151
     * @covers ::set
152
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
153
     * @expectedExceptionMessage $key must be a valid non empty string
154
     *
155
     * @return void
156
     */
157
    public function setNonStringKey()
158
    {
159
        $this->cache->set(new \StdClass(), new DateTime());
0 ignored issues
show
Documentation introduced by
new \StdClass() is of type object<stdClass>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
160
    }
161
162
    /**
163
     * Verify behavior of set() with string $key containing reserved characters.
164
     *
165
     * @test
166
     * @covers ::set
167
     * @expectedException \Psr\SimpleCache\InvalidArgumentException
168
     * @expectedExceptionMessage Key 'key with {, ) & @' contains unsupported characters
169
     *
170
     * @return void
171
     */
172
    public function setKeyContainsReservedCharacters()
173
    {
174
        $this->cache->set('key with {, ) & @', new DateTime());
175
    }
176
177
    /**
178
     * Verify basic behavior of delete().
179
     *
180
     * @test
181
     * @covers ::delete
182
     *
183
     * @return void
184
     */
185
    public function delete()
186
    {
187
        $this->collection->insertOne(['_id' => 'key1']);
188
        $this->collection->insertOne(['_id' => 'key2']);
189
190
        $this->assertTrue($this->cache->delete('key1'));
191
192
        $actual = $this->collection->find(
193
            [],
194
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
195
        )->toArray();
196
197
        $this->assertEquals([['_id' => 'key2']], $actual);
198
    }
199
200
    /**
201
     * Verify behavior of delete() when mongo exception is thrown.
202
     *
203
     * @test
204
     * @covers ::delete
205
     *
206
     * @return void
207
     */
208
    public function deleteMongoException()
209
    {
210
        $mockCollection = $this->getFailingCollectionMock('deleteOne');
211
        $cache = new MongoCache($mockCollection, $this->getSerializer());
212
        $this->assertFalse($cache->delete('key'));
213
    }
214
215
    /**
216
     * Verify basic behavior of clear().
217
     *
218
     * @test
219
     * @covers ::clear
220
     *
221
     * @return void
222
     */
223
    public function clear()
224
    {
225
        $this->collection->insertOne(['_id' => 'key1']);
226
        $this->collection->insertOne(['_id' => 'key2']);
227
228
        $this->assertTrue($this->cache->clear());
229
230
        $actual = $this->collection->find(
231
            [],
232
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
233
        )->toArray();
234
235
        $this->assertSame([], $actual);
236
    }
237
238
    /**
239
     * Verify behavior of clear() when mongo exception is thrown.
240
     *
241
     * @test
242
     * @covers ::clear
243
     *
244
     * @return void
245
     */
246
    public function clearMongoException()
247
    {
248
        $mockCollection = $this->getFailingCollectionMock('deleteMany');
249
        $cache = new MongoCache($mockCollection, $this->getSerializer());
250
        $this->assertFalse($cache->clear());
251
    }
252
253
    /**
254
     * Verify basic behavior of getMultiple
255
     *
256
     * @test
257
     * @covers ::getMultiple
258
     *
259
     * @return void
260
     */
261
    public function getMultiple()
262
    {
263
        $this->collection->insertOne(
264
            [
265
                '_id' => 'key1',
266
                'data' => [
267
                    'timestamp' => 1491782286,
268
                    'timezone' => 'America/New_York',
269
                ],
270
                'expires' => new UTCDateTime(strtotime('+1 day') * 1000),
271
            ]
272
        );
273
        $this->collection->insertOne(
274
            [
275
                '_id' => 'key3',
276
                'data' => [
277
                    'timestamp' => 1491807244,
278
                    'timezone' => 'Pacific/Honolulu',
279
                ],
280
                'expires' => new UTCDateTime(strtotime('+1 day') * 1000),
281
            ]
282
        );
283
284
        $default = new \StdClass();
285
286
        $dates = $this->cache->getMultiple(['key1', 'key2', 'key3', 'key4'], $default);
0 ignored issues
show
Documentation introduced by
array('key1', 'key2', 'key3', 'key4') is of type array<integer,string,{"0..."string","3":"string"}>, but the function expects a object<SubjectivePHP\Psr\SimpleCache\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
287
288
        $this->assertCount(4, $dates);
289
290
        $this->assertSame('2017-04-09 23:58:06', $dates['key1']->format('Y-m-d H:i:s'));
291
        $this->assertSame($default, $dates['key2']);
292
        $this->assertSame('2017-04-10 06:54:04', $dates['key3']->format('Y-m-d H:i:s'));
293
        $this->assertSame($default, $dates['key4']);
294
    }
295
296
    /**
297
     * Verify basic behavior of setMultiple().
298
     *
299
     * @test
300
     * @covers ::setMultiple
301
     *
302
     * @return void
303
     */
304
    public function setMultple()
305
    {
306
        $dates = [
307
            'key1' => new DateTime(),
308
            'key2' => new DateTime(),
309
        ];
310
311
        $this->assertTrue($this->cache->setMultiple($dates, 86400));
0 ignored issues
show
Documentation introduced by
$dates is of type array<string,object<Date...2":"object<DateTime>"}>, but the function expects a object<SubjectivePHP\Psr\SimpleCache\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
312
        $expires = new UTCDateTime((time() + 86400) * 1000);
313
        $this->assertDateTimeDocument('key1', $expires, $dates['key1']);
314
        $this->assertDateTimeDocument('key2', $expires, $dates['key2']);
315
    }
316
317
    /**
318
     * Verify behavior of setMultiple() when mongo throws an exception.
319
     *
320
     * @test
321
     * @covers ::setMultiple
322
     *
323
     * @return void
324
     */
325
    public function setMultpleMongoException()
326
    {
327
        $mockCollection = $this->getFailingCollectionMock('updateOne');
328
        $cache = new MongoCache($mockCollection, $this->getSerializer());
329
        $responses = ['key1' => new DateTime(), 'key2' => new DateTime()];
330
        $this->assertFalse($cache->setMultiple($responses, 86400));
0 ignored issues
show
Documentation introduced by
$responses is of type array<string,object<Date...2":"object<DateTime>"}>, but the function expects a object<SubjectivePHP\Psr\SimpleCache\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
331
    }
332
333
    /**
334
     * Verify basic behavior of deleteMultiple().
335
     *
336
     * @test
337
     * @covers ::deleteMultiple
338
     *
339
     * @return void
340
     */
341
    public function deleteMultiple()
342
    {
343
        $this->collection->insertOne(['_id' => 'key1']);
344
        $this->collection->insertOne(['_id' => 'key2']);
345
        $this->collection->insertOne(['_id' => 'key3']);
346
347
        $this->assertTrue($this->cache->deleteMultiple(['key1', 'key3']));
0 ignored issues
show
Documentation introduced by
array('key1', 'key3') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a object<SubjectivePHP\Psr\SimpleCache\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
348
349
        $actual = $this->collection->find(
350
            [],
351
            ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']]
352
        )->toArray();
353
354
        $this->assertEquals([['_id' => 'key2']], $actual);
355
    }
356
357
    /**
358
     * Verify behavior of deleteMultiple() when mongo throws an exception.
359
     *
360
     * @test
361
     * @covers ::deleteMultiple
362
     *
363
     * @return void
364
     */
365
    public function deleteMultipleMongoException()
366
    {
367
        $mockCollection = $this->getFailingCollectionMock('deleteMany');
368
        $cache = new MongoCache($mockCollection, $this->getSerializer());
369
        $this->assertFalse($cache->deleteMultiple(['key1', 'key3']));
0 ignored issues
show
Documentation introduced by
array('key1', 'key3') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a object<SubjectivePHP\Psr\SimpleCache\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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
                'data' => [
410
                    'timestamp' => $expected->getTimestamp(),
411
                    'timezone' => $expected->getTimeZone()->getName(),
412
                ],
413
            ],
414
            $actual
415
        );
416
    }
417
418
    private function getFailingCollectionMock(string $methodToFail) : Collection
419
    {
420
        $mock = $this->getMockBuilder(Collection::class)->disableOriginalConstructor()->getMock();
421
        $mock->method($methodToFail)->will($this->throwException(new \Exception()));
422
        return $mock;
423
    }
424
425
    /**
426
     * Helper method to get a SerializerInterface instance.
427
     *
428
     * @return SerializerInterface
429
     */
430
    private function getSerializer() : SerializerInterface
431
    {
432
        return new class implements SerializerInterface
0 ignored issues
show
Coding Style introduced by
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...
433
        {
434
            /**
435
             * @see SerializerInterface::unserialize().
436
             *
437
             * @param mixed $data The serialized data.
438
             *
439
             * @return DateTime
440
             */
441
            public function unserialize($data)
442
            {
443
                return new DateTime("@{$data['timestamp']}", timezone_open($data['timezone']));
444
            }
445
446
            /**
447
             * @see SerializerInterface::serialize().
448
             *
449
             * @param mixed $value The data to serialize.
450
             *
451
             * @return array
452
             */
453
            public function serialize($value)
454
            {
455
                return [
456
                    'timestamp' => $value->getTimestamp(),
457
                    'timezone' => $value->getTimezone()->getName(),
458
                ];
459
            }
460
        };
461
    }
462
}
463