Completed
Push — ezp26297-rest_embedding_http_c... ( 6dcd26...7b4207 )
by
unknown
44:13 queued 12:57
created

TestCase::extractPathFromHref()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 11
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
 * @version //autogentag//
10
 */
11
namespace eZ\Bundle\EzPublishRestBundle\Tests\Functional;
12
13
use Buzz\Message\Request as HttpRequest;
14
use Buzz\Message\Response as HttpResponse;
15
use Buzz\Message\Response;
16
use PHPUnit_Framework_TestCase;
17
18
class TestCase extends PHPUnit_Framework_TestCase
19
{
20
    /**
21
     * @var \Buzz\Client\ClientInterface
22
     */
23
    private $httpClient;
24
25
    /**
26
     * @var string
27
     */
28
    private $httpHost;
29
30
    /**
31
     * @var string
32
     * Basic auth login:password
33
     */
34
    private $httpAuth;
35
36
    protected static $testSuffix;
37
38
    /**
39
     * @var array
40
     */
41
    private $headers = [];
42
43
    /**
44
     * The username to use for login.
45
     * @var string
46
     */
47
    private $loginUsername;
48
49
    /**
50
     * The password to use for login.
51
     * @var string
52
     */
53
    private $loginPassword;
54
55
    /**
56
     * If true, a login request is automatically done during setUp().
57
     * @var bool
58
     */
59
    protected $autoLogin = true;
60
61
    /**
62
     * List of REST contentId (/content/objects/12345) created by tests.
63
     *
64
     * @var array
65
     */
66
    private static $createdContent = array();
67
68
    protected function setUp()
69
    {
70
        parent::setUp();
71
72
        $this->httpHost = getenv('EZP_TEST_REST_HOST') ?: 'localhost';
73
        $this->httpAuth = getenv('EZP_TEST_REST_AUTH') ?: 'admin:publish';
74
        list($this->loginUsername, $this->loginPassword) = explode(':', $this->httpAuth);
75
76
        $this->httpClient = new \Buzz\Client\Curl();
77
        $this->httpClient->setVerifyPeer(false);
78
        $this->httpClient->setTimeout(90);
79
        $this->httpClient->setOption(CURLOPT_FOLLOWLOCATION, false);
80
81
        if ($this->autoLogin) {
82
            $session = $this->login();
83
            $this->headers[] = sprintf('Cookie: %s=%s', $session->name, $session->identifier);
84
            $this->headers[] = sprintf('X-CSRF-Token: %s', $session->csrfToken);
85
        }
86
    }
87
88
    /**
89
     * @return HttpResponse
90
     */
91
    public function sendHttpRequest(HttpRequest $request)
92
    {
93
        $response = new HttpResponse();
94
        $this->httpClient->send($request, $response);
95
96
        return $response;
97
    }
98
99
    protected function getHttpHost()
100
    {
101
        return $this->httpHost;
102
    }
103
104
    protected function getLoginUsername()
105
    {
106
        return $this->loginUsername;
107
    }
108
109
    protected function getLoginPassword()
110
    {
111
        return $this->loginPassword;
112
    }
113
114
    /**
115
     * @return HttpRequest
116
     */
117
    public function createHttpRequest($method, $uri, $contentType = '', $acceptType = '')
118
    {
119
        $headers = array_merge(
120
            $method === 'POST' && $uri === '/api/ezp/v2/user/sessions' ? [] : $this->headers,
121
            [
122
                'Content-Type: ' . $this->generateMediaTypeString($contentType),
123
                'Accept: ' . $this->generateMediaTypeString($acceptType),
124
            ]
125
        );
126
127
        switch ($method) {
128
            case 'PUBLISH': $method = 'POST';  $headers[] = 'X-HTTP-Method-Override: PUBLISH'; break;
129
            case 'MOVE':    $method = 'POST';  $headers[] = 'X-HTTP-Method-Override: MOVE';    break;
130
            case 'PATCH':   $method = 'PATCH'; $headers[] = 'X-HTTP-Method-Override: PATCH';   break;
131
            case 'COPY':    $method = 'POST';  $headers[] = 'X-HTTP-Method-Override: COPY';    break;
132
        }
133
134
        $request = new HttpRequest($method, $uri, $this->httpHost);
135
        $request->addHeaders($headers);
136
137
        return $request;
138
    }
139
140
    protected function assertHttpResponseCodeEquals(HttpResponse $response, $expected)
141
    {
142
        $responseCode = $response->getStatusCode();
143
        if ($responseCode != $expected) {
144
            $errorMessageString = '';
145
            if (strpos($response->getHeader('Content-Type'), 'application/vnd.ez.api.ErrorMessage+xml') !== false) {
146
                $body = \simplexml_load_string($response->getContent());
147
                $errorMessageString = $this->getHttpResponseCodeErrorMessage($body);
148
            } elseif (strpos($response->getHeader('Content-Type'), 'application/vnd.ez.api.ErrorMessage+json') !== false) {
149
                $body = json_decode($response->getContent());
150
                $errorMessageString = $this->getHttpResponseCodeErrorMessage($body->ErrorMessage);
151
            }
152
153
            self::assertEquals($expected, $responseCode, $errorMessageString);
154
        }
155
    }
156
157
    private function getHttpResponseCodeErrorMessage($errorMessage)
158
    {
159
        $errorMessageString = <<< EOF
160
Server error message ({$errorMessage->errorCode}): {$errorMessage->errorMessage}
161
162
{$errorMessage->errorDescription}
163
164
EOF;
165
166
        // If server is in debug mode it will return file, line and trace.
167
        if (!empty($errorMessage->file)) {
168
            $errorMessageString .= "\nIn {$errorMessage->file}:{$errorMessage->line}\n\n{$errorMessage->trace}";
169
        } else {
170
            $errorMessageString .= "\nIn \<no trace, debug disabled\>";
171
        }
172
173
        return $errorMessageString;
174
    }
175
176
    protected function assertHttpResponseHasHeader(HttpResponse $response, $header, $expectedValue = null)
177
    {
178
        $headerValue = $response->getHeader($header);
179
        self::assertNotNull($headerValue, "Failed asserting that response has a $header header");
180
        if ($expectedValue !== null) {
181
            self::assertEquals($expectedValue, $headerValue);
182
        }
183
    }
184
185
    protected function generateMediaTypeString($typeString)
186
    {
187
        return "application/vnd.ez.api.$typeString";
188
    }
189
190
    protected function addCreatedElement($href)
191
    {
192
        $testCase = $this;
193
        self::$createdContent[$href] = function () use ($href, $testCase) {
194
            $testCase->sendHttpRequest(
195
                $testCase->createHttpRequest('DELETE', $href)
196
            );
197
        };
198
    }
199
200
    public static function tearDownAfterClass()
201
    {
202
        self::clearCreatedElement(self::$createdContent);
0 ignored issues
show
Unused Code introduced by
The call to the method eZ\Bundle\EzPublishRestB...::clearCreatedElement() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
203
    }
204
205
    private static function clearCreatedElement(array $contentArray)
0 ignored issues
show
Unused Code introduced by
The parameter $contentArray is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
206
    {
207
        /*foreach (array_reverse($contentArray) as $href => $callback) {
208
            $callback();
209
        }*/
210
    }
211
212
    /**
213
     * @param string $parentLocationId The REST id of the parent location
214
     *
215
     * @return array created Content, as an array
216
     */
217
    protected function createFolder($string, $parentLocationId)
218
    {
219
        $string = $this->addTestSuffix($string);
220
        $xml = <<< XML
221
<?xml version="1.0" encoding="UTF-8"?>
222
<ContentCreate>
223
  <ContentType href="/api/ezp/v2/content/types/1" />
224
  <mainLanguageCode>eng-GB</mainLanguageCode>
225
  <LocationCreate>
226
    <ParentLocation href="{$parentLocationId}" />
227
    <priority>0</priority>
228
    <hidden>false</hidden>
229
    <sortField>PATH</sortField>
230
    <sortOrder>ASC</sortOrder>
231
  </LocationCreate>
232
  <Section href="/api/ezp/v2/content/sections/1" />
233
  <alwaysAvailable>true</alwaysAvailable>
234
  <remoteId>{$string}</remoteId>
235
  <User href="/api/ezp/v2/user/users/14" />
236
  <modificationDate>2012-09-30T12:30:00</modificationDate>
237
  <fields>
238
    <field>
239
      <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier>
240
      <languageCode>eng-GB</languageCode>
241
      <fieldValue>{$string}</fieldValue>
242
    </field>
243
  </fields>
244
</ContentCreate>
245
XML;
246
247
        return $this->createContent($xml);
248
    }
249
250
    /**
251
     * @param $xml
252
     *
253
     * @return array Content key of the Content struct array
254
     */
255
    protected function createContent($xml)
256
    {
257
        $request = $this->createHttpRequest('POST', '/api/ezp/v2/content/objects', 'ContentCreate+xml', 'Content+json');
258
        $request->setContent($xml);
259
260
        $response = $this->sendHttpRequest($request);
261
262
        self::assertHttpResponseCodeEquals($response, 201);
263
264
        $content = json_decode($response->getContent(), true);
265
266
        if (!isset($content['Content']['CurrentVersion']['Version'])) {
267
            self::fail("Incomplete response (no version):\n" . $response->getContent() . "\n");
268
        }
269
270
        $response = $this->sendHttpRequest(
271
            $request = $this->createHttpRequest('PUBLISH', $content['Content']['CurrentVersion']['Version']['_href'])
272
        );
273
274
        self::assertHttpResponseCodeEquals($response, 204);
275
276
        $this->addCreatedElement($content['Content']['_href'], true);
277
278
        return $content['Content'];
279
    }
280
281
    /**
282
     * @param string $contentHref
283
     *
284
     * @return array
285
     */
286 View Code Duplication
    protected function getContentLocations($contentHref)
287
    {
288
        $response = $this->sendHttpRequest(
289
            $this->createHttpRequest('GET', "$contentHref/locations", '', 'LocationList+json')
290
        );
291
        self::assertHttpResponseCodeEquals($response, 200);
292
        $folderLocations = json_decode($response->getContent(), true);
293
294
        return $folderLocations;
295
    }
296
297
    protected function addTestSuffix($string)
298
    {
299
        if (!isset(self::$testSuffix)) {
300
            self::$testSuffix = uniqid();
301
        }
302
303
        return $string . '_' . self::$testSuffix;
304
    }
305
306
    /**
307
     * Sends a login request to the REST server.
308
     *
309
     * @return \stdClass an object with the name, identifier, csrftoken properties.
310
     */
311 View Code Duplication
    protected function login()
312
    {
313
        $request = $this->createHttpRequest('POST', '/api/ezp/v2/user/sessions', 'SessionInput+json', 'Session+json');
314
        $this->setSessionInput($request);
315
        $response = $this->sendHttpRequest($request);
316
        self::assertHttpResponseCodeEquals($response, 201);
317
318
        return json_decode($response->getContent())->Session;
319
    }
320
321
    /**
322
     * Sets the request's content to a JSON session creation payload.
323
     *
324
     * @param HttpRequest $request
325
     * @param string $password The password to use in the input. Will use the default one if not set.
326
     *
327
     * @return string
328
     */
329
    protected function setSessionInput(HttpRequest $request, $password = null)
330
    {
331
        $request->setContent(
332
            sprintf('{"SessionInput": {"login": "admin", "password": "%s"}}', $password ?: $this->loginPassword)
333
        );
334
    }
335
336
    /**
337
     * Asserts that $response has the given set of $cacheTags.
338
     *
339
     * @param Response $response
340
     * @param array $expectedTags Example: ['content-42', 'location-300']
341
     */
342
    protected function assertHttpResponseHasCacheTags(Response $response, $expectedTags)
343
    {
344
        $this->assertHttpResponseHasHeader($response, 'xkey');
345
346
        $responseCacheTag = $response->getHeader('xkey');
347
        foreach ($expectedTags as $expectedTag) {
348
            $this->assertContains($expectedTag, $responseCacheTag);
349
        }
350
    }
351
352
    /**
353
     * Extracts and returns the last id from $href.
354
     *
355
     * @param string $href Ex: '/api/ezp/v2/content/objects/1'
356
     * @return int Ex: 1
357
     */
358
    protected function extractLastIdFromHref($href)
359
    {
360
        $contentTypeHrefParts = explode('/', $href);
361
362
        return (int)array_pop($contentTypeHrefParts);
363
    }
364
365
    protected function extractPathFromHref($href)
366
    {
367
        $parts = array_filter(
368
            explode('/', str_replace('/api/ezp/v2/', '', $href)),
369
            function ($value) {
370
                return is_numeric($value);
371
            }
372
        );
373
374
        return $parts;
375
    }
376
377
    /**
378
     * Extracts a content id from any href containing one.
379
     *
380
     * @param string $href Ex: /api/ezp/v2/content/objects/1/anything
381
     * @return int
382
     */
383
    protected function extractContentIdFromHref($href)
384
    {
385
        $contentId = null;
386
        $leftOvers = null;
387
388
        sscanf(
389
            $href,
390
            '/api/ezp/v2/content/objects/%d/%s',
391
            $contentId,
392
            $leftOvers
393
        );
394
395
        return $contentId;
396
    }
397
}
398