Completed
Push — master ( b14d31...cc7476 )
by Michał
01:27
created

testHandleBatchBreadcrumbsSecurityAndRouting()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 9.392
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Dziki\MonologSentryBundle\Tests\Unit\Handler;
6
7
use Dziki\MonologSentryBundle\Handler\Raven;
8
use Monolog\Formatter\FormatterInterface;
9
use Monolog\Formatter\LineFormatter;
10
use Monolog\Logger;
11
use PHPUnit\Framework\TestCase;
12
use Raven_Client;
13
14
class RavenTest extends TestCase
15
{
16
    public function setUp()
17
    {
18
        if (!class_exists('Raven_Client')) {
19
            $this->markTestSkipped('sentry/sentry not installed');
20
        }
21
    }
22
23
    public function testConstruct()
24
    {
25
        $handler = new Raven($this->getRavenClient());
26
        $this->assertInstanceOf(Raven::class, $handler);
27
    }
28
29
    protected function getRavenClient()
30
    {
31
        $dsn = 'http://43f6017361224d098402974103bfc53d:[email protected]:9000/1';
32
33
        return new MockRavenClient($dsn);
34
    }
35
36 View Code Duplication
    public function testDebug()
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...
37
    {
38
        $ravenClient = $this->getRavenClient();
39
        $handler = $this->getHandler($ravenClient);
40
41
        $record = $this->getRecord(Logger::DEBUG, 'A test debug message');
42
        $handler->handle($record);
43
44
        $this->assertEquals($ravenClient::DEBUG, $ravenClient->lastData['level']);
45
        $this->assertContains($record['message'], $ravenClient->lastData['message']);
46
    }
47
48
    protected function getHandler($ravenClient)
49
    {
50
        $handler = new Raven($ravenClient);
51
52
        return $handler;
53
    }
54
55
    /**
56
     * @param int $level
57
     * @param string $message
58
     * @param array $context
59
     *
60
     * @param string $channel
61
     * @param array $extra
62
     * @return array Record
63
     * @throws \Exception
64
     */
65
    protected function getRecord($level = Logger::WARNING, $message = 'test', array $context = [], $channel = 'test', $extra = []): array
66
    {
67
        return [
68
            'message' => (string)$message,
69
            'context' => $context,
70
            'level' => $level,
71
            'level_name' => Logger::getLevelName($level),
72
            'channel' => $channel,
73
            'datetime' => new \DateTimeImmutable(),
74
            'extra' => $extra,
75
        ];
76
    }
77
78 View Code Duplication
    public function testWarning()
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...
79
    {
80
        $ravenClient = $this->getRavenClient();
81
        $handler = $this->getHandler($ravenClient);
82
83
        $record = $this->getRecord(Logger::WARNING, 'A test warning message');
84
        $handler->handle($record);
85
86
        $this->assertEquals($ravenClient::WARNING, $ravenClient->lastData['level']);
87
        $this->assertContains($record['message'], $ravenClient->lastData['message']);
88
    }
89
90 View Code Duplication
    public function testTag()
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...
91
    {
92
        $ravenClient = $this->getRavenClient();
93
        $handler = $this->getHandler($ravenClient);
94
95
        $tags = [1, 2, 'foo'];
96
        $record = $this->getRecord(Logger::INFO, 'test', ['tags' => $tags]);
97
        $handler->handle($record);
98
99
        $this->assertEquals($tags, $ravenClient->lastData['tags']);
100
    }
101
102
    public function testExtraParameters()
103
    {
104
        $ravenClient = $this->getRavenClient();
105
        $handler = $this->getHandler($ravenClient);
106
107
        $checksum = '098f6bcd4621d373cade4e832627b4f6';
108
        $release = '05a671c66aefea124cc08b76ea6d30bb';
109
        $eventId = '31423';
110
        $record = $this->getRecord(
111
            Logger::INFO,
112
            'test',
113
            ['checksum' => $checksum, 'release' => $release, 'event_id' => $eventId]
114
        );
115
        $handler->handle($record);
116
117
        $this->assertEquals($checksum, $ravenClient->lastData['checksum']);
118
        $this->assertEquals($release, $ravenClient->lastData['release']);
119
        $this->assertEquals(
120
            $eventId,
121
            $ravenClient->lastData['event_id'] ?? $ravenClient->lastData['extra']['context']['event_id']
122
        );
123
    }
124
125 View Code Duplication
    public function testFingerprint()
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...
126
    {
127
        $ravenClient = $this->getRavenClient();
128
        $handler = $this->getHandler($ravenClient);
129
130
        $fingerprint = ['{{ default }}', 'other value'];
131
        $record = $this->getRecord(Logger::INFO, 'test', ['fingerprint' => $fingerprint]);
132
        $handler->handle($record);
133
134
        $this->assertEquals($fingerprint, $ravenClient->lastData['fingerprint']);
135
    }
136
137
    public function testUserContext()
138
    {
139
        $ravenClient = $this->getRavenClient();
140
        $handler = $this->getHandler($ravenClient);
141
142
        $recordWithNoContext = $this->getRecord(Logger::ERROR, 'test with default user context');
143
        // set user context 'externally'
144
145
        $user = [
146
            'id' => '123',
147
            'email' => '[email protected]',
148
        ];
149
150
        $recordWithContext = $this->getRecord(
151
            Logger::ERROR,
152
            'test',
153
            [
154
                'user' => $user,
155
                'tags' => ['another_tag' => 'null_value'],
156
                'something' => 'anything',
157
                'exception' => new \Exception('test exception'),
158
                'logger' => 'logger',
159
            ],
160
            'any',
161
            [
162
                'tags' => ['tag_name' => 'value'],
163
                'some_additional_data_key' => 'some_additional_data',
164
            ]
165
        );
166
167
        // handle with null context
168
        $ravenClient->user_context(null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
169
        $handler->handle($recordWithContext);
170
171
        $this->assertEquals($user, $ravenClient->lastData['user']);
172
173
        $ravenClient->user_context(['id' => 'test_user_id']);
174
        // handle context
175
        $handler->handle($recordWithContext);
176
        $this->assertEquals($user, $ravenClient->lastData['user']);
177
178
        // check to see if its reset
179
        $handler->handle($recordWithNoContext);
180
        $this->assertInternalType('array', $ravenClient->context->user);
181
        $this->assertSame('test_user_id', $ravenClient->context->user['id']);
182
    }
183
184
    public function testException()
185
    {
186
        $ravenClient = $this->getRavenClient();
187
        $handler = $this->getHandler($ravenClient);
188
189
        try {
190
            $this->methodThatThrowsAnException();
191
        } catch (\Exception $e) {
192
            $record = $this->getRecord(Logger::ERROR, $e->getMessage(), ['context' => ['exception' => $e]]);
193
            $handler->handle($record);
194
        }
195
196
        $this->assertEquals('[test] ' . $record['message'], $ravenClient->lastData['message']);
197
    }
198
199
    private function methodThatThrowsAnException()
200
    {
201
        throw new \Exception('This is an exception');
202
    }
203
204
    public function testHandleBatch()
205
    {
206
        $records = $this->getMultipleRecords();
207
        $records[] = $this->getRecord(Logger::WARNING, 'warning');
208
        $records[] = $this->getRecord(Logger::WARNING, 'warning');
209
210
        $logFormatter = $this->createMock('Monolog\\Formatter\\FormatterInterface');
211
212
        $formatter = $this->createMock('Monolog\\Formatter\\FormatterInterface');
213
        $formatter->expects($this->once())->method('format')->with(
214
            $this->callback(
215
                function ($record) {
216
                    return 400 == $record['level'];
217
                }
218
            )
219
        )
220
        ;
221
222
        $handler = $this->getHandler($this->getRavenClient());
223
        $handler->setBatchFormatter($logFormatter);
224
        $handler->setFormatter($formatter);
225
        $handler->handleBatch($records);
226
227
        $handler->handleBatch([]);
228
    }
229
230
    protected function getMultipleRecords(): array
231
    {
232
        return [
233
            $this->getRecord(Logger::DEBUG, 'debug message 1'),
234
            $this->getRecord(Logger::DEBUG, 'debug message 2'),
235
            $this->getRecord(Logger::INFO, 'information'),
236
            $this->getRecord(Logger::WARNING, 'warning'),
237
            $this->getRecord(Logger::ERROR, 'error'),
238
        ];
239
    }
240
241
    /**
242
     * @test
243
     */
244
    public function doNothingOnEmptyBatch(): void
245
    {
246
        $logFormatter = $this->createMock(FormatterInterface::class);
247
        $logFormatter->expects($this->never())
248
                     ->method('format')
249
        ;
250
251
        $logFormatter->expects($this->never())
252
                     ->method('formatBatch')
253
        ;
254
255
        $handler = $this->getHandler($this->getRavenClient());
256
        $handler->setBatchFormatter($logFormatter);
257
        $handler->setFormatter($logFormatter);
258
        $handler->handleBatch([]);
259
    }
260
261
    /**
262
     * @test
263
     */
264
    public function addContextsIfProvided(): void
265
    {
266
        $logFormatter = $this->createMock(FormatterInterface::class);
267
        $logFormatter->expects($this->once())
268
                     ->method('format')
269
                     ->willReturnArgument(0)
270
        ;
271
272
        $ravenClient = $this->getRavenClient();
273
274
        $handler = $this->getHandler($ravenClient);
275
        $handler->setBatchFormatter($logFormatter);
276
        $handler->setFormatter($logFormatter);
277
278
        $record = $this->getRecord();
279
        $record['contexts'] = ['browser_context'];
280
        $handler->handle($record);
281
282
        $this->assertSame(['browser_context'], $ravenClient->lastData['contexts']);
283
    }
284
285
    public function testHandleBatchDoNothingIfRecordsAreBelowLevel()
286
    {
287
        $records = [
288
            $this->getRecord(Logger::DEBUG, 'debug message 1'),
289
            $this->getRecord(Logger::DEBUG, 'debug message 2'),
290
            $this->getRecord(Logger::INFO, 'information'),
291
        ];
292
293
        $handler = $this->getMockBuilder('Monolog\Handler\RavenHandler')
294
                        ->setMethods(['handle'])
295
                        ->setConstructorArgs([$this->getRavenClient()])
296
                        ->getMock()
297
        ;
298
        $handler->expects($this->never())->method('handle');
299
        $handler->setLevel(Logger::ERROR);
300
        $handler->handleBatch($records);
301
    }
302
303
    public function testHandleBatchPicksProperMessage()
304
    {
305
        $records = [
306
            $this->getRecord(Logger::DEBUG, 'debug message 1'),
307
            $this->getRecord(Logger::DEBUG, 'debug message 2'),
308
            $this->getRecord(Logger::INFO, 'information 1'),
309
            $this->getRecord(Logger::ERROR, 'error 1'),
310
            $this->getRecord(Logger::WARNING, 'warning'),
311
            $this->getRecord(Logger::ERROR, 'error 2'),
312
            $this->getRecord(Logger::INFO, 'information 2'),
313
        ];
314
315
        $logFormatter = $this->createMock('Monolog\\Formatter\\FormatterInterface');
316
317
        $formatter = $this->createMock('Monolog\\Formatter\\FormatterInterface');
318
        $formatter->expects($this->once())->method('format')->with(
319
            $this->callback(
320
                function ($record) use ($records) {
321
                    return 'error 1' == $record['message'];
322
                }
323
            )
324
        )
325
        ;
326
327
        $handler = $this->getHandler($this->getRavenClient());
328
        $handler->setBatchFormatter($logFormatter);
329
        $handler->setFormatter($formatter);
330
        $handler->handleBatch($records);
331
    }
332
333
    public function testGetSetBatchFormatter()
334
    {
335
        $ravenClient = $this->getRavenClient();
336
        $handler = $this->getHandler($ravenClient);
337
338
        $handler->setBatchFormatter($formatter = new LineFormatter());
339
        $this->assertSame($formatter, $handler->getBatchFormatter());
340
    }
341
342
    public function testRelease()
343
    {
344
        $ravenClient = $this->getRavenClient();
345
        $handler = $this->getHandler($ravenClient);
346
        $release = 'v42.42.42';
347
        $handler->setRelease($release);
348
        $record = $this->getRecord(Logger::INFO, 'test');
349
        $handler->handle($record);
350
351
        $this->assertEquals($release, $ravenClient->lastData['release']);
352
353
        $localRelease = 'v41.41.41';
354
        $record = $this->getRecord(Logger::INFO, 'test', ['release' => $localRelease]);
355
        $handler->handle($record);
356
        $this->assertEquals($localRelease, $ravenClient->lastData['release']);
357
    }
358
359
    public function testHandleBatchBreadcrumbsSecurityAndRouting(): void
360
    {
361
        $records = $this->getMultipleRecords();
362
        $records[] = $this->getRecord(
363
            Logger::WARNING,
364
            'warning',
365
            ['route_parameters' => [
366
                '_route' => 'foo_bar_route',
367
                '_controller' => 'Foo\Bar\Controller',
368
            ],
369
                'request_uri' => 'foo.bar',
370
            ],
371
            'request'
372
        );
373
        $records[] = $this->getRecord(Logger::WARNING, 'warning', ['user' => ['username' => 'foobar']], 'security');
374
375
        $logFormatter = $this->createMock('Monolog\\Formatter\\FormatterInterface');
376
377
        $formatter = $this->createMock('Monolog\\Formatter\\FormatterInterface');
378
        $formatter->expects($this->once())->method('format')->with(
379
            $this->callback(
380
                function ($record) {
381
                    return 400 == $record['level'];
382
                }
383
            )
384
        )
385
        ;
386
387
        $handler = $this->getHandler($this->getRavenClient());
388
        $handler->setBatchFormatter($logFormatter);
389
        $handler->setFormatter($formatter);
390
        $handler->handleBatch($records);
391
    }
392
393
    protected function getIdentityFormatter(): FormatterInterface
394
    {
395
        $formatter = $this->createMock(FormatterInterface::class);
396
        $formatter->expects($this->any())
397
                  ->method('format')
398
                  ->will(
399
                      $this->returnCallback(
400
                          function ($record) {
401
                              return $record['message'];
402
                          }
403
                      )
404
                  )
405
        ;
406
407
        return $formatter;
408
    }
409
}
410
411
class MockRavenClient extends Raven_Client
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
412
{
413
    public $lastData;
414
    public $lastStack;
415
416
    public function capture($data, $stack = null, $vars = null)
417
    {
418
        $data = array_merge($this->get_user_data(), $data);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $data. This often makes code more readable.
Loading history...
419
        $this->lastData = $data;
420
        $this->lastStack = $stack;
421
    }
422
}
423