Issues (32)

Tests/Listener/ResponseListenerTest.php (5 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Ekino New Relic bundle.
7
 *
8
 * (c) Ekino - Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Ekino\NewRelicBundle\Tests\Listener;
15
16
use Ekino\NewRelicBundle\Listener\ResponseListener;
17
use Ekino\NewRelicBundle\NewRelic\Config;
18
use Ekino\NewRelicBundle\NewRelic\NewRelicInteractorInterface;
19
use Ekino\NewRelicBundle\Twig\NewRelicExtension;
20
use PHPUnit\Framework\TestCase;
0 ignored issues
show
The type PHPUnit\Framework\TestCase was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\HttpFoundation\Response;
23
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
0 ignored issues
show
The type Symfony\Component\HttpKe...ent\FilterResponseEvent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use Symfony\Component\HttpKernel\Event\ResponseEvent;
25
use Symfony\Component\HttpKernel\HttpKernelInterface;
26
27
class ResponseListenerTest extends TestCase
28
{
29
    protected function setUp(): void
30
    {
31
        $this->interactor = $this->getMockBuilder(NewRelicInteractorInterface::class)->getMock();
0 ignored issues
show
Bug Best Practice introduced by
The property interactor does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
32
        $this->newRelic = $this->getMockBuilder(Config::class)
0 ignored issues
show
Bug Best Practice introduced by
The property newRelic does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
33
            ->setMethods(['getCustomEvents', 'getCustomMetrics', 'getCustomParameters'])
34
            ->disableOriginalConstructor()
35
            ->getMock();
36
        $this->extension = $this->getMockBuilder(NewRelicExtension::class)
0 ignored issues
show
Bug Best Practice introduced by
The property extension does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
37
            ->setMethods(['isHeaderCalled', 'isFooterCalled', 'isUsed'])
38
            ->disableOriginalConstructor()
39
            ->getMock();
40
    }
41
42
    public function testOnKernelResponseOnlyMasterRequestsAreProcessed()
43
    {
44
        $event = $this->createFilterResponseEventDummy(null, null, HttpKernelInterface::SUB_REQUEST);
45
46
        $object = new ResponseListener($this->newRelic, $this->interactor);
47
        $object->onKernelResponse($event);
48
49
        $this->newRelic->expects($this->never())->method('getCustomMetrics');
50
    }
51
52
    public function testOnKernelResponseWithOnlyCustomMetricsAndParameters()
53
    {
54
        $events = [
55
            'WidgetSale' => [
56
                [
57
                    'color' => 'red',
58
                    'weight' => 12.5,
59
                ],
60
                [
61
                    'color' => 'blue',
62
                    'weight' => 12.5,
63
                ],
64
            ],
65
        ];
66
67
        $metrics = [
68
            'foo_a' => 4.7,
69
            'foo_b' => 11,
70
        ];
71
72
        $parameters = [
73
            'foo_1' => 'bar_1',
74
            'foo_2' => 'bar_2',
75
        ];
76
77
        $this->newRelic->expects($this->once())->method('getCustomEvents')->willReturn($events);
78
        $this->newRelic->expects($this->once())->method('getCustomMetrics')->willReturn($metrics);
79
        $this->newRelic->expects($this->once())->method('getCustomParameters')->willReturn($parameters);
80
81
        $this->interactor->expects($this->at(0))->method('addCustomMetric')->with('foo_a', 4.7);
82
        $this->interactor->expects($this->at(1))->method('addCustomMetric')->with('foo_b', 11);
83
        $this->interactor->expects($this->at(2))->method('addCustomParameter')->with('foo_1', 'bar_1');
84
        $this->interactor->expects($this->at(3))->method('addCustomParameter')->with('foo_2', 'bar_2');
85
86
        $this->interactor->expects($this->at(4))->method('addCustomEvent')->with('WidgetSale', [
87
            'color' => 'red',
88
            'weight' => 12.5,
89
        ]);
90
        $this->interactor->expects($this->at(5))->method('addCustomEvent')->with('WidgetSale', [
91
            'color' => 'blue',
92
            'weight' => 12.5,
93
        ]);
94
95
        $event = $this->createFilterResponseEventDummy();
96
97
        $object = new ResponseListener($this->newRelic, $this->interactor, false);
98
        $object->onKernelResponse($event);
99
    }
100
101
    public function testOnKernelResponseInstrumentDisabledInRequest()
102
    {
103
        $this->setupNoCustomMetricsOrParameters();
104
105
        $this->interactor->expects($this->once())->method('disableAutoRUM');
106
107
        $event = $this->createFilterResponseEventDummy();
108
109
        $object = new ResponseListener($this->newRelic, $this->interactor, true);
110
        $object->onKernelResponse($event);
111
    }
112
113
    public function testSymfonyCacheEnabled()
114
    {
115
        $this->setupNoCustomMetricsOrParameters();
116
117
        $this->interactor->expects($this->once())->method('endTransaction');
118
119
        $event = $this->createFilterResponseEventDummy();
120
121
        $object = new ResponseListener($this->newRelic, $this->interactor, false, true);
122
        $object->onKernelResponse($event);
123
    }
124
125
    public function testSymfonyCacheDisabled()
126
    {
127
        $this->setupNoCustomMetricsOrParameters();
128
129
        $this->interactor->expects($this->never())->method('endTransaction');
130
131
        $event = $this->createFilterResponseEventDummy();
132
133
        $object = new ResponseListener($this->newRelic, $this->interactor, false, false);
134
        $object->onKernelResponse($event);
135
    }
136
137
    /**
138
     * @dataProvider providerOnKernelResponseOnlyInstrumentHTMLResponses
139
     */
140
    public function testOnKernelResponseOnlyInstrumentHTMLResponses($content, $expectsSetContent, $contentType)
141
    {
142
        $this->setupNoCustomMetricsOrParameters();
143
144
        $this->interactor->expects($this->once())->method('disableAutoRUM');
145
        $this->interactor->expects($this->any())->method('getBrowserTimingHeader')->willReturn('__Timing_Header__');
146
        $this->interactor->expects($this->any())->method('getBrowserTimingFooter')->willReturn('__Timing_Feader__');
147
148
        $response = $this->createResponseMock($content, $expectsSetContent, $contentType);
149
        $event = $this->createFilterResponseEventDummy(null, $response);
150
151
        $object = new ResponseListener($this->newRelic, $this->interactor, true);
152
        $object->onKernelResponse($event);
153
    }
154
155
    public function providerOnKernelResponseOnlyInstrumentHTMLResponses()
156
    {
157
        return [
158
            // unsupported content types
159
            [null, null, 'text/xml'],
160
            [null, null, 'text/plain'],
161
            [null, null, 'application/json'],
162
163
            ['content', 'content', 'text/html'],
164
            ['<div class="head">head</div>', '<div class="head">head</div>', 'text/html'],
165
            ['<header>content</header>', '<header>content</header>', 'text/html'],
166
167
            // head, body tags
168
            ['<head><title /></head>', '<head>__Timing_Header__<title /></head>', 'text/html'],
169
            ['<body><div /></body>', '<body><div />__Timing_Feader__</body>', 'text/html'],
170
            ['<head><title /></head><body><div /></body>', '<head>__Timing_Header__<title /></head><body><div />__Timing_Feader__</body>', 'text/html'],
171
172
            // with charset
173
            ['<head><title /></head><body><div /></body>', '<head>__Timing_Header__<title /></head><body><div />__Timing_Feader__</body>', 'text/html; charset=UTF-8'],
174
        ];
175
    }
176
177
    public function testInteractionWithTwigExtensionHeader()
178
    {
179
        $this->newRelic->expects($this->never())->method('getCustomMetrics');
180
        $this->newRelic->expects($this->never())->method('getCustomParameters');
181
        $this->newRelic->expects($this->once())->method('getCustomEvents')->willReturn([]);
182
183
        $this->interactor->expects($this->never())->method('disableAutoRUM');
184
        $this->interactor->expects($this->never())->method('getBrowserTimingHeader');
185
        $this->interactor->expects($this->once())->method('getBrowserTimingFooter')->willReturn('__Timing_Feader__');
186
187
        $this->extension->expects($this->exactly(2))->method('isUsed')->willReturn(true);
188
        $this->extension->expects($this->once())->method('isHeaderCalled')->willReturn(true);
189
        $this->extension->expects($this->once())->method('isFooterCalled')->willReturn(false);
190
191
        $request = $this->createRequestMock(true);
192
        $response = $this->createResponseMock('content', 'content', 'text/html');
193
        $event = $this->createFilterResponseEventDummy($request, $response);
194
195
        $object = new ResponseListener($this->newRelic, $this->interactor, true, false, $this->extension);
196
        $object->onKernelResponse($event);
197
    }
198
199
    public function testInteractionWithTwigExtensionFooter()
200
    {
201
        $this->newRelic->expects($this->never())->method('getCustomMetrics');
202
        $this->newRelic->expects($this->never())->method('getCustomParameters');
203
        $this->newRelic->expects($this->once())->method('getCustomEvents')->willReturn([]);
204
205
        $this->interactor->expects($this->never())->method('disableAutoRUM');
206
        $this->interactor->expects($this->once())->method('getBrowserTimingHeader')->willReturn('__Timing_Feader__');
207
        $this->interactor->expects($this->never())->method('getBrowserTimingFooter');
208
209
        $this->extension->expects($this->exactly(2))->method('isUsed')->willReturn(true);
210
        $this->extension->expects($this->once())->method('isHeaderCalled')->willReturn(false);
211
        $this->extension->expects($this->once())->method('isFooterCalled')->willReturn(true);
212
213
        $request = $this->createRequestMock(true);
214
        $response = $this->createResponseMock('content', 'content', 'text/html');
215
        $event = $this->createFilterResponseEventDummy($request, $response);
216
217
        $object = new ResponseListener($this->newRelic, $this->interactor, true, false, $this->extension);
218
        $object->onKernelResponse($event);
219
    }
220
221
    public function testInteractionWithTwigExtensionHeaderFooter()
222
    {
223
        $this->newRelic->expects($this->never())->method('getCustomMetrics');
224
        $this->newRelic->expects($this->never())->method('getCustomParameters');
225
        $this->newRelic->expects($this->once())->method('getCustomEvents')->willReturn([]);
226
227
        $this->interactor->expects($this->never())->method('disableAutoRUM');
228
        $this->interactor->expects($this->never())->method('getBrowserTimingHeader');
229
        $this->interactor->expects($this->never())->method('getBrowserTimingFooter');
230
231
        $this->extension->expects($this->exactly(2))->method('isUsed')->willReturn(true);
232
        $this->extension->expects($this->once())->method('isHeaderCalled')->willReturn(true);
233
        $this->extension->expects($this->once())->method('isFooterCalled')->willReturn(true);
234
235
        $request = $this->createRequestMock(true);
236
        $response = $this->createResponseMock('content', 'content', 'text/html');
237
        $event = $this->createFilterResponseEventDummy($request, $response);
238
239
        $object = new ResponseListener($this->newRelic, $this->interactor, true, false, $this->extension);
240
        $object->onKernelResponse($event);
241
    }
242
243
    private function setUpNoCustomMetricsOrParameters()
244
    {
245
        $this->newRelic->expects($this->once())->method('getCustomEvents')->willReturn([]);
246
        $this->newRelic->expects($this->once())->method('getCustomMetrics')->willReturn([]);
247
        $this->newRelic->expects($this->once())->method('getCustomParameters')->willReturn([]);
248
249
        $this->interactor->expects($this->never())->method('addCustomEvent');
250
        $this->interactor->expects($this->never())->method('addCustomMetric');
251
        $this->interactor->expects($this->never())->method('addCustomParameter');
252
    }
253
254
    private function createRequestMock($instrumentEnabled = true)
255
    {
256
        $mock = $this->getMockBuilder(Request::class)
257
            ->setMethods(['get'])
258
            ->getMock();
259
        $mock->attributes = $mock;
260
261
        $mock->expects($this->any())->method('get')->willReturn($instrumentEnabled);
262
263
        return $mock;
264
    }
265
266
    private function createResponseMock($content = null, $expectsSetContent = null, $contentType = 'text/html')
267
    {
268
        $mock = $this->getMockBuilder(Response::class)
269
            ->setMethods(['get', 'getContent', 'setContent'])
270
            ->getMock();
271
        $mock->headers = $mock;
272
273
        $mock->expects($this->any())->method('get')->willReturn($contentType);
274
        $mock->expects($content ? $this->any() : $this->never())->method('getContent')->willReturn($content);
275
276
        if ($expectsSetContent) {
277
            $mock->expects($this->exactly(2))->method('setContent')->withConsecutive([''], [$expectsSetContent]);
278
        } else {
279
            $mock->expects($this->never())->method('setContent');
280
        }
281
282
        return $mock;
283
    }
284
285
    private function createFilterResponseEventDummy(Request $request = null, Response $response = null, int $requestType = HttpKernelInterface::MASTER_REQUEST)
286
    {
287
        $kernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock();
288
289
        $eventClass = \class_exists(ResponseEvent::class) ? ResponseEvent::class : FilterResponseEvent::class;
290
        $event = new $eventClass($kernel, $request ?? new Request(), $requestType, $response ?? new Response());
291
292
        return $event;
293
    }
294
}
295