Completed
Push — 6.13 ( 055733...f64074 )
by André
23:24 queued 09:44
created

TestCase::getHttpHost()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
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\Message\Request as HttpRequest;
12
use Buzz\Message\Response as HttpResponse;
13
use PHPUnit\Framework\TestCase as BaseTestCase;
14
15
class TestCase extends BaseTestCase
16
{
17
    /**
18
     * @var \Buzz\Client\ClientInterface
19
     */
20
    private $httpClient;
21
22
    /**
23
     * @var string
24
     */
25
    private $httpHost;
26
27
    /**
28
     * @var string
29
     * Basic auth login:password
30
     */
31
    private $httpAuth;
32
33
    protected static $testSuffix;
34
35
    /**
36
     * @var array
37
     */
38
    private $headers = [];
39
40
    /**
41
     * The username to use for login.
42
     * @var string
43
     */
44
    private $loginUsername;
45
46
    /**
47
     * The password to use for login.
48
     * @var string
49
     */
50
    private $loginPassword;
51
52
    /**
53
     * If true, a login request is automatically done during setUp().
54
     * @var bool
55
     */
56
    protected $autoLogin = true;
57
58
    /**
59
     * List of REST contentId (/content/objects/12345) created by tests.
60
     *
61
     * @var array
62
     */
63
    private static $createdContent = array();
64
65
    protected function setUp()
66
    {
67
        parent::setUp();
68
69
        $this->httpHost = getenv('EZP_TEST_REST_HOST') ?: 'localhost';
70
        $this->httpAuth = getenv('EZP_TEST_REST_AUTH') ?: 'admin:publish';
71
        list($this->loginUsername, $this->loginPassword) = explode(':', $this->httpAuth);
72
73
        $this->httpClient = new \Buzz\Client\Curl();
74
        $this->httpClient->setVerifyPeer(false);
75
        $this->httpClient->setTimeout(90);
76
        $this->httpClient->setOption(CURLOPT_FOLLOWLOCATION, false);
77
78
        if ($this->autoLogin) {
79
            $session = $this->login();
80
            $this->headers[] = sprintf('Cookie: %s=%s', $session->name, $session->identifier);
81
            $this->headers[] = sprintf('X-CSRF-Token: %s', $session->csrfToken);
82
        }
83
    }
84
85
    /**
86
     * @return HttpResponse
87
     */
88
    public function sendHttpRequest(HttpRequest $request)
89
    {
90
        $response = new HttpResponse();
91
        $this->httpClient->send($request, $response);
92
93
        return $response;
94
    }
95
96
    protected function getHttpHost()
97
    {
98
        return $this->httpHost;
99
    }
100
101
    protected function getLoginUsername()
102
    {
103
        return $this->loginUsername;
104
    }
105
106
    protected function getLoginPassword()
107
    {
108
        return $this->loginPassword;
109
    }
110
111
    /**
112
     * @return HttpRequest
113
     */
114
    public function createHttpRequest($method, $uri, $contentType = '', $acceptType = '')
115
    {
116
        $headers = array_merge(
117
            $method === 'POST' && $uri === '/api/ezp/v2/user/sessions' ? [] : $this->headers,
118
            [
119
                'Content-Type: ' . $this->generateMediaTypeString($contentType),
120
                'Accept: ' . $this->generateMediaTypeString($acceptType),
121
            ]
122
        );
123
124
        switch ($method) {
125
            case 'PUBLISH': $method = 'POST'; $headers[] = 'X-HTTP-Method-Override: PUBLISH'; break;
126
            case 'MOVE':    $method = 'POST'; $headers[] = 'X-HTTP-Method-Override: MOVE'; break;
127
            case 'PATCH':   $method = 'PATCH'; $headers[] = 'X-HTTP-Method-Override: PATCH'; break;
128
            case 'COPY':    $method = 'POST'; $headers[] = 'X-HTTP-Method-Override: COPY'; break;
129
        }
130
131
        $request = new HttpRequest($method, $uri, $this->httpHost);
132
        $request->addHeaders($headers);
133
134
        return $request;
135
    }
136
137
    protected function assertHttpResponseCodeEquals(HttpResponse $response, $expected)
138
    {
139
        $responseCode = $response->getStatusCode();
140
        if ($responseCode != $expected) {
141
            $errorMessageString = '';
142
            if (strpos($response->getHeader('Content-Type'), 'application/vnd.ez.api.ErrorMessage+xml') !== false) {
143
                $body = \simplexml_load_string($response->getContent());
144
                $errorMessageString = $this->getHttpResponseCodeErrorMessage($body);
145
            } elseif (strpos($response->getHeader('Content-Type'), 'application/vnd.ez.api.ErrorMessage+json') !== false) {
146
                $body = json_decode($response->getContent());
147
                $errorMessageString = $this->getHttpResponseCodeErrorMessage($body->ErrorMessage);
148
            }
149
150
            self::assertEquals($expected, $responseCode, $errorMessageString);
151
        }
152
    }
153
154
    private function getHttpResponseCodeErrorMessage($errorMessage)
155
    {
156
        $errorMessageString = <<< EOF
157
Server error message ({$errorMessage->errorCode}): {$errorMessage->errorMessage}
158
159
{$errorMessage->errorDescription}
160
161
EOF;
162
163
        // If server is in debug mode it will return file, line and trace.
164
        if (!empty($errorMessage->file)) {
165
            $errorMessageString .= "\nIn {$errorMessage->file}:{$errorMessage->line}\n\n{$errorMessage->trace}";
166
        } else {
167
            $errorMessageString .= "\nIn \<no trace, debug disabled\>";
168
        }
169
170
        return $errorMessageString;
171
    }
172
173
    protected function assertHttpResponseHasHeader(HttpResponse $response, $header, $expectedValue = null)
174
    {
175
        $headerValue = $response->getHeader($header);
176
        self::assertNotNull($headerValue, "Failed asserting that response has a $header header");
177
        if ($expectedValue !== null) {
178
            self::assertEquals($expectedValue, $headerValue);
179
        }
180
    }
181
182
    protected function generateMediaTypeString($typeString)
183
    {
184
        return "application/vnd.ez.api.$typeString";
185
    }
186
187
    protected function getMediaFromTypeString($typeString)
188
    {
189
        $prefix = 'application/vnd.ez.api.';
190
        self::assertStringStartsWith(
191
            $prefix,
192
            $typeString,
193
            "Unknown media: {$typeString}"
194
        );
195
196
        return substr($typeString, strlen($prefix));
197
    }
198
199
    protected function addCreatedElement($href)
200
    {
201
        $testCase = $this;
202
        self::$createdContent[$href] = function () use ($href, $testCase) {
203
            $testCase->sendHttpRequest(
204
                $testCase->createHttpRequest('DELETE', $href)
205
            );
206
        };
207
    }
208
209
    public static function tearDownAfterClass()
210
    {
211
        self::clearCreatedElement(self::$createdContent);
212
    }
213
214
    private static function clearCreatedElement(array $contentArray)
215
    {
216
        foreach (array_reverse($contentArray) as $href => $callback) {
217
            $callback();
218
        }
219
    }
220
221
    /**
222
     * @param string $parentLocationId The REST id of the parent location
223
     *
224
     * @return array created Content, as an array
225
     */
226
    protected function createFolder($string, $parentLocationId)
227
    {
228
        $string = $this->addTestSuffix($string);
229
        $xml = <<< XML
230
<?xml version="1.0" encoding="UTF-8"?>
231
<ContentCreate>
232
  <ContentType href="/api/ezp/v2/content/types/1" />
233
  <mainLanguageCode>eng-GB</mainLanguageCode>
234
  <LocationCreate>
235
    <ParentLocation href="{$parentLocationId}" />
236
    <priority>0</priority>
237
    <hidden>false</hidden>
238
    <sortField>PATH</sortField>
239
    <sortOrder>ASC</sortOrder>
240
  </LocationCreate>
241
  <Section href="/api/ezp/v2/content/sections/1" />
242
  <alwaysAvailable>true</alwaysAvailable>
243
  <remoteId>{$string}</remoteId>
244
  <User href="/api/ezp/v2/user/users/14" />
245
  <modificationDate>2012-09-30T12:30:00</modificationDate>
246
  <fields>
247
    <field>
248
      <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier>
249
      <languageCode>eng-GB</languageCode>
250
      <fieldValue>{$string}</fieldValue>
251
    </field>
252
  </fields>
253
</ContentCreate>
254
XML;
255
256
        return $this->createContent($xml);
257
    }
258
259
    /**
260
     * @param $xml
261
     *
262
     * @return array Content key of the Content struct array
263
     */
264
    protected function createContent($xml)
265
    {
266
        $request = $this->createHttpRequest('POST', '/api/ezp/v2/content/objects', 'ContentCreate+xml', 'Content+json');
267
        $request->setContent($xml);
268
269
        $response = $this->sendHttpRequest($request);
270
271
        self::assertHttpResponseCodeEquals($response, 201);
272
273
        $content = json_decode($response->getContent(), true);
274
275
        if (!isset($content['Content']['CurrentVersion']['Version'])) {
276
            self::fail("Incomplete response (no version):\n" . $response->getContent() . "\n");
277
        }
278
279
        $response = $this->sendHttpRequest(
280
            $request = $this->createHttpRequest('PUBLISH', $content['Content']['CurrentVersion']['Version']['_href'])
281
        );
282
283
        self::assertHttpResponseCodeEquals($response, 204);
284
285
        $this->addCreatedElement($content['Content']['_href'], true);
286
287
        return $content['Content'];
288
    }
289
290
    /**
291
     * @param string $contentHref
292
     *
293
     * @return array
294
     */
295 View Code Duplication
    protected function getContentLocations($contentHref)
296
    {
297
        $response = $this->sendHttpRequest(
298
            $this->createHttpRequest('GET', "$contentHref/locations", '', 'LocationList+json')
299
        );
300
        self::assertHttpResponseCodeEquals($response, 200);
301
        $folderLocations = json_decode($response->getContent(), true);
302
303
        return $folderLocations;
304
    }
305
306
    protected function addTestSuffix($string)
307
    {
308
        if (!isset(self::$testSuffix)) {
309
            self::$testSuffix = uniqid();
310
        }
311
312
        return $string . '_' . self::$testSuffix;
313
    }
314
315
    /**
316
     * Sends a login request to the REST server.
317
     *
318
     * @return \stdClass an object with the name, identifier, csrftoken properties.
319
     */
320 View Code Duplication
    protected function login()
321
    {
322
        $request = $this->createHttpRequest('POST', '/api/ezp/v2/user/sessions', 'SessionInput+json', 'Session+json');
323
        $this->setSessionInput($request);
324
        $response = $this->sendHttpRequest($request);
325
        self::assertHttpResponseCodeEquals($response, 201);
326
327
        return json_decode($response->getContent())->Session;
328
    }
329
330
    /**
331
     * Sets the request's content to a JSON session creation payload.
332
     *
333
     * @param HttpRequest $request
334
     * @param string $password The password to use in the input. Will use the default one if not set.
335
     *
336
     * @return string
337
     */
338
    protected function setSessionInput(HttpRequest $request, $password = null)
339
    {
340
        $request->setContent(
341
            sprintf('{"SessionInput": {"login": "admin", "password": "%s"}}', $password ?: $this->loginPassword)
342
        );
343
    }
344
}
345