Skip to content

Commit

Permalink
Added ability to restrict match count
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-sun committed Nov 14, 2024
1 parent bd09ad5 commit c462b16
Show file tree
Hide file tree
Showing 2 changed files with 428 additions and 31 deletions.
196 changes: 167 additions & 29 deletions src/WiremockContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class WiremockContext implements Context

private const BODY_RECORDINGS_START = '{"targetBaseUrl":"%s","requestBodyPattern":{"matcher":"equalToJson","ignoreArrayOrder":true,"ignoreExtraElements":true},"persist":true}';

private const STUB_MATCH_COUNT_STRATEGY_EXACT = 'exact';
private const STUB_MATCH_COUNT_STRATEGY_MAX = 'max';
private const STUB_MATCH_COUNT_STRATEGY_MIN = 'min';
private const STUB_MATCH_COUNT_STRATEGY_ANY = 'any';

private HttpClientInterface $client;

/**
Expand Down Expand Up @@ -66,31 +71,34 @@ public function addWiremockStubStep(PyStringNode $body): void
#[Given('/^wiremock stubs from "([^"]+)"$/')]
public function addWiremockStubFromFileStep(string $path): void
{
$absolutePath = $this->stubsDirectory . '/' . $path;
$this->addWiremockStubFromFile($path);
}

if (is_dir($absolutePath)) {
$files = scandir($absolutePath);
/**
* @throws WiremockContextException
*/
#[Given('/^wiremock stubs from "([^"]+)" and should be called exactly (?P<expectedCallCount>\d+) times$/')]
public function addWiremockStubFromFileShouldBeCalledExactlyStep(string $path, int $expectedCallCount): void
{
$this->addWiremockStubFromFile($path, $expectedCallCount, self::STUB_MATCH_COUNT_STRATEGY_EXACT);
}

foreach ($files as $file) {
$filePath = $absolutePath . '/' . $file;
if (is_dir($filePath)) {
continue;
}
/**
* @throws WiremockContextException
*/
#[Given('/^wiremock stubs from "([^"]+)" and should be called minimal (?P<expectedCallCount>\d+) times$/')]
public function addWiremockStubFromFileShouldBeCalledMinimalStep(string $path, int $expectedCallCount): void
{
$this->addWiremockStubFromFile($path, $expectedCallCount, self::STUB_MATCH_COUNT_STRATEGY_MIN);
}

try {
$this->loadStubFromFile($filePath);
} catch (Throwable $exception) {
throw new WiremockContextException(
sprintf(
'Unable to load file "%s"',
$filePath
)
, 0, $exception);
}
}
} else {
$this->loadStubFromFile($absolutePath);
}
/**
* @throws WiremockContextException
*/
#[Given('/^wiremock stubs from "([^"]+)" and should be called at most (?P<expectedCallCount>\d+) times$/')]
public function addWiremockStubFromFileShouldBeCalledAtMostStep(string $path, int $expectedCallCount): void
{
$this->addWiremockStubFromFile($path, $expectedCallCount, self::STUB_MATCH_COUNT_STRATEGY_MAX);
}

#[Given('/^clean wiremock$/')]
Expand Down Expand Up @@ -243,22 +251,32 @@ private function sendRequest(string $method, string $url, ?string $body = null):
}
}

public function addStub(string $body): void
{
public function addStub(
string $body,
?int $expectedCallCount = null,
string $type = self::STUB_MATCH_COUNT_STRATEGY_ANY
): void {
$response = $this->sendRequest(
'POST',
self::PATH_MAPPINGS,
$body
);

$this->stubs[$response['id']] = $response;
$stubId = $response['id'];

$this->stubs[$stubId]['response'] = $response;
$this->stubs[$stubId]['count'] = $expectedCallCount;
$this->stubs[$stubId]['type'] = $type;
}

private function loadStubFromFile(string $filePath): void
private function loadStubFromFile(string $filePath, ?int $expectedCallCount, string $type): void
{
$this->addStub(file_get_contents($filePath));
$this->addStub(file_get_contents($filePath), $expectedCallCount, $type);
}

/**
* @throws WiremockContextException
*/
private function allStubsMatched(): void
{
$response = $this->sendRequest(
Expand All @@ -285,17 +303,137 @@ private function allStubsMatched(): void
));
}

$requestedStubsIds[] = $requestData['stubMapping']['id'];
$mappedRequestStubId = $requestData['stubMapping']['id'];
$requestedStubsIds[] = $mappedRequestStubId;
}

$requestedStubsIds = array_unique($requestedStubsIds);

if ($diff = array_diff(array_keys($this->stubs), $requestedStubsIds)) {
$unrequestedStubs = [];
foreach ($diff as $stubId) {
$unrequestedStubs[] = $this->stubs[$stubId];
$unrequestedStubs[] = $this->stubs[$stubId]['response'];
}

throw new WiremockContextException('Unrequested stub(s) found: ' . json_encode($unrequestedStubs, JSON_PRETTY_PRINT));
}
}

/**
* @throws WiremockContextException
*/
#[AfterScenario]
public function allStubsMatchedAsExpectedForEachScenario(): void
{
$this->allStubsMatchedAsExpected();
}

/**
* @throws WiremockContextException
*/
private function addWiremockStubFromFile(
string $path,
?int $expectedCallCount = null,
string $type = self::STUB_MATCH_COUNT_STRATEGY_ANY
): void {
$absolutePath = $this->stubsDirectory . '/' . $path;

if (is_dir($absolutePath)) {
$files = scandir($absolutePath);

foreach ($files as $file) {
$filePath = $absolutePath . '/' . $file;
if (is_dir($filePath)) {
continue;
}

try {
$this->loadStubFromFile($filePath, $expectedCallCount, $type);
} catch (Throwable $exception) {
throw new WiremockContextException(
sprintf(
'Unable to load file "%s"',
$filePath
)
, 0, $exception);
}
}
} else {
$this->loadStubFromFile($absolutePath, $expectedCallCount, $type);
}
}

/**
* @param array $requestedStubsCallCounts
* @throws WiremockContextException
*/
private function allStubsMatchedAsExpected(): void
{
$response = $this->sendRequest(
'GET',
self::PATH_REQUESTS
);

$requestedStubsCallCounts = [];

foreach ($response['requests'] as $requestData) {
$mappedRequestStubId = $requestData['stubMapping']['id'];

if (!isset($requestedStubsCallCounts[$mappedRequestStubId])) {
$requestedStubsCallCounts[$mappedRequestStubId] = 0;
}

$requestedStubsCallCounts[$mappedRequestStubId]++;
}

$errors = [];

foreach ($requestedStubsCallCounts as $stubId => $actualCount) {
$expectedCount = $this->stubs[$stubId]['count'];
$expectedType = $this->stubs[$stubId]['type'];

$url = $this->stubs[$stubId]['response']['request']['urlPath'];

switch ($expectedType) {
case self::STUB_MATCH_COUNT_STRATEGY_EXACT:
if ($actualCount !== $expectedCount) {
$errors[] = sprintf(
'Stub with URL "%s" was expected to be called exactly %d time(s), but was called %d time(s)',
$url,
$expectedCount,
$actualCount
);
}
break;
case self::STUB_MATCH_COUNT_STRATEGY_MAX:
if ($actualCount > $expectedCount) {
$errors[] = sprintf(
'Stub with URL "%s" was expected to be called at most %d time(s), but was called %d time(s)',
$url,
$expectedCount,
$actualCount
);
}
break;
case self::STUB_MATCH_COUNT_STRATEGY_MIN:
if ($actualCount < $expectedCount) {
$errors[] = sprintf(
'Stub with URL "%s" was expected to be called minimum %d time(s), but was called %d time(s)',
$url,
$expectedCount,
$actualCount
);
}
break;
case self::STUB_MATCH_COUNT_STRATEGY_ANY:
break;
default:
throw new WiremockContextException(sprintf('Unknown expectation type %s for URL %s', $expectedType, $url));
}

if (!empty($errors)) {
throw new WiremockContextException(implode("\n", $errors));
}
}
}
}
Loading

0 comments on commit c462b16

Please sign in to comment.