Completed
Push — master ( ca3a05...58f804 )
by André
66:34 queued 43:18
created

RestContext::setUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the RestContext for RestBundle.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Bundle\EzPublishRestBundle\Features\Context;
12
13
use Behat\Mink\Mink;
14
use Behat\MinkExtension\Context\MinkAwareContext;
15
use eZ\Publish\Core\REST\Client\Values\ErrorMessage;
16
use EzSystems\BehatBundle\Context\Api\Context;
17
use EzSystems\BehatBundle\Helper\Gherkin as GherkinHelper;
18
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
19
use Behat\Gherkin\Node\TableNode;
20
use PHPUnit_Framework_Assert as Assertion;
21
use Exception;
22
23
/**
24
 * RestContext is the core of the REST testing
25
 *   All SubContext (traits), helpers are loaded here
26
 *   Settings and client initializations is done here
27
 *   Also it contains all REST generic actions.
28
 */
29
class RestContext extends Context implements MinkAwareContext
30
{
31
    use SubContext\EzRest;
32
    use SubContext\Authentication;
33
    use SubContext\ContentTypeGroup;
34
    use SubContext\Exception;
35
    use SubContext\Views;
36
    use SubContext\User;
37
38
    const AUTHTYPE_BASICHTTP = 'http_basic';
39
    const AUTHTYPE_SESSION = 'session';
40
41
    const DEFAULT_URL = 'http://localhost/';
42
    const DEFAULT_DRIVER = 'GuzzleDriver';
43
    const DEFAULT_BODY_TYPE = 'json';
44
45
    const DEFAULT_AUTH_TYPE = self::AUTHTYPE_SESSION;
46
47
    /**
48
     * Rest driver for all requests and responses.
49
     *
50
     * @var \eZ\Bundle\EzPublishRestBundle\Features\Context\RestClient\DriverInterface
51
     */
52
    protected $restDriver;
53
54
    /**
55
     * @var string
56
     */
57
    private $url;
58
59
    /**
60
     * @var string
61
     */
62
    private $driver;
63
64
    /**
65
     * @var \Behat\Mink\Mink
66
     */
67
    private $mink;
68
69
    /**
70
     * @var array
71
     */
72
    private $minkParameters;
73
74
    /**
75
     * Initialize class.
76
     *
77
     * @param string $url    Base URL for REST calls
0 ignored issues
show
Bug introduced by
There is no parameter named $url. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
78
     * @param string $driver REST Driver to be used
79
     * @param string $json
0 ignored issues
show
Bug introduced by
There is no parameter named $json. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
80
     */
81
    public function __construct(
82
        $driver = self::DEFAULT_DRIVER,
83
        $type = self::DEFAULT_BODY_TYPE,
84
        $authType = self::DEFAULT_AUTH_TYPE
85
    ) {
86
        $this->driver = $driver;
87
        $this->restBodyType = $type;
88
        $this->authType = $authType;
89
90
        $this->setRestDriver($this->driver);
91
    }
92
93
    private function setUrl($url)
94
    {
95
        $this->url = $url;
96
        if (isset($this->restDriver)) {
97
            $this->restDriver->setHost($this->url);
98
        }
99
    }
100
101
    /**
102
     * Sets Mink instance.
103
     *
104
     * @param Mink $mink Mink session manager
105
     */
106
    public function setMink(Mink $mink)
107
    {
108
        $this->mink = $mink;
109
    }
110
111
    /**
112
     * Sets parameters provided for Mink.
113
     * While at it, take the base_url, and use it to build the one for the REST driver.
114
     *
115
     * @param array $parameters
116
     */
117
    public function setMinkParameters(array $parameters)
118
    {
119
        $this->minkParameters = $parameters;
120
        $this->setUrl($parameters['base_url'] . '/api/ezp/v2/');
121
    }
122
123
    /**
124
     * @BeforeScenario
125
     */
126
    private function resetDriver()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
127
    {
128
        $this->setRestDriver($this->driver, $this->url);
129
    }
130
131
    /**
132
     * Create and set the REST driver to be used.
133
     *
134
     * @param string $restDriver REST driver class name
135
     */
136
    private function setRestDriver($restDriver)
137
    {
138
        $namespace = '\\' . __NAMESPACE__ .  '\\RestClient\\';
139
        $driver = $namespace . $restDriver;
140
        $parent = $namespace . 'DriverInterface';
141
142
        if (
143
            empty($restDriver)
144
            || !class_exists($driver)
145
            || !is_subclass_of($driver, $parent)
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $parent can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
146
        ) {
147
            throw new InvalidArgumentException('rest driver', $driver);
148
        }
149
150
        // create a new REST Driver
151
        $this->restDriver = new $driver();
152
        if (isset($this->url)) {
153
            $this->restDriver->setHost($this->url);
154
        }
155
    }
156
157
    /**
158
     * @When I create a :type request to :resource (url)
159
     */
160
    public function createRequest($type, $resource)
161
    {
162
        $this->restDriver->setMethod($type);
163
        $this->restDriver->setResource(
164
            $this->changeMappedValuesOnUrl($resource)
165
        );
166
        $this->responseObject = null;
167
    }
168
169
    /**
170
     * @When I send a :type request to :resource (url)
171
     */
172
    public function createAndSendRequest($type, $resource)
173
    {
174
        $this->createRequest($type, $resource);
175
        $this->restDriver->send();
176
    }
177
178
    /**
179
     * @When I set :header header with :value (value)
180
     */
181
    public function setHeader($header, $value)
182
    {
183
        $this->restDriver->setHeader($header, $value);
184
    }
185
186
    /**
187
     * @When I set headers:
188
     */
189
    public function setHeaders(TableNode $table)
190
    {
191
        $headers = GherkinHelper::convertTableToArrayOfData($table);
192
193
        foreach ($headers as $header => $value) {
0 ignored issues
show
Bug introduced by
The expression $headers of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
194
            $this->iAddHeaderWithValue($header, $value);
195
        }
196
    }
197
198
    /**
199
     * @When I send the request
200
     */
201
    public function sendRequest()
202
    {
203
        $requestObject = $this->getRequestObject();
204
        if (!empty($requestObject)) {
205
            $this->addObjectToRequestBody(
206
                $requestObject,
207
                $this->restBodyType
208
            );
209
        }
210
        $this->restDriver->send();
211
    }
212
213
    /**
214
     * @Then response status code is :code
215
     */
216
    public function assertStatusCode($code)
217
    {
218
        $exceptionMessage = '';
219
        if ($code != $this->restDriver->getStatusCode() && $code >= 200 && $code < 400) {
220
            $errorMessage = $this->getResponseObject();
221
            if ($errorMessage instanceof ErrorMessage) {
222
                $exceptionMessage = <<< EOF
223
224
Server Error ({$errorMessage->code}): {$errorMessage->message}
225
226
{$errorMessage->description}
227
228
In {$errorMessage->file}:{$errorMessage->line}
229
230
{$errorMessage->trace}
231
EOF;
232
            } elseif ($errorMessage instanceof Exception) {
233
                $exceptionMessage = <<< EOF
234
235
Client Exception ({$errorMessage->getCode()}): {$errorMessage->getMessage()}
236
237
In {$errorMessage->getFile()}:{$errorMessage->getLine()}
238
EOF;
239
                // If previous exception is available it is most likely carrying info on server exception.
240
                if ($previous = $errorMessage->getPrevious()) {
241
                    $exceptionName = get_class($previous);
242
                    $exceptionMessage .= <<< EOF
243
244
Previous Exception $exceptionName ({$previous->getCode()}): {$previous->getMessage()}
245
246
In {$previous->getFile()}:{$previous->getLine()}
247
248
{$previous->getTraceAsString()}
249
EOF;
250
                }
251
            }
252
        }
253
254
        Assertion::assertEquals(
255
            $code,
256
            $this->restDriver->getStatusCode(),
257
            "Expected status code '$code' found '{$this->restDriver->getStatusCode()}'$exceptionMessage"
258
        );
259
    }
260
261
    /**
262
     * @Then response status message is :message
263
     */
264
    public function assertStatusMessage($message)
265
    {
266
        Assertion::assertEquals(
267
            strtolower($message),
268
            strtolower($this->restDriver->getStatusMessage()),
269
            "Expected status message '$message' found '{$this->restDriver->getStatusMessage()}'"
270
        );
271
    }
272
273
    /**
274
     * @Then response header :header exist
275
     */
276
    public function existResponseHeader($header)
277
    {
278
        Assertion::assertNotNull(
279
            $this->restDriver->getHeader($header),
280
            "Expected '$header' header not found"
281
        );
282
    }
283
284
    /**
285
     * @Then response header :header don't exist
286
     */
287
    public function dontExistResponseHeader($header)
288
    {
289
        Assertion::assertNull(
290
            $this->restDriver->getHeader($header),
291
            "Unexpected '$header' header found with '{$this->restDriver->getHeader($header)}' value"
292
        );
293
    }
294
295
    /**
296
     * @Then response header :header have :value (value)
297
     */
298
    public function assertHeaderHaveValue($header, $value)
299
    {
300
        Assertion::assertEquals(
301
            $value,
302
            $this->restDriver->getResponseHeader($header),
0 ignored issues
show
Bug introduced by
The method getResponseHeader() does not seem to exist on object<eZ\Bundle\EzPubli...Client\DriverInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
303
            "Expected '$header' header with '$value' found it with '{$this->restDriver->getHeader($header)}' value"
304
        );
305
    }
306
307
    /**
308
     * @Then response header :header don't have :value (value)
309
     */
310
    public function assertHeaderDontHaveValue($header, $value)
311
    {
312
        Assertion::assertNotEquals(
313
            $value,
314
            $this->restDriver->getResponseHeader($header),
0 ignored issues
show
Bug introduced by
The method getResponseHeader() does not seem to exist on object<eZ\Bundle\EzPubli...Client\DriverInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
315
            "Unexpected '$header' header found with '{$this->restDriver->getHeader($header)}' value"
316
        );
317
    }
318
319
    /**
320
     * @Then response body has :value (value)
321
     */
322
    public function responseBodyHasValue($value)
323
    {
324
        Assertion::assertEquals(
325
            $value,
326
            $this->restDriver->getBody(),
327
            "Expected body isn't equal to the actual one."
328
            . "\nExpected: "
329
            . print_r($value, true)
330
            . "\nActual: "
331
            . print_r($this->restDriver->getBody(), true)
332
        );
333
    }
334
}
335