mcustiel /
phiremock-client
| 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
Loading history...
|
|||
| 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 |