Completed
Pull Request — master (#49)
by John
02:41
created

willLogExceptionsWith4xxCodesAsBadRequestNotices()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 15
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 15
loc 15
rs 9.4285
cc 2
eloc 10
nc 2
nop 0
1
<?php
2
/*
3
 * This file is part of the KleijnWeb\SwaggerBundle package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace KleijnWeb\SwaggerBundle\Tests\EventListener;
10
11
use KleijnWeb\SwaggerBundle\EventListener\ExceptionListener;
12
use KleijnWeb\SwaggerBundle\Exception\InvalidParametersException;
13
use Psr\Log\LoggerInterface;
14
use Psr\Log\LogLevel;
15
use Ramsey\VndError\VndError;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
18
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
19
20
/**
21
 * @author John Kleijn <[email protected]>
22
 */
23
class ExceptionListenerTest extends \PHPUnit_Framework_TestCase
24
{
25
    /**
26
     * @var GetResponseForExceptionEvent
27
     */
28
    private $event;
29
30
    /**
31
     * @var \ReflectionProperty
32
     */
33
    private $codeProperty;
34
35
    /**
36
     * @var \Exception
37
     */
38
    private $exception;
39
40
    /**
41
     * @var Request
42
     */
43
    private $request;
44
45
    /**
46
     * @var LoggerInterface
47
     */
48
    private $logger;
49
50
    /**
51
     * @var ExceptionListener
52
     */
53
    private $exceptionListener;
54
55
    /**
56
     * Set up mocking
57
     */
58
    protected function setUp()
59
    {
60
        $this->event = $this
61
            ->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
62
            ->disableOriginalConstructor()
63
            ->setMethods(['getException', 'getRequest'])
64
            ->getMock();
65
66
        $this->exception = new \Exception("Mary had a little lamb");
67
        $reflection = new \ReflectionClass($this->exception);
68
        $codeProperty = $reflection->getProperty('code');
69
        $this->codeProperty = $codeProperty;
70
        $this->codeProperty->setAccessible(true);
71
        $attributes = [
72
            '_resource' => '/foo/bar'
73
        ];
74
        $this->request = new Request($query = [], $request = [], $attributes);
75
76
        $this->event
77
            ->expects($this->any())
78
            ->method('getException')
79
            ->willReturn($this->exception);
80
81
        $this->event
82
            ->expects($this->any())
83
            ->method('getRequest')
84
            ->willReturn($this->request);
85
86
        $this->validationErrorFactory = $this
0 ignored issues
show
Bug introduced by
The property validationErrorFactory does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
87
            ->getMockBuilder('KleijnWeb\SwaggerBundle\Response\VndValidationErrorFactory')
88
            ->disableOriginalConstructor()
89
            ->getMock();
90
91
        $this->logger = $this->getMockForAbstractClass('Psr\Log\LoggerInterface');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getMockForAbstrac...\Log\\LoggerInterface') of type object<PHPUnit_Framework_MockObject_MockObject> is incompatible with the declared type object<Psr\Log\LoggerInterface> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
92
        $this->exceptionListener = new ExceptionListener($this->validationErrorFactory, $this->logger);
93
    }
94
95
    /**
96
     * @test
97
     */
98 View Code Duplication
    public function willLogExceptionsWith4xxCodesAsBadRequestNotices()
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...
99
    {
100
        for ($i = 0; $i < 99; $i++) {
101
            $logger = $this->getMockForAbstractClass('Psr\Log\LoggerInterface');
102
            $logger
103
                ->expects($this->once())
104
                ->method('log')
105
                ->with(LogLevel::NOTICE, $this->stringStartsWith('Bad Request'));
106
107
            /** @var LoggerInterface $logger */
108
            $this->exceptionListener->setLogger($logger);
109
            $this->codeProperty->setValue($this->exception, 400 + $i);
110
            $this->exceptionListener->onKernelException($this->event);
111
        }
112
    }
113
114
    /**
115
     * @test
116
     */
117 View Code Duplication
    public function willLogExceptionsWith5xxCodesAsRuntimeErrors()
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...
118
    {
119
        for ($i = 0; $i < 99; $i++) {
120
            $logger = $this->getMockForAbstractClass('Psr\Log\LoggerInterface');
121
            $logger
122
                ->expects($this->once())
123
                ->method('log')
124
                ->with(LogLevel::ERROR, $this->stringStartsWith('Internal Server Error'));
125
126
            /** @var LoggerInterface $logger */
127
            $this->exceptionListener->setLogger($logger);
128
            $this->codeProperty->setValue($this->exception, 500 + $i);
129
            $this->exceptionListener->onKernelException($this->event);
130
        }
131
    }
132
133
    /**
134
     * @test
135
     */
136
    public function willLogExceptionsWithUnexpectedCodesAsCriticalErrors()
137
    {
138
        $sample = [4096, 777, 22, 5, 0];
139
        foreach ($sample as $code) {
140
            $logger = $this->getMockForAbstractClass('Psr\Log\LoggerInterface');
141
            $logger
142
                ->expects($this->once())
143
                ->method('log')
144
                ->with(LogLevel::CRITICAL, $this->stringStartsWith('Internal Server Error'));
145
146
            /** @var LoggerInterface $logger */
147
            $this->exceptionListener->setLogger($logger);
148
            $this->codeProperty->setValue($this->exception, $code);
149
            $this->exceptionListener->onKernelException($this->event);
150
        }
151
    }
152
153
    /**
154
     * @test
155
     */
156 View Code Duplication
    public function willSetResponseWithVndErrorHeader()
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...
157
    {
158
        foreach ([400, 500] as $code) {
159
            $this->codeProperty->setValue($this->exception, $code);
160
            $this->exceptionListener->onKernelException($this->event);
161
            $response = $this->event->getResponse();
162
            $this->assertContains('application/vnd.error', $response->headers->get('Content-Type'));
163
        }
164
    }
165
166
    /**
167
     * @test
168
     */
169 View Code Duplication
    public function willSetResponseWithValidJsonContent()
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...
170
    {
171
        foreach ([400, 500] as $code) {
172
            $this->codeProperty->setValue($this->exception, $code);
173
            $this->exceptionListener->onKernelException($this->event);
174
            $response = $this->event->getResponse();
175
            $this->assertNotNull(json_decode($response->getContent()));
176
        }
177
    }
178
179
    /**
180
     * @test
181
     */
182
    public function willSetResponseWithSimpleMessage()
183
    {
184
        foreach ([400 => 'Bad Request', 500 => 'Internal Server Error'] as $code => $message) {
185
            $this->codeProperty->setValue($this->exception, $code);
186
            $this->exceptionListener->onKernelException($this->event);
187
            $response = $this->event->getResponse();
188
            $this->assertNotNull($body = json_decode($response->getContent()));
189
            $this->assertEquals($message, $body->message);
190
        }
191
    }
192
193
    /**
194
     * @test
195
     */
196 View Code Duplication
    public function willSetResponseWithLogRef()
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...
197
    {
198
        foreach ([400, 500] as $code) {
199
            $this->codeProperty->setValue($this->exception, $code);
200
            $this->exceptionListener->onKernelException($this->event);
201
            $response = $this->event->getResponse();
202
            $this->assertNotNull(json_decode($response->getContent())->logref);
203
        }
204
    }
205
206
    /**
207
     * @test
208
     */
209
    public function logrefInResponseAndLogMatch()
210
    {
211
        foreach ([400, 500] as $code) {
212
            $logref = null;
213
            $logger = $this->getMockForAbstractClass('Psr\Log\LoggerInterface');
214
            $logger
215
                ->expects($this->once())
216
                ->method('log')
217
                ->with($this->anything(), $this->callback(function ($message) use (&$logref) {
218
                    $matches = [];
219
                    if (preg_match('/logref ([a-z0-9]*)/', $message, $matches)) {
220
                        $logref = $matches[1];
221
222
                        return true;
223
                    }
224
225
                    return false;
226
                }));
227
228
            /** @var LoggerInterface $logger */
229
            $this->exceptionListener->setLogger($logger);
230
            $this->codeProperty->setValue($this->exception, $code);
231
            $this->exceptionListener->onKernelException($this->event);
232
            $response = $this->event->getResponse();
233
            $this->assertEquals($logref, json_decode($response->getContent())->logref);
234
        }
235
    }
236
237
    /**
238
     * @test
239
     */
240
    public function willReturn404Responses()
241
    {
242
        $event = $this
243
            ->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
244
            ->disableOriginalConstructor()
245
            ->setMethods(['getException', 'getRequest'])
246
            ->getMock();
247
248
        $event->expects($this->any())
249
            ->method('getException')
250
            ->willReturn(new NotFoundHttpException());
251
252
        $event->expects($this->any())
253
            ->method('getRequest')
254
            ->willReturn($this->request);
255
256
        $this->exceptionListener->onKernelException($event);
257
        $response = $event->getResponse();
258
        $this->assertSame(404, $response->getStatusCode());
259
    }
260
261
    /**
262
     * @test
263
     */
264
    public function willCreateValidationErrorResponse()
265
    {
266
        $event = $this
267
            ->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
268
            ->disableOriginalConstructor()
269
            ->setMethods(['getException', 'getRequest'])
270
            ->getMock();
271
272
        $exception = new InvalidParametersException('Oh noes', []);
273
274
        $event->expects($this->any())
275
            ->method('getException')
276
            ->willReturn($exception);
277
278
        $event->expects($this->any())
279
            ->method('getRequest')
280
            ->willReturn($this->request);
281
282
        $this->validationErrorFactory
283
            ->expects($this->any())
284
            ->method('create')
285
            ->with($this->request, $exception)
286
            ->willReturn(new VndError('Try again'));
287
288
        $this->exceptionListener->onKernelException($event);
289
        $response = $event->getResponse();
290
        $this->assertSame(400, $response->getStatusCode());
291
    }
292
}
293