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\Client; |
||
20 | |||
21 | use Laminas\Diactoros\Request as PsrRequest; |
||
22 | use Laminas\Diactoros\Uri; |
||
23 | use Mcustiel\Phiremock\Client\Connection\Host; |
||
24 | use Mcustiel\Phiremock\Client\Connection\Port; |
||
25 | use Mcustiel\Phiremock\Client\Connection\Scheme; |
||
26 | use Mcustiel\Phiremock\Client\Utils\ConditionsBuilder; |
||
27 | use Mcustiel\Phiremock\Client\Utils\ExpectationBuilder; |
||
28 | use Mcustiel\Phiremock\Common\StringStream; |
||
29 | use Mcustiel\Phiremock\Common\Utils\ArrayToExpectationConverter; |
||
30 | use Mcustiel\Phiremock\Common\Utils\ExpectationToArrayConverter; |
||
31 | use Mcustiel\Phiremock\Common\Utils\ScenarioStateInfoToArrayConverter; |
||
32 | use Mcustiel\Phiremock\Domain\Expectation; |
||
33 | use Mcustiel\Phiremock\Domain\HttpResponse; |
||
34 | use Mcustiel\Phiremock\Domain\Options\ScenarioName; |
||
35 | use Mcustiel\Phiremock\Domain\Options\ScenarioState; |
||
36 | use Mcustiel\Phiremock\Domain\ScenarioStateInfo; |
||
37 | use Mcustiel\Phiremock\Domain\Version; |
||
38 | use Psr\Http\Client\ClientExceptionInterface; |
||
39 | use Psr\Http\Client\ClientInterface; |
||
40 | use Psr\Http\Message\ResponseInterface; |
||
41 | use RuntimeException; |
||
42 | |||
43 | class Phiremock |
||
44 | { |
||
45 | const API_EXPECTATIONS_URL = '/__phiremock/expectations'; |
||
46 | const API_EXECUTIONS_URL = '/__phiremock/executions'; |
||
47 | const API_SCENARIOS_URL = '/__phiremock/scenarios'; |
||
48 | const API_RESET_URL = '/__phiremock/reset'; |
||
49 | |||
50 | /** @var ClientInterface */ |
||
51 | private $connection; |
||
52 | |||
53 | /** @var ArrayToExpectationConverter */ |
||
54 | private $arrayToExpectationConverter; |
||
55 | |||
56 | /** @var ExpectationToArrayConverter */ |
||
57 | private $expectationToArrayConverter; |
||
58 | |||
59 | /** @var ScenarioStateInfoToArrayConverter */ |
||
60 | private $scenarioStateInfoToArrayConverter; |
||
61 | |||
62 | /** @var Host */ |
||
63 | private $host; |
||
64 | |||
65 | /** @var Port */ |
||
66 | private $port; |
||
67 | |||
68 | /** @var Scheme */ |
||
69 | private $scheme; |
||
70 | |||
71 | public function __construct( |
||
72 | Host $host, |
||
73 | Port $port, |
||
74 | ClientInterface $remoteConnection, |
||
75 | ExpectationToArrayConverter $expectationToArrayConverter, |
||
76 | ArrayToExpectationConverter $arrayToExpectationConverter, |
||
77 | ScenarioStateInfoToArrayConverter $scenarioStateInfoToArrayConverter, |
||
78 | ?Scheme $scheme = null |
||
79 | ) { |
||
80 | $this->host = $host; |
||
81 | $this->port = $port; |
||
82 | $this->connection = $remoteConnection; |
||
83 | $this->expectationToArrayConverter = $expectationToArrayConverter; |
||
84 | $this->arrayToExpectationConverter = $arrayToExpectationConverter; |
||
85 | $this->scenarioStateInfoToArrayConverter = $scenarioStateInfoToArrayConverter; |
||
86 | $this->scheme = $scheme ?? Scheme::createHttp(); |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Creates an expectation with a response for a given request. |
||
91 | * @throws ClientExceptionInterface |
||
92 | */ |
||
93 | public function createExpectation(Expectation $expectation): void |
||
94 | { |
||
95 | $body = @json_encode($this->expectationToArrayConverter->convert($expectation)); |
||
96 | if (json_last_error() !== JSON_ERROR_NONE) { |
||
97 | throw new RuntimeException('Error generating json body for request: ' . json_last_error_msg()); |
||
98 | } |
||
99 | $this->createExpectationFromJson($body); |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
100 | } |
||
101 | |||
102 | /** |
||
103 | * Creates an expectation from a json configuration |
||
104 | * @throws ClientExceptionInterface |
||
105 | */ |
||
106 | public function createExpectationFromJson(string $body): void |
||
107 | { |
||
108 | $uri = $this->createBaseUri()->withPath(self::API_EXPECTATIONS_URL); |
||
109 | $request = (new PsrRequest()) |
||
110 | ->withUri($uri) |
||
111 | ->withMethod('POST') |
||
112 | ->withHeader('Content-Type', 'application/json') |
||
113 | ->withBody(new StringStream($body)); |
||
114 | $this->ensureIsExpectedResponse(201, $this->connection->sendRequest($request)); |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Restores pre-defined expectations and resets scenarios and requests counter. |
||
119 | * @throws ClientExceptionInterface |
||
120 | */ |
||
121 | public function reset(): void |
||
122 | { |
||
123 | $uri = $this->createBaseUri()->withPath(self::API_RESET_URL); |
||
124 | $request = (new PsrRequest())->withUri($uri)->withMethod('POST'); |
||
125 | |||
126 | $this->ensureIsExpectedResponse(200, $this->connection->sendRequest($request)); |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Clears all the currently configured expectations. |
||
131 | * @throws ClientExceptionInterface |
||
132 | */ |
||
133 | public function clearExpectations(): void |
||
134 | { |
||
135 | $uri = $this->createBaseUri()->withPath(self::API_EXPECTATIONS_URL); |
||
136 | $request = (new PsrRequest())->withUri($uri)->withMethod('DELETE'); |
||
137 | |||
138 | $this->ensureIsExpectedResponse(200, $this->connection->sendRequest($request)); |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * @throws ClientExceptionInterface |
||
143 | * @return Expectation[] |
||
144 | */ |
||
145 | public function listExpectations(): array |
||
146 | { |
||
147 | $uri = $this->createBaseUri()->withPath(self::API_EXPECTATIONS_URL); |
||
148 | $request = (new PsrRequest())->withUri($uri)->withMethod('GET'); |
||
149 | $response = $this->connection->sendRequest($request); |
||
150 | |||
151 | $this->ensureIsExpectedResponse(200, $response); |
||
152 | |||
153 | $arraysList = json_decode($response->getBody()->__toString(), true); |
||
154 | $expectationsList = []; |
||
155 | |||
156 | foreach ($arraysList as $expectationArray) { |
||
157 | $expectationsList[] = $this->arrayToExpectationConverter |
||
158 | ->convert($expectationArray); |
||
159 | } |
||
160 | return $expectationsList; |
||
161 | } |
||
162 | |||
163 | /** @throws ClientExceptionInterface */ |
||
164 | public function countExecutions(?ConditionsBuilder $requestBuilder = null): int |
||
165 | { |
||
166 | $uri = $this->createBaseUri()->withPath(self::API_EXECUTIONS_URL); |
||
167 | |||
168 | $request = (new PsrRequest()) |
||
169 | ->withUri($uri) |
||
170 | ->withMethod('POST') |
||
171 | ->withHeader('Content-Type', 'application/json'); |
||
172 | if ($requestBuilder !== null) { |
||
173 | $requestBuilderResult = $requestBuilder->build(); |
||
174 | $expectation = new Expectation( |
||
175 | $requestBuilderResult->getRequestConditions(), |
||
176 | HttpResponse::createEmpty(), |
||
177 | $requestBuilderResult->getScenarioName(), |
||
178 | null, |
||
179 | new Version('2') |
||
180 | ); |
||
181 | $jsonBody = json_encode($this->expectationToArrayConverter->convert($expectation)); |
||
182 | $request = $request->withBody( |
||
183 | new StringStream( |
||
184 | $jsonBody |
||
185 | ) |
||
186 | ); |
||
187 | } |
||
188 | |||
189 | $response = $this->connection->sendRequest($request); |
||
190 | |||
191 | $this->ensureIsExpectedResponse(200, $response); |
||
192 | $json = json_decode($response->getBody()->__toString()); |
||
193 | |||
194 | return $json->count; |
||
195 | } |
||
196 | |||
197 | /** @throws ClientExceptionInterface */ |
||
198 | public function listExecutions(?ConditionsBuilder $requestBuilder = null): array |
||
199 | { |
||
200 | $uri = $this->createBaseUri()->withPath(self::API_EXECUTIONS_URL); |
||
201 | |||
202 | $request = (new PsrRequest()) |
||
203 | ->withUri($uri) |
||
204 | ->withMethod('PUT') |
||
205 | ->withHeader('Content-Type', 'application/json'); |
||
206 | if ($requestBuilder !== null) { |
||
207 | $requestBuilderResult = $requestBuilder->build(); |
||
208 | $expectation = new Expectation( |
||
209 | $requestBuilderResult->getRequestConditions(), |
||
210 | HttpResponse::createEmpty(), |
||
211 | $requestBuilderResult->getScenarioName(), |
||
212 | null, |
||
213 | new Version('2') |
||
214 | ); |
||
215 | $request = $request->withBody( |
||
216 | new StringStream( |
||
217 | json_encode($this->expectationToArrayConverter->convert($expectation)) |
||
218 | ) |
||
219 | ); |
||
220 | } |
||
221 | |||
222 | $response = $this->connection->sendRequest($request); |
||
223 | $this->ensureIsExpectedResponse(200, $response); |
||
224 | return json_decode($response->getBody()->__toString()); |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * Sets scenario state. |
||
229 | * @throws ClientExceptionInterface |
||
230 | */ |
||
231 | public function setScenarioState(string $scenarioName, string $scenarioState): void |
||
232 | { |
||
233 | $scenarioStateInfo = new ScenarioStateInfo( |
||
234 | new ScenarioName($scenarioName), |
||
235 | new ScenarioState($scenarioState) |
||
236 | ); |
||
237 | $uri = $this->createBaseUri()->withPath(self::API_SCENARIOS_URL); |
||
238 | $request = (new PsrRequest()) |
||
239 | ->withUri($uri) |
||
240 | ->withMethod('PUT') |
||
241 | ->withHeader('Content-Type', 'application/json') |
||
242 | ->withBody( |
||
243 | new StringStream( |
||
244 | json_encode( |
||
245 | $this->scenarioStateInfoToArrayConverter->convert($scenarioStateInfo) |
||
246 | ) |
||
247 | ) |
||
248 | ); |
||
249 | |||
250 | $response = $this->connection->sendRequest($request); |
||
251 | $this->ensureIsExpectedResponse(200, $response); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Resets all the scenarios to start state. |
||
256 | * @throws ClientExceptionInterface |
||
257 | */ |
||
258 | public function resetScenarios(): void |
||
259 | { |
||
260 | $uri = $this->createBaseUri()->withPath(self::API_SCENARIOS_URL); |
||
261 | $request = (new PsrRequest())->withUri($uri)->withMethod('DELETE'); |
||
262 | |||
263 | $this->ensureIsExpectedResponse(200, $this->connection->sendRequest($request)); |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Resets all the requests counters to 0. |
||
268 | * @throws ClientExceptionInterface |
||
269 | */ |
||
270 | public function resetRequestsCounter(): void |
||
271 | { |
||
272 | $uri = $this->createBaseUri()->withPath(self::API_EXECUTIONS_URL); |
||
273 | $request = (new PsrRequest())->withUri($uri)->withMethod('DELETE'); |
||
274 | |||
275 | $this->ensureIsExpectedResponse(200, $this->connection->sendRequest($request)); |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Inits the fluent interface to create an expectation. |
||
280 | * |
||
281 | * @return ExpectationBuilder |
||
282 | */ |
||
283 | public static function on(ConditionsBuilder $requestBuilder): ExpectationBuilder |
||
284 | { |
||
285 | return new ExpectationBuilder($requestBuilder); |
||
286 | } |
||
287 | |||
288 | /** Shortcut. */ |
||
289 | public static function onRequest(string $method, string $url): ExpectationBuilder |
||
290 | { |
||
291 | return new ExpectationBuilder( |
||
292 | ConditionsBuilder::create($method, $url) |
||
293 | ); |
||
294 | } |
||
295 | |||
296 | private function createBaseUri(): Uri |
||
297 | { |
||
298 | return (new Uri()) |
||
299 | ->withScheme($this->scheme->asString()) |
||
300 | ->withHost($this->host->asString()) |
||
301 | ->withPort($this->port->asInt()); |
||
302 | } |
||
303 | |||
304 | /** @throws RuntimeException */ |
||
305 | private function ensureIsExpectedResponse(int $statusCode, ResponseInterface $response): void |
||
306 | { |
||
307 | $responseStatusCode = $response->getStatusCode(); |
||
308 | if ($responseStatusCode !== $statusCode) { |
||
309 | if ($responseStatusCode >= 500) { |
||
310 | $errors = json_decode($response->getBody()->__toString(), true)['details']; |
||
311 | |||
312 | throw new RuntimeException('An error occurred creating the expectation: ' . ($errors ? var_export($errors, true) : '') . $response->getBody()->__toString()); |
||
313 | } |
||
314 | |||
315 | if ($responseStatusCode >= 400) { |
||
316 | throw new RuntimeException('Request error while creating the expectation'); |
||
317 | } |
||
318 | throw new RuntimeException('Unexpected response while creating the expectation'); |
||
319 | } |
||
320 | } |
||
321 | } |
||
322 |