Completed
Push — master ( c5de01...c9c19d )
by
unknown
07:34 queued 05:10
created

TutuContext::thereIsAFileWithFollowingContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
use Behat\Behat\Context\SnippetAcceptingContext;
4
use Behat\Behat\Hook\Scope\AfterScenarioScope;
5
use Behat\Gherkin\Node\PyStringNode;
6
use Behat\Gherkin\Node\TableNode;
7
use Behat\MinkExtension\Context\RawMinkContext;
8
use Behat\Testwork\Tester\Result\TestResult;
9
use GuzzleHttp\Exception\RequestException;
10
use Symfony\Component\Filesystem\Filesystem;
11
use Symfony\Component\Process\Process;
12
use Symfony\Component\Process\ProcessBuilder;
13
14
class TutuContext extends RawMinkContext implements SnippetAcceptingContext
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
15
{
16
    const MAX_PHP_SERVER_RESTARTS = 5;
17
18
    /**
19
     * @var Process
20
     */
21
    private $tutuProcess;
22
23
    /**
24
     * @var int
25
     */
26
    private $tutuPort;
27
28
    /**
29
     * @var string;
30
     */
31
    private $tutuHost;
32
33
    /**
34
     * @var string
35
     */
36
    private $webPath;
37
38
    /**
39
     * @var string
40
     */
41
    private $workDir;
42
43
    /**
44
     * @var int
45
     */
46
    private $httpCallAttemptsPerScenario = 0;
47
48
    public function __construct($webPath)
49
    {
50
        if (!file_exists($webPath)) {
51
            throw new \InvalidArgumentException(sprintf("Path %s does not exist", $webPath));
52
        }
53
        $this->webPath = $webPath;
54
        $this->headersToRemove = [];
55
    }
56
57
    /**
58
     * @BeforeScenario
59
     */
60
    public function createWorkDir()
61
    {
62
        $this->workDir = sprintf(
63
            '%s/%s',
64
            sys_get_temp_dir(),
65
            uniqid('TuTuContext')
66
        );
67
        $fs = new Filesystem();
68
        $fs->mkdir($this->workDir, 0777);
69
        chdir($this->workDir);
70
        $fs->mkdir($this->workDir . '/resources');
71
        $fs->mkdir($this->workDir . '/config');
72
        $fs->dumpFile($this->workDir . '/config/responses.yml', '');
73
    }
74
75
    /** @AfterScenario */
76
    public function printLastResponseWhenScenarioFail(AfterScenarioScope $scope)
77
    {
78
        if ($scope->getTestResult()->getResultCode() === TestResult::FAILED) {
79
            echo "Last response:\n";
80
            echo "Code " . $this->getSession()->getStatusCode() . "\n";
81
            $this->printLastResponse();
82
        }
83
    }
84
85
    /** @AfterScenario */
86
    public function resetHttpCallAttempts()
87
    {
88
        $this->httpCallAttemptsPerScenario = 0;
89
    }
90
91
    /**
92
     * @AfterScenario
93
     */
94
    public function removeWorkDir()
95
    {
96
        $fs = new Filesystem();
97
        $fs->remove($this->workDir);
98
    }
99
100
    /**
101
     * @AfterScenario
102
     */
103
    public function killEveryProcessRunningOnTuTuPort()
104
    {
105
        $killerProcess = new Process(sprintf("kill $(lsof -t -i:%d)", (int) $this->tutuPort));
106
        $killerProcess->run();
107
    }
108
109
    /**
110
     * @Given there is a empty responses config file :fileName
111
     */
112
    public function thereIsAEmptyFile($fileName)
113
    {
114
        $this->thereIsARoutingFileWithFollowingContent($fileName, new PyStringNode([], 0));
115
    }
116
117
    /**
118
     * @Given there is a resource file :fileName with following content
119
     */
120
    public function thereIsAResourceFileWithFollowingContent($fileName, PyStringNode $fileContent)
121
    {
122
        $fs = new Filesystem();
123
        $resourceFilePath = $this->workDir . '/resources/' . $fileName;
124
        if ($fs->exists($resourceFilePath)) {
125
            $fs->remove($resourceFilePath);
126
        }
127
128
        $fs->dumpFile($resourceFilePath, (string) $fileContent);
129
    }
130
131
    /**
132
     * @Given there is a :filePath file with following content
133
     */
134
    public function thereIsAFileWithFollowingContent($filePath, PyStringNode $fileContent)
135
    {
136
        $fs = new Filesystem();
137
        $fs->dumpFile($this->workDir . '/' . $filePath, (string) $fileContent);
138
    }
139
140
    /**
141
     * @Given there is a routing file :fileName with following content:
142
     * @Given there is a responses config file :fileName with following content:
143
     * @Given there is a config file :fileName with following content:
144
     */
145
    public function thereIsARoutingFileWithFollowingContent($fileName, PyStringNode $fileContent)
146
    {
147
        $fs = new Filesystem();
148
        $configFilePath = $this->workDir . '/config/' . $fileName;
149
        if ($fs->exists($configFilePath)) {
150
            $fs->remove($configFilePath);
151
        }
152
153
        $content = (string) $fileContent;
154
        $content = str_replace('%workDir%', $this->workDir, $content);
155
156
        $fs->dumpFile($configFilePath, $content);
157
    }
158
159
    /**
160
     * @Given TuTu is running on host :host at port :port
161
     */
162
    public function tutuIsRunningOnHostAtPort($host, $port)
163
    {
164
        $this->tutuPort = $port;
165
        $this->tutuHost = $host;
166
        $this->killEveryProcessRunningOnTuTuPort();
167
        $builder = new ProcessBuilder([
168
            PHP_BINARY,
169
            '-S',
170
            sprintf('%s:%s > /Users/norzechowicz/Workspace/PHP/coduo/TuTu/log.txt', $host, $port)
171
        ]);
172
173
        if (file_exists($this->workDir . '/config/config.yml')) {
174
            $builder->setEnv('tutu_config', $this->workDir . '/config/config.yml');
175
        }
176
177
        $builder->setEnv('tutu_responses', $this->workDir . '/config/responses.yml');
178
        $builder->setEnv('tutu_resources', $this->workDir . '/resources');
179
        $builder->setWorkingDirectory($this->webPath);
180
        $builder->setTimeout(null);
181
182
        $this->tutuProcess = $builder->getProcess();
183
        $this->tutuProcess->start();
184
        sleep(1);
185
    }
186
187
    /**
188
     * @Then print last response
189
     */
190
    public function printLastResponse()
191
    {
192
        $content = $this->getSession()->getDriver()->getContent();
193
194
        echo "\n\033[36m|  " . strtr($content, array("\n" => "\n|  ")) . "\033[0m\n\n";
195
    }
196
197
    /**
198
     * @Then response status code should be :expectedStatus
199
     */
200
    public function responseStatusCodeShouldBe($expectedStatus)
201
    {
202
        $status = $this->getSession()->getStatusCode();
203
        if ($status !== (int)$expectedStatus) {
204
            throw new \RuntimeException(sprintf("Status %d is not equal to %d.", $status, (int) $expectedStatus));
205
        }
206
    }
207
208
    /**
209
     * @Then the response content should be equal to:
210
     */
211
    public function theResponseContentShouldBeEqualTo(PyStringNode $expectedContent)
212
    {
213
        $content = $this->getSession()->getDriver()->getContent();
214
        if ((string) $content !== (string) $expectedContent) {
215
            throw new \RuntimeException("Content is different than expected.");
216
        }
217
    }
218
219
    /**
220
     * @Then the response content should match expression:
221
     */
222
    public function theResponseContentShouldMatchExpression(PyStringNode $pattern)
223
    {
224
        $content = $this->getSession()->getDriver()->getContent();
225
        expect($content)->toMatch('/'.$pattern.'/');
226
    }
227
228
    /**
229
     * @Then the response content should be empty
230
     */
231
    public function theResponseContentShouldBeEmpty()
232
    {
233
        $content = $this->getSession()->getDriver()->getContent();
234
        if (!empty($content)) {
235
            throw new \RuntimeException("Content is not empty.");
236
        }
237
    }
238
239
    /**
240
     * @Then response should have following hedaers:
241
     */
242
    public function responseShouldHaveFollowingHedaers(TableNode $responseHeaders)
243
    {
244
        $headers = $this->getSession()->getResponseHeaders();
245
246
        foreach ($responseHeaders->getHash() as $header) {
247
            if (!array_key_exists($header['Name'], $headers)) {
248
                throw new \RuntimeException(sprintf("There is no \"%s\" header in response.", $header['Name']));
249
            }
250
251
            if ($headers[$header['Name']][0] !== $header['Value']) {
252
                throw new \RuntimeException(sprintf(
253
                    "Header \"%s\" value \"%s\" is not equal to \"%s\".",
254
                    $header['Name'],
255
                    $headers[$header['Name']][0],
256
                    $header['Value']
257
                ));
258
            }
259
        }
260
    }
261
262
263
    /**
264
     * @When http client sends :method request on :url
265
     */
266
    public function httpClientSendRequestOn($method, $url)
267
    {
268
        $this->makeHttpCall($method, $url);
269
    }
270
271
    /**
272
     * @When http client sends :method request on :url with following parameters:
273
     */
274
    public function httpClientSendPostRequestOnWithFollowingParameters($method, $url, TableNode $parametersTable)
275
    {
276
        $parameters = [];
277
        foreach ($parametersTable->getHash() as $parameterData) {
278
            $parameters[$parameterData['Parameter']] = $parameterData['Value'];
279
        }
280
281
        $this->makeHttpCall($method, $url, $parameters);
282
    }
283
284
    /**
285
     * @When http client sends :method request on :url with following headers
286
     */
287
    public function httpClientSendGetRequestOnWithFollowingHeaders($method, $url, TableNode $headersTable)
288
    {
289
        $session = $this->getSession();
290
        $client = $session->getDriver()->getClient();
291
        $headers = [];
292
        foreach ($headersTable->getHash() as $headerData) {
293
            $client->setHeader($headerData['Header'], $headerData['Value']);
294
            $headers[] = $headerData['Header'];
295
        }
296
297
        $this->makeHttpCall($method, $url, [], $headers);
298
    }
299
300
    /**
301
     * @When http client sends :method request on :url with body
302
     */
303
    public function httpClientSendGetRequestOnWithBody($method, $url, PyStringNode $body)
304
    {
305
        $this->makeHttpCall($method, $url, [], [], (string) $body);
306
    }
307
308
    /**
309
     * @param $method
310
     * @param $url
311
     * @param array $parameters
312
     * @param null $body
313
     */
314
    private function makeHttpCall($method, $url, array $parameters = [], array $headers = [], $body = null)
315
    {
316
        $session = $this->getSession();
317
        $client = $session->getDriver()->getClient();
318
        $client->followRedirects(false);
319
        try {
320
            $client->request(
321
                $method,
322
                $url,
323
                $parameters, // parameters
324
                [], // files
325
                $headers, // $_SERVER
326
                $body
327
            );
328
        } catch (RequestException $exception) {
0 ignored issues
show
Bug introduced by
The class GuzzleHttp\Exception\RequestException 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...
329
            if (strpos($exception->getMessage(), 'cURL error 7') !== false) {
330
                if ($this->httpCallAttemptsPerScenario < self::MAX_PHP_SERVER_RESTARTS) {
331
                    $this->httpCallAttemptsPerScenario++;
332
                    $this->tutuIsRunningOnHostAtPort($this->tutuHost, $this->tutuPort);
333
                    $this->makeHttpCall($method, $url, $parameters, $headers, $body);
334
                    return ;
335
                }
336
            }
337
338
            throw $exception;
339
        }
340
    }
341
}
342