Completed
Push — 7.0 ( 9675c2...dcfe30 )
by André
145:48 queued 105:00
created

TestCase::setSessionInput()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 1
nop 2
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the Functional\TestCase class.
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
namespace eZ\Bundle\EzPublishRestBundle\Tests\Functional;
10
11
use Buzz\Client\Curl;
12
use Nyholm\Psr7\Request as HttpRequest;
13
use PHPUnit\Framework\TestCase as BaseTestCase;
14
use PHPUnit\Framework\ExpectationFailedException;
15
use Psr\Http\Message\RequestInterface;
16
use Psr\Http\Message\ResponseInterface;
17
18
class TestCase extends BaseTestCase
19
{
20
    const X_HTTP_METHOD_OVERRIDE_MAP = [
21
        'PUBLISH' => 'POST',
22
        'MOVE' => 'POST',
23
        'PATCH' => 'PATCH',
24
        'COPY' => 'POST',
25
    ];
26
27
    /**
28
     * @var \Buzz\Client\BuzzClientInterface
29
     */
30
    private $httpClient;
31
32
    /**
33
     * @var string
34
     */
35
    private $httpHost;
36
37
    /**
38
     * @var string
39
     * Basic auth login:password
40
     */
41
    private $httpAuth;
42
43
    protected static $testSuffix;
44
45
    /**
46
     * @var array
47
     */
48
    private $headers = [];
49
50
    /**
51
     * The username to use for login.
52
     * @var string
53
     */
54
    private $loginUsername;
55
56
    /**
57
     * The password to use for login.
58
     * @var string
59
     */
60
    private $loginPassword;
61
62
    /**
63
     * If true, a login request is automatically done during setUp().
64
     * @var bool
65
     */
66
    protected $autoLogin = true;
67
68
    /**
69
     * List of REST contentId (/content/objects/12345) created by tests.
70
     *
71
     * @var array
72
     */
73
    private static $createdContent = array();
74
75
    protected function setUp()
76
    {
77
        parent::setUp();
78
79
        $this->httpHost = getenv('EZP_TEST_REST_HOST') ?: 'localhost';
80
        $this->httpAuth = getenv('EZP_TEST_REST_AUTH') ?: 'admin:publish';
81
        list($this->loginUsername, $this->loginPassword) = explode(':', $this->httpAuth);
82
83
        $this->httpClient = new Curl([
84
            'verify' => false,
85
            'timeout' => 90,
86
            'allow_redirects' => false,
87
        ]);
88
89
        if ($this->autoLogin) {
90
            $session = $this->login();
91
            $this->headers['Cookie'] = sprintf('%s=%s', $session->name, $session->identifier);
92
            $this->headers['X-CSRF-Token'] = $session->csrfToken;
93
        }
94
    }
95
96
    /**
97
     * @param \Psr\Http\Message\RequestInterface $request
98
     *
99
     * @return \Psr\Http\Message\ResponseInterface
100
     *
101
     * @throws \Psr\Http\Client\ClientException
102
     */
103
    public function sendHttpRequest(RequestInterface $request): ResponseInterface
104
    {
105
        return $this->httpClient->sendRequest($request);
106
    }
107
108
    protected function getHttpHost()
109
    {
110
        return $this->httpHost;
111
    }
112
113
    protected function getLoginUsername()
114
    {
115
        return $this->loginUsername;
116
    }
117
118
    protected function getLoginPassword()
119
    {
120
        return $this->loginPassword;
121
    }
122
123
    /**
124
     * Get base URI for \Buzz\Browser based requests.
125
     *
126
     * @return string
127
     */
128
    protected function getBaseURI()
129
    {
130
        return "http://{$this->httpHost}";
131
    }
132
133
    /**
134
     * @param string $method
135
     * @param string $uri
136
     * @param string $contentType
137
     * @param string $acceptType
138
     * @param string $body
139
     *
140
     * @param array $extraHeaders [key => value] array of extra headers
141
     *
142
     * @return \Psr\Http\Message\RequestInterface
143
     */
144
    public function createHttpRequest(
145
        string $method,
146
        string $uri,
147
        string $contentType = '',
148
        string $acceptType = '',
149
        string $body = '',
150
        array $extraHeaders = []
151
    ): RequestInterface {
152
        $headers = array_merge(
153
            $method === 'POST' && $uri === '/api/ezp/v2/user/sessions' ? [] : $this->headers,
154
            [
155
                'Content-Type' => $this->generateMediaTypeString($contentType),
156
                'Accept' => $this->generateMediaTypeString($acceptType),
157
            ]
158
        );
159
160
        if (isset(static::X_HTTP_METHOD_OVERRIDE_MAP[$method])) {
161
            $headers['X-HTTP-Method-Override'] = $method;
162
            $method = static::X_HTTP_METHOD_OVERRIDE_MAP[$method];
163
        }
164
165
        return new HttpRequest(
166
            $method,
167
            $this->httpHost . $uri,
168
            array_merge($headers, $extraHeaders),
169
            $body
170
        );
171
    }
172
173
    protected function assertHttpResponseCodeEquals(ResponseInterface $response, $expected)
174
    {
175
        $responseCode = $response->getStatusCode();
176
        try {
177
            self::assertEquals($expected, $responseCode);
178
        } catch (ExpectationFailedException $e) {
0 ignored issues
show
Bug introduced by
The class PHPUnit\Framework\ExpectationFailedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
179
            $errorMessageString = '';
180
            $contentTypeHeader = $response->hasHeader('Content-Type')
181
                ? $response->getHeader('Content-Type')[0]
182
                : '';
183
            if (strpos($contentTypeHeader, 'application/vnd.ez.api.ErrorMessage+xml') !== false) {
184
                $body = \simplexml_load_string($response->getBody());
185
                $errorMessageString = $this->getHttpResponseCodeErrorMessage($body);
186
            } elseif (strpos($contentTypeHeader, 'application/vnd.ez.api.ErrorMessage+json') !== false) {
187
                $body = json_decode($response->getBody());
188
                $errorMessageString = $this->getHttpResponseCodeErrorMessage($body->ErrorMessage);
189
            }
190
191
            self::assertEquals($expected, $responseCode, $errorMessageString);
192
        }
193
    }
194
195
    private function getHttpResponseCodeErrorMessage($errorMessage)
196
    {
197
        $errorMessageString = <<< EOF
198
Server error message ({$errorMessage->errorCode}): {$errorMessage->errorMessage}
199
200
{$errorMessage->errorDescription}
201
202
EOF;
203
204
        // If server is in debug mode it will return file, line and trace.
205
        if (!empty($errorMessage->file)) {
206
            $errorMessageString .= "\nIn {$errorMessage->file}:{$errorMessage->line}\n\n{$errorMessage->trace}";
207
        } else {
208
            $errorMessageString .= "\nIn \<no trace, debug disabled\>";
209
        }
210
211
        return $errorMessageString;
212
    }
213
214
    protected function assertHttpResponseHasHeader(ResponseInterface $response, $header, $expectedValue = null)
215
    {
216
        $headerValue = $response->hasHeader($header) ? $response->getHeader($header)[0] : null;
217
        self::assertNotNull($headerValue, "Failed asserting that response has a {$header} header");
218
        if ($expectedValue !== null) {
219
            self::assertEquals($expectedValue, $headerValue);
220
        }
221
    }
222
223
    protected function generateMediaTypeString($typeString)
224
    {
225
        return "application/vnd.ez.api.$typeString";
226
    }
227
228
    protected function getMediaFromTypeString($typeString)
229
    {
230
        $prefix = 'application/vnd.ez.api.';
231
        self::assertStringStartsWith(
232
            $prefix,
233
            $typeString,
234
            "Unknown media: {$typeString}"
235
        );
236
237
        return substr($typeString, strlen($prefix));
238
    }
239
240
    protected function addCreatedElement($href)
241
    {
242
        $testCase = $this;
243
        self::$createdContent[$href] = function () use ($href, $testCase) {
244
            $testCase->sendHttpRequest(
245
                $testCase->createHttpRequest('DELETE', $href)
246
            );
247
        };
248
    }
249
250
    public static function tearDownAfterClass()
251
    {
252
        self::clearCreatedElement(self::$createdContent);
253
    }
254
255
    private static function clearCreatedElement(array $contentArray)
256
    {
257
        foreach (array_reverse($contentArray) as $href => $callback) {
258
            $callback();
259
        }
260
    }
261
262
    /**
263
     * @param string $parentLocationId The REST id of the parent location
264
     *
265
     * @return array created Content, as an array
266
     */
267
    protected function createFolder($string, $parentLocationId)
268
    {
269
        $string = $this->addTestSuffix($string);
270
        $remoteId = md5(uniqid($string, true));
271
        $xml = <<< XML
272
<?xml version="1.0" encoding="UTF-8"?>
273
<ContentCreate>
274
  <ContentType href="/api/ezp/v2/content/types/1" />
275
  <mainLanguageCode>eng-GB</mainLanguageCode>
276
  <LocationCreate>
277
    <ParentLocation href="{$parentLocationId}" />
278
    <priority>0</priority>
279
    <hidden>false</hidden>
280
    <sortField>PATH</sortField>
281
    <sortOrder>ASC</sortOrder>
282
  </LocationCreate>
283
  <Section href="/api/ezp/v2/content/sections/1" />
284
  <alwaysAvailable>true</alwaysAvailable>
285
  <remoteId>{$remoteId}</remoteId>
286
  <User href="/api/ezp/v2/user/users/14" />
287
  <modificationDate>2012-09-30T12:30:00</modificationDate>
288
  <fields>
289
    <field>
290
      <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier>
291
      <languageCode>eng-GB</languageCode>
292
      <fieldValue>{$string}</fieldValue>
293
    </field>
294
  </fields>
295
</ContentCreate>
296
XML;
297
298
        return $this->createContent($xml);
299
    }
300
301
    /**
302
     * @param $xml
303
     *
304
     * @return array Content key of the Content struct array
305
     */
306
    protected function createContent($xml)
307
    {
308
        $request = $this->createHttpRequest(
309
            'POST',
310
            '/api/ezp/v2/content/objects',
311
            'ContentCreate+xml',
312
            'Content+json',
313
            $xml
314
        );
315
        $response = $this->sendHttpRequest($request);
316
317
        self::assertHttpResponseCodeEquals($response, 201);
318
319
        $content = json_decode($response->getBody(), true);
320
321
        if (!isset($content['Content']['CurrentVersion']['Version'])) {
322
            self::fail("Incomplete response (no version):\n" . $response->getBody() . "\n");
323
        }
324
325
        $response = $this->sendHttpRequest(
326
            $request = $this->createHttpRequest('PUBLISH', $content['Content']['CurrentVersion']['Version']['_href'])
327
        );
328
329
        self::assertHttpResponseCodeEquals($response, 204);
330
331
        $this->addCreatedElement($content['Content']['_href']);
332
333
        return $content['Content'];
334
    }
335
336
    /**
337
     * @param string $contentHref
338
     *
339
     * @return array
340
     */
341 View Code Duplication
    protected function getContentLocations($contentHref)
342
    {
343
        $response = $this->sendHttpRequest(
344
            $this->createHttpRequest('GET', "$contentHref/locations", '', 'LocationList+json')
345
        );
346
        self::assertHttpResponseCodeEquals($response, 200);
347
        $folderLocations = json_decode($response->getBody(), true);
348
349
        return $folderLocations;
350
    }
351
352
    protected function addTestSuffix($string)
353
    {
354
        if (!isset(self::$testSuffix)) {
355
            self::$testSuffix = uniqid();
356
        }
357
358
        return $string . '_' . self::$testSuffix;
359
    }
360
361
    /**
362
     * Sends a login request to the REST server.
363
     *
364
     * @return \stdClass an object with the name, identifier, csrftoken properties.
365
     */
366
    protected function login()
367
    {
368
        $request = $this->createAuthenticationHttpRequest($this->getLoginUsername(), $this->getLoginPassword());
369
        $response = $this->sendHttpRequest($request);
370
        self::assertHttpResponseCodeEquals($response, 201);
371
372
        return json_decode($response->getBody())->Session;
373
    }
374
375
    /**
376
     * @param string $login
377
     * @param string $password
378
     * @param array $extraHeaders extra [key => value] headers to be passed with the authentication request.
379
     *
380
     * @return \Psr\Http\Message\RequestInterface
381
     */
382
    protected function createAuthenticationHttpRequest(string $login, string $password, array $extraHeaders = [])
383
    {
384
        return $this->createHttpRequest(
385
            'POST',
386
            '/api/ezp/v2/user/sessions',
387
            'SessionInput+json',
388
            'Session+json',
389
            sprintf('{"SessionInput": {"login": "%s", "password": "%s"}}', $login, $password),
390
            $extraHeaders
391
        );
392
    }
393
}
394