Failed Conditions
Push — master ( 516359...a8b39a )
by Florent
04:00
created

ResponseContext   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 353
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 0
Metric Value
wmc 45
lcom 1
cbo 15
dl 0
loc 353
rs 7.5292
c 0
b 0
f 0

How to fix   Complexity   

Complex Class

Complex classes like ResponseContext often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ResponseContext, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Component\Server\Tests\Context;
15
16
use Assert\Assertion;
17
use Behat\Behat\Context\Context;
18
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
19
use Behat\Gherkin\Node\PyStringNode;
20
use Psr\Http\Message\ResponseInterface;
21
22
final class ResponseContext implements Context
23
{
24
    /**
25
     * @var null|ResponseInterface
26
     */
27
    private $response = null;
28
29
    /**
30
     * @var null|array
31
     */
32
    private $error = null;
33
34
    /**
35
     * @var ApplicationContext
36
     */
37
    private $applicationContext;
38
39
    /**
40
     * @BeforeScenario
41
     *
42
     * @param BeforeScenarioScope $scope
43
     */
44
    public function gatherContexts(BeforeScenarioScope $scope)
45
    {
46
        $environment = $scope->getEnvironment();
47
48
        $this->applicationContext = $environment->getContext(ApplicationContext::class);
49
    }
50
51
    /**
52
     * @param ResponseInterface $response
53
     */
54
    public function setResponse(ResponseInterface $response)
55
    {
56
        $this->response = $response;
57
        if ($this->response->getBody()->isSeekable()) {
58
            $this->response->getBody()->rewind();
59
        }
60
    }
61
62
    /**
63
     * @return ResponseInterface
64
     */
65
    public function getResponse(): ResponseInterface
66
    {
67
        return $this->response;
68
    }
69
70
    /**
71
     * @Then the response code is :code
72
     */
73
    public function theResponseCodeIs($code)
74
    {
75
        Assertion::eq((int) $code, $this->getResponse()->getStatusCode());
76
    }
77
78
    /**
79
     * @Then the response contains
80
     */
81
    public function theResponseContains(PyStringNode $response)
82
    {
83
        $this->rewind();
84
        Assertion::eq($response->getRaw(), (string) $this->getResponse()->getBody()->getContents());
85
    }
86
87
    /**
88
     * @Then the response contains an Id Token with the following claims for the client :client_id
89
     */
90
    public function theResponseContainsAnIdTokenWithTheFollowingClaims($client_id, PyStringNode $response)
91
    {
92
        $client = $this->applicationContext->getApplication()->getClientRepository()->find(\OAuth2Framework\Component\Server\Model\Client\ClientId::create($client_id));
93
        Assertion::isInstanceOf($client, \OAuth2Framework\Component\Server\Model\Client\Client::class);
94
        $claims = json_decode($response->getRaw(), true);
95
        $response = (string) $this->getResponse()->getBody()->getContents();
96
        $jwt = $this->applicationContext->getApplication()->getJwtLoader()->load($response);
97
        Assertion::isInstanceOf($jwt, \Jose\Object\JWSInterface::class);
98
        Assertion::true(empty(array_diff($claims, $jwt->getClaims())));
99
    }
100
101
    /**
102
     * @Then the response contains an error with code :code
103
     */
104
    public function theResponseContainsAnError($code)
105
    {
106
        Assertion::eq((int) $code, $this->getResponse()->getStatusCode());
107
        Assertion::greaterOrEqualThan($this->getResponse()->getStatusCode(), 400);
108
        if (401 === $this->getResponse()->getStatusCode()) {
109
            $headers = $this->getResponse()->getHeader('WWW-Authenticate');
110
            Assertion::greaterThan(count($headers), 0);
111
            $header = $headers[0];
112
            preg_match_all('/(\w+\*?)="((?:[^"\\\\]|\\\\.)+)"|([^\s,$]+)/', substr($header, strpos($header, ' ')), $matches, PREG_SET_ORDER);
113
            if (!is_array($matches)) {
114
                throw new \InvalidArgumentException('Unable to parse header');
115
            }
116
            foreach ($matches as $match) {
117
                $this->error[$match[1]] = $match[2];
118
            }
119
        } else {
120
            $this->rewind();
121
            $response = (string) $this->getResponse()->getBody()->getContents();
122
            $json = json_decode($response, true);
123
            Assertion::isArray($json);
124
            Assertion::keyExists($json, 'error');
125
            $this->error = $json;
126
        }
127
    }
128
129
    /**
130
     * @Then the error is :error
131
     *
132
     * @param string $error
133
     */
134
    public function theErrorIs($error)
135
    {
136
        Assertion::notNull($this->error);
137
        Assertion::keyExists($this->error, 'error');
138
        Assertion::eq($error, $this->error['error']);
139
    }
140
141
    /**
142
     * @Then the error description is :errorDescription
143
     *
144
     * @param string $errorDescription
145
     */
146
    public function theErrorDescriptionIs($errorDescription)
147
    {
148
        Assertion::notNull($this->error);
149
        Assertion::keyExists($this->error, 'error_description');
150
        Assertion::eq($errorDescription, $this->error['error_description']);
151
    }
152
153
    /**
154
     * @Then the client should be redirected
155
     */
156
    public function theClientShouldBeRedirected()
157
    {
158
        Assertion::eq(302, $this->getResponse()->getStatusCode());
159
        $header = $this->getResponse()->getHeaders();
160
        Assertion::keyExists($header, 'Location');
161
        $location = $header['Location'];
162
        Assertion::true(!empty($location));
163
    }
164
165
    /**
166
     * @Then no access token creation event is thrown
167
     */
168
    public function noAccessTokenCreationEventIsThrown()
169
    {
170
        $events = $this->applicationContext->getApplication()->getAccessTokenCreatedEventHandler()->getEvents();
171
        Assertion::eq(0, count($events));
172
    }
173
174
    /**
175
     * @Then the response contains an access token
176
     */
177
    public function theResponseContainsAnAccessToken()
178
    {
179
        $this->rewind();
180
        $content = (string) $this->getResponse()->getBody()->getContents();
181
        $data = json_decode($content, true);
182
        Assertion::isArray($data);
183
        Assertion::keyExists($data, 'access_token');
184
    }
185
186
    /**
187
     * @Then an access token creation event is thrown
188
     */
189
    public function anAccessTokenCreationEventIsThrown()
190
    {
191
        $events = $this->applicationContext->getApplication()->getAccessTokenCreatedEventHandler()->getEvents();
192
        Assertion::greaterThan(count($events), 0);
193
    }
194
195
    /**
196
     * @Then a refresh token creation event is thrown
197
     */
198
    public function aRefreshTokenCreationEventIsThrown()
199
    {
200
        $events = $this->applicationContext->getApplication()->getRefreshTokenCreatedEventHandler()->getEvents();
201
        Assertion::greaterThan(count($events), 0);
202
    }
203
204
    /**
205
     * @Then the response contains something like :pattern
206
     */
207
    public function theResponseContainsSomethingLike($pattern)
208
    {
209
        $this->rewind();
210
        $content = (string) $this->getResponse()->getBody()->getContents();
211
        Assertion::regex($content, $pattern);
212
    }
213
214
    /**
215
     * @Then the content type of the response is :content_type
216
     */
217
    public function theContentTypeOfTheResponseIs($content_type)
218
    {
219
        Assertion::eq($content_type, implode('', $this->getResponse()->getHeader('Content-Type')));
220
    }
221
222
    private function rewind()
223
    {
224
        if (true === $this->getResponse()->getBody()->isSeekable()) {
225
            $this->getResponse()->getBody()->rewind();
226
        }
227
    }
228
229
    /**
230
     * @Then the redirection Uri starts with :pattern
231
     */
232
    public function theRedirectionUriStartsWith($pattern)
233
    {
234
        $locations = $this->getResponse()->getHeader('Location');
235
        foreach ($locations as $location) {
236
            if (mb_substr($location, 0, mb_strlen($pattern, '8bit'), '8bit') === $pattern) {
237
                return;
238
            }
239
        }
240
        throw new \InvalidArgumentException(sprintf('The location header is "%s".', implode(', ', $locations)));
241
    }
242
243
    /**
244
     * @Then the redirection Uri query should contain a parameter :parameter
245
     */
246
    public function theRedirectionUriQueryShouldContainAParameter($parameter)
247
    {
248
        $uriFactory = $this->applicationContext->getApplication()->getUriFactory();
249
        $locations = $this->getResponse()->getHeader('Location');
250
        foreach ($locations as $location) {
251
            $uri = $uriFactory->createUri($location);
252
            $query = $uri->getQuery();
253
            parse_str($query, $data);
254
            if (array_key_exists($parameter, $data)) {
255
                return;
256
            }
257
        }
258
        throw new \InvalidArgumentException(sprintf('The location header is "%s".', implode(', ', $locations)));
259
    }
260
261
    /**
262
     * @Then the redirection Uri query should contain a parameter :parameter with value :value
263
     */
264
    public function theRedirectionUriQueryShouldContainAParameterWithValue($parameter, $value)
265
    {
266
        $uriFactory = $this->applicationContext->getApplication()->getUriFactory();
267
        $locations = $this->getResponse()->getHeader('Location');
268
        foreach ($locations as $location) {
269
            $uri = $uriFactory->createUri($location);
270
            $query = $uri->getQuery();
271
            parse_str($query, $data);
272
            if (array_key_exists($parameter, $data)) {
273
                Assertion::eq($data[$parameter], $value, sprintf('The parameter \'%s\' value is \'%s\'.', $parameter, $data[$parameter]));
274
275
                return;
276
            }
277
        }
278
        throw new \InvalidArgumentException(sprintf('The location header is "%s".', implode(', ', $locations)));
279
    }
280
281
    /**
282
     * @Then the redirection Uri fragment should contain a parameter :parameter
283
     */
284
    public function theRedirectionUriFragmentShouldContainAParameter($parameter)
285
    {
286
        $uriFactory = $this->applicationContext->getApplication()->getUriFactory();
287
        $locations = $this->getResponse()->getHeader('Location');
288
        foreach ($locations as $location) {
289
            $uri = $uriFactory->createUri($location);
290
            $fragment = $uri->getFragment();
291
            parse_str($fragment, $data);
292
            if (array_key_exists($parameter, $data)) {
293
                return;
294
            }
295
        }
296
        throw new \InvalidArgumentException(sprintf('The location header is "%s".', implode(', ', $locations)));
297
    }
298
299
    /**
300
     * @Then the redirection Uri fragment should contain a parameter :parameter with value :value
301
     */
302
    public function theRedirectionUriFragmentShouldContainAParameterWithValue($parameter, $value)
303
    {
304
        $uriFactory = $this->applicationContext->getApplication()->getUriFactory();
305
        $locations = $this->getResponse()->getHeader('Location');
306
        foreach ($locations as $location) {
307
            $uri = $uriFactory->createUri($location);
308
            $fragment = $uri->getFragment();
309
            parse_str($fragment, $data);
310
            if (array_key_exists($parameter, $data)) {
311
                Assertion::eq($data[$parameter], $value, sprintf('The parameter \'%s\' value is \'%s\'.', $parameter, $data[$parameter]));
312
313
                return;
314
            }
315
        }
316
        throw new \InvalidArgumentException(sprintf('The location header is "%s".', implode(', ', $locations)));
317
    }
318
319
    /**
320
     * @Then the redirection ends with :pattern
321
     */
322
    public function theRedirectionEndsWith($pattern)
323
    {
324
        $locations = $this->getResponse()->getHeader('Location');
325
        foreach ($locations as $location) {
326
            if (mb_substr($location, -mb_strlen($pattern, '8bit'), null, '8bit') === $pattern) {
327
                return;
328
            }
329
        }
330
        throw new \InvalidArgumentException(sprintf('The location header is "%s".', implode(', ', $locations)));
331
    }
332
333
    /**
334
     * @Then the redirect query should contain parameter :parameter with value :value
335
     */
336
    public function theRedirectQueryShouldContainParameterWithValue($parameter, $value)
337
    {
338
        $uriFactory = $this->applicationContext->getApplication()->getUriFactory();
339
        $locations = $this->getResponse()->getHeader('Location');
340
        foreach ($locations as $location) {
341
            $uri = $uriFactory->createUri($location);
342
            $query = $uri->getQuery();
343
            parse_str($query, $data);
344
            if (array_key_exists($parameter, $data)) {
345
                Assertion::eq($data[$parameter], $value);
346
347
                return;
348
            }
349
        }
350
        throw new \InvalidArgumentException(sprintf('The location header is "%s".', implode(', ', $locations)));
351
    }
352
353
    /**
354
     * @Then I should be on the login screen
355
     */
356
    public function iShouldBeOnTheLoginScreen()
357
    {
358
        $this->rewind();
359
        $content = (string) $this->getResponse()->getBody()->getContents();
360
361
        Assertion::eq($content, 'You are redirected to the login page');
362
    }
363
364
    /**
365
     * @Then I should be on the consent screen
366
     */
367
    public function iShouldBeOnTheConsentScreen()
368
    {
369
        $this->rewind();
370
        $content = (string) $this->getResponse()->getBody()->getContents();
371
372
        Assertion::eq($content, 'You are on the consent screen');
373
    }
374
}
375