Failed Conditions
Push — master ( e075e1...a59e29 )
by Chad
12s
created

MongoCacheTest::deleteMongoException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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