Passed
Push — master ( f6edcd...1260c1 )
by Mariano
07:56
created

requestFormDataMatchExpectation()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 14
c 0
b 0
f 0
nc 5
nop 2
dl 0
loc 25
rs 9.4888
1
<?php
2
/**
3
 * This file is part of Phiremock.
4
 *
5
 * Phiremock is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Lesser General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * Phiremock is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with Phiremock.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
namespace Mcustiel\Phiremock\Server\Utils;
20
21
use Mcustiel\Phiremock\Domain\Conditions;
22
use Mcustiel\Phiremock\Domain\Expectation;
23
use Mcustiel\Phiremock\Server\Model\ScenarioStorage;
24
use Psr\Http\Message\ServerRequestInterface;
25
use Psr\Log\LoggerInterface;
26
27
class RequestExpectationComparator
28
{
29
    /** @var ScenarioStorage */
30
    private $scenarioStorage;
31
    /** @var LoggerInterface */
32
    private $logger;
33
34
    public function __construct(
35
        ScenarioStorage $scenarioStorage,
36
        LoggerInterface $logger
37
    ) {
38
        $this->scenarioStorage = $scenarioStorage;
39
        $this->logger = $logger;
40
    }
41
42
    public function equals(ServerRequestInterface $httpRequest, Expectation $expectation): bool
43
    {
44
        $this->logger->debug('Checking if request matches an expectation');
45
46
        if (!$this->isExpectedScenarioState($expectation)) {
47
            return false;
48
        }
49
50
        $expectedRequest = $expectation->getRequest();
51
52
        $foundMatch = $this->compareRequestParts($httpRequest, $expectedRequest);
53
        $this->logger->debug('Matches? ' . ((bool) $foundMatch ? 'yes' : 'no'));
54
55
        return $foundMatch;
56
    }
57
58
    private function compareRequestParts(ServerRequestInterface $httpRequest, Conditions $expectedRequest): bool
59
    {
60
        return $this->requestMethodMatchesExpectation($httpRequest, $expectedRequest)
61
            && $this->requestUrlMatchesExpectation($httpRequest, $expectedRequest)
62
            && $this->requestBodyMatchesExpectation($httpRequest, $expectedRequest)
63
            && $this->requestHeadersMatchExpectation($httpRequest, $expectedRequest)
64
            && $this->requestFormDataMatchExpectation($httpRequest, $expectedRequest);
65
    }
66
67
    private function isExpectedScenarioState(Expectation $expectation): bool
68
    {
69
        if ($expectation->getRequest()->hasScenarioState()) {
70
            $this->checkScenarioNameOrThrowException($expectation);
71
            $this->logger->debug('Checking scenario state again expectation');
72
            $scenarioState = $this->scenarioStorage->getScenarioState(
73
                $expectation->getScenarioName()
74
            );
75
            if (!$expectation->getRequest()->getScenarioState()->equals($scenarioState)) {
76
                return false;
77
            }
78
        }
79
80
        return true;
81
    }
82
83
    /** @throws \RuntimeException */
84
    private function checkScenarioNameOrThrowException(Expectation $expectation)
85
    {
86
        if (!$expectation->hasScenarioName()) {
87
            throw new \InvalidArgumentException('Expecting scenario state without specifying scenario name');
88
        }
89
    }
90
91
    private function requestMethodMatchesExpectation(ServerRequestInterface $httpRequest, Conditions $expectedRequest): bool
92
    {
93
        $method = $expectedRequest->getMethod();
94
        if (!$method) {
95
            return true;
96
        }
97
        $this->logger->debug('Checking METHOD against expectation');
98
99
        return $method->getMatcher()->matches($httpRequest->getMethod());
100
    }
101
102
    private function requestUrlMatchesExpectation(ServerRequestInterface $httpRequest, Conditions $expectedRequest): bool
103
    {
104
        $url = $expectedRequest->getUrl();
105
        if (!$url) {
106
            return true;
107
        }
108
        $this->logger->debug('Checking URL against expectation');
109
110
        $requestUrl = $this->getComparableRequestUrl($httpRequest);
111
112
        return $url->getMatcher()->matches($requestUrl);
113
    }
114
115
    private function getComparableRequestUrl($httpRequest)
116
    {
117
        $requestUrl = $httpRequest->getUri()->getPath();
118
        if ($httpRequest->getUri()->getQuery()) {
119
            $requestUrl .= '?' . $httpRequest->getUri()->getQuery();
120
        }
121
        if ($httpRequest->getUri()->getFragment()) {
122
            $requestUrl .= '#' . $httpRequest->getUri()->getFragment();
123
        }
124
125
        return $requestUrl;
126
    }
127
128
    private function requestBodyMatchesExpectation(ServerRequestInterface $httpRequest, Conditions $expectedRequest): bool
129
    {
130
        $bodycondition = $expectedRequest->getBody();
131
        if (!$bodycondition) {
132
            return true;
133
        }
134
        $this->logger->debug('Checking BODY against expectation');
135
136
        return $bodycondition->getMatcher()->matches($httpRequest->getBody()->__toString());
137
    }
138
139
    private function requestHeadersMatchExpectation(ServerRequestInterface $httpRequest, Conditions $expectedRequest): bool
140
    {
141
        $headerConditions = $expectedRequest->getHeaders();
142
        if (!$headerConditions) {
143
            return true;
144
        }
145
        $this->logger->debug('Checking HEADERS against expectation');
146
        /** @var \Mcustiel\Phiremock\Domain\Condition\Conditions\HeaderCondition $headerCondition */
147
        foreach ($headerConditions as $header => $headerCondition) {
148
            $headerName = $header->asString();
149
            $this->logger->debug("Checking $headerName against expectation");
150
151
            $matches = $headerCondition->getMatcher()->matches(
152
                $httpRequest->getHeaderLine($headerName)
153
            );
154
            if (!$matches) {
155
                return false;
156
            }
157
        }
158
159
        return true;
160
    }
161
162
    private function requestFormDataMatchExpectation(ServerRequestInterface $httpRequest, Conditions $expectedRequest): bool
163
    {
164
        $formDataConditions = $expectedRequest->getFormFields();
165
        if (!$formDataConditions) {
166
            return true;
167
        }
168
        $this->logger->debug('Checking FORM DATA against expectation');
169
        /** @var \Mcustiel\Phiremock\Domain\Condition\Conditions\FormFieldCondition $fieldCondition */
170
        foreach ($formDataConditions as $field => $fieldCondition) {
171
            $fieldName = $field->asString();
172
            $this->logger->debug("Checking $fieldName against expectation");
173
174
            if (!isset($httpRequest->getParsedBody()[$fieldName])) {
175
                return false;
176
            }
177
178
            $matches = $fieldCondition->getMatcher()->matches(
179
                $httpRequest->getParsedBody()[$fieldName]
180
            );
181
            if (!$matches) {
182
                return false;
183
            }
184
        }
185
186
        return true;
187
    }
188
}
189