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

MongoCacheTest::setNonStringKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
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 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());
0 ignored issues
show
Documentation introduced by
new \DateTime() is of type object<DateTime>, but the function expects a null|integer|object<Subj...mpleCache\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...
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());
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...
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()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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());
0 ignored issues
show
Documentation introduced by
$mockCollection is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<MongoDB\Collection>.

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...
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()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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());
0 ignored issues
show
Documentation introduced by
$mockCollection is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<MongoDB\Collection>.

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...
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
Unused Code introduced by
$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
Unused Code introduced by
$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);
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...
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));
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...
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());
0 ignored issues
show
Documentation introduced by
$mockCollection is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<MongoDB\Collection>.

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...
320
        $responses = ['key1' => new DateTime(), 'key2' => new DateTime()];
321
        $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...
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']));
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...
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()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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());
0 ignored issues
show
Documentation introduced by
$mockCollection is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<MongoDB\Collection>.

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...
363
        $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...
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
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...
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