theProviderRequestShouldReturnResponseWithAndBody()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 8
cts 8
cp 1
rs 9.7
c 0
b 0
f 0
cc 2
nc 2
nop 3
crap 2
1
<?php
2
3
namespace SmartGamma\Behat\PactExtension\Context;
4
5
use Behat\Behat\Hook\Scope\StepScope;
6
use Behat\Behat\Hook\Scope\ScenarioScope;
7
use Behat\Gherkin\Node\PyStringNode;
8
use Behat\Gherkin\Node\TableNode;
9
use Behat\Testwork\Hook\Scope\AfterTestScope;
10
use SmartGamma\Behat\PactExtension\Exception\InvalidResponseObjectNameFormat;
11
use SmartGamma\Behat\PactExtension\Exception\NoConsumerRequestDefined;
12
use SmartGamma\Behat\PactExtension\Infrastructure\ProviderState\InjectorStateDTO;
13
use SmartGamma\Behat\PactExtension\Infrastructure\ProviderState\ProviderState;
14
use SmartGamma\Behat\PactExtension\Infrastructure\ProviderState\PlainTextStateDTO;
15
use SmartGamma\Behat\PactExtension\Infrastructure\Interaction\InteractionRequestDTO;
16
use SmartGamma\Behat\PactExtension\Infrastructure\Interaction\InteractionResponseDTO;
17
use SmartGamma\Behat\PactExtension\Infrastructure\Pact;
18
use Behat\Gherkin\Node\ArgumentInterface;
19
20
class PactContext implements PactContextInterface
21
{
22
    /**
23
     * @var string
24
     */
25
    private static $stepName;
26
27
    /**
28
     * @var array
29
     */
30
    private static $tags = [];
31
32
    /**
33
     * @var Pact
34
     */
35
    private static $pact;
36
37
    /**
38
     * @var ProviderState
39
     */
40
    private static $providerState;
41
42
    /**
43
     * @var array
44
     */
45
    private $consumerRequest = [];
46
47
    /**
48
     * @var array
49
     */
50
    private $headers = [];
51
52
    /**
53
     * @var Authenticator
54
     */
55
    private $authenticator;
56
57
    /**
58
     * @var array
59
     */
60
    private $matchingObjectStructures = [];
61
62
    /**
63
     * @param Pact          $pact
64
     * @param ProviderState $providerState
65
     * @param Authenticator $authenticator
66
     */
67 19
    public function initialize(Pact $pact, ProviderState $providerState, Authenticator $authenticator): void
68
    {
69 19
        self::$pact          = $pact;
70 19
        self::$providerState = $providerState;
71 19
        $this->authenticator = $authenticator;
72 19
        self::$stepName = __FUNCTION__;
73
    }
74
75
    /**
76
     * @BeforeScenario
77
     */
78 1
    public function setupBehatTags(ScenarioScope $scope): void
79
    {
80 1
        self::$tags = $scope->getScenario()->getTags();
81 1
        self::$providerState->clearStates();
82
    }
83
84
    /**
85
     * @BeforeScenario
86
     */
87
    public static function setupBehatStepName(ScenarioScope $step): void
88
    {
89
        if ($step->getScenario()->getTitle()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $step->getScenario()->getTitle() of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
90
            self::$providerState->setDefaultPlainTextState($step->getScenario()->getTitle());
91
        }
92
    }
93
94
    /**
95
     * @BeforeStep
96
     */
97
    public static function setupBehatScenarioName(StepScope $step): void
98
    {
99
        self::$stepName = $step->getStep()->getText();
100
    }
101
102
    /**
103
     * @Given :providerName request :method to :uri should return response with :status
104
     */
105 1 View Code Duplication
    public function registerInteraction(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
        string $providerName,
107
        string $method,
108
        string $uri,
109
        int $status
110
    ): void
111
    {
112 1
        $headers = $this->getHeaders($providerName);
113 1
        $request       = new InteractionRequestDTO($providerName, self::$stepName, $uri, $method, $headers);
114 1
        $response      = new InteractionResponseDTO($status);
115 1
        $providerState = self::$providerState->getStateDescription($providerName);
116
117 1
        self::$pact->registerInteraction($request, $response, $providerState);
118
    }
119
120
    /**
121
     * @Given :providerName request :method to :uri should return response with :status and body:
122
     */
123 1 View Code Duplication
    public function registerInteractionWithBody(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
        string $providerName,
125
        string $method,
126
        string $uri,
127
        int $status,
128
        TableNode $responseTable
129
    ): void
130
    {
131 1
        $headers = $this->getHeaders($providerName);
132 1
        $request       = new InteractionRequestDTO($providerName, self::$stepName, $uri, $method, $headers);
133 1
        $response      = new InteractionResponseDTO($status, $responseTable->getHash(), $this->matchingObjectStructures);
134 1
        $providerState = self::$providerState->getStateDescription($providerName);
135
136 1
        self::$pact->registerInteraction($request, $response, $providerState);
137
    }
138
139
    /**
140
     * @Given :providerName request :method to :uri with :query should return response with :status
141
     */
142 1 View Code Duplication
    public function registerInteractionWithQuery(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
143
        string $providerName,
144
        string $method,
145
        string $uri,
146
        string $query,
147
        int $status
148
    ): void
149
    {
150 1
        $headers = $this->getHeaders($providerName);
151 1
        $request       = new InteractionRequestDTO($providerName, self::$stepName, $uri, $method, $headers, $query);
152 1
        $response      = new InteractionResponseDTO($status);
153 1
        $providerState = self::$providerState->getStateDescription($providerName);
154
155 1
        self::$pact->registerInteraction($request, $response, $providerState);
156
    }
157
158
    /**
159
     * @Given :providerName request :method to :uri with :query should return response with :status and body:
160
     */
161 1 View Code Duplication
    public function registerInteractionWithQueryAndBody(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
        string $providerName,
163
        string $method,
164
        string $uri,
165
        string $query,
166
        int $status,
167
        TableNode $responseTable
168
    ): void
169
    {
170 1
        $headers = $this->getHeaders($providerName);
171 1
        $request       = new InteractionRequestDTO($providerName, self::$stepName, $uri, $method, $headers, $query);
172 1
        $response      = new InteractionResponseDTO($status, $responseTable->getHash(), $this->matchingObjectStructures);
173 1
        $providerState = self::$providerState->getStateDescription($providerName);
174
175 1
        self::$pact->registerInteraction($request, $response, $providerState);
176
    }
177
178
    /**
179
     * @Given :providerName request :method to :uri with parameters:
180
     */
181 2
    public function requestToWithParameters(
0 ignored issues
show
Coding Style introduced by
function requestToWithParameters() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
182
        string $providerName,
183
        string $method,
184
        string $uri,
185
        TableNode $table
186
    ): bool
187
    {
188 2
        $headers = $this->getHeaders($providerName);
189 2
        $requestBody = $table->getRowsHash();
190 2
        array_shift($requestBody);
191 2
        $this->consumerRequest[$providerName] = new InteractionRequestDTO($providerName, self::$stepName, $uri, $method, $headers, null, $requestBody);
192
193 2
        return true;
194
    }
195
196
    /**
197
     * @Given request above to :providerName should return response with :status and body:
198
     */
199 2
    public function theProviderRequestShouldReturnResponseWithAndBody(
200
        string $providerName,
201
        int $status,
202
        TableNode $responseTable
203
    ): void
204
    {
205 2
        if (false === isset($this->consumerRequest[$providerName])) {
206 1
            throw new NoConsumerRequestDefined('No consumer InteractionRequestDTO defined. Call step: "Given :providerName request :method to :uri with parameters:" before this one.');
207
        }
208
209 1
        $request       = $this->consumerRequest[$providerName];
210 1
        $response      = new InteractionResponseDTO($status, $responseTable->getHash(), $this->matchingObjectStructures);
211 1
        $providerState = self::$providerState->getStateDescription($providerName);
212 1
        unset($this->consumerRequest[$providerName]);
213
214 1
        self::$pact->registerInteraction($request, $response, $providerState);
215
    }
216
217
    /**
218
     * @Given :object object should have follow structure:
219
     */
220 2
    public function hasFollowStructureInTheResponseAbove($object, TableNode $table): bool
221
    {
222 2
        if (!preg_match('/^<.*>$/', $object)) {
223 1
            throw new InvalidResponseObjectNameFormat('Response object name should be taken in "<...>" like <name>');
224
        }
225
226 1
        $eachParameters = $table->getRowsHash();
227 1
        array_shift($eachParameters);
228
229 1
        $this->matchingObjectStructures[$object] = $eachParameters;
230
231 1
        return true;
232
    }
233
234
    /**
235
     * @Given :providerName API is available
236
     */
237 1
    public function mockedApiProviderIsAvailable(string $providerName): int
238
    {
239 1
        return self::$pact->startServer($providerName);
240
    }
241
242
    /**
243
     * @Given :entity on the provider :providerName:
244
     */
245 1
    public function onTheProvider(string $entity, string $providerName, TableNode $table): void
246
    {
247 1
        $parameters    = \array_slice($table->getRowsHash(), 1);
248 1
        $injectorState = new InjectorStateDTO($providerName, $entity, $parameters);
249 1
        self::$providerState->addInjectorState($injectorState);
250
    }
251
252
    /**
253
     * @Given :entity as :entityDescription on the provider :providerName:
254
     */
255 1
    public function onTheProviderWithDescription(string $entity, string $providerName, string $entityDescription, TableNode $table): void
256
    {
257 1
        $parameters    = \array_slice($table->getRowsHash(), 1);
258 1
        $injectorState = new InjectorStateDTO($providerName, $entity, $parameters, $entityDescription);
259 1
        self::$providerState->addInjectorState($injectorState);
260
    }
261
262
    /**
263
     * @Given provider :providerName state:
264
     */
265 1
    public function providerPlainTextState(string $providerName, PyStringNode $state): void
266
    {
267 1
        $textStateDTO = new PlainTextStateDTO($providerName, $state->getRaw());
268 1
        self::$providerState->setPlainTextState($textStateDTO);
269
    }
270
271
    /**
272
     * @Given the consumer :authType authorized as :credentials on :providerName
273
     */
274 1
    public function theConsumerAuthorizedAsOn(string $authType, string $credentials, string $providerName): void
275
    {
276 1
        $this->headers[$providerName] = $this->authenticator->authorizeConsumerRequestToProvider($authType, $credentials);
277
    }
278
279
    /**
280
     * @AfterScenario
281
     */
282 2
    public function verifyInteractions(): void
283
    {
284 2
        if (\in_array('pact', self::$tags, true)) {
285 1
            self::$pact->verifyInteractions();
286
        }
287
    }
288
289
    /**
290
     * @AfterSuite
291
     */
292 2
    public static function teardown(AfterTestScope $scope): bool
0 ignored issues
show
Coding Style introduced by
function teardown() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
293
    {
294 2
        if (!$scope->getTestResult()->isPassed()) {
295 1
            echo 'A test has failed. Skipping PACT file upload.';
296
297 1
            return false;
298
        }
299
300 1
        return self::$pact->finalize(self::$pact->getConsumerVersion());
301
    }
302
303 6
    private function getHeaders(string $providerName): array
304
    {
305 6
        return isset($this->headers[$providerName]) ? $this->headers[$providerName] : [];
306
    }
307
}
308