Skip to content

Commit

Permalink
Add reset message queues before scenario
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiidonii committed Oct 20, 2024
1 parent 47b11a3 commit 7916391
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 58 deletions.
71 changes: 40 additions & 31 deletions src/Context/MessengerContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
use Behat\Gherkin\Node\PyStringNode;
use Behat\Hook\BeforeScenario;
use Exception;
use JsonException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Messenger\Transport\InMemory\InMemoryTransport;
use Symfony\Component\Messenger\Transport\Sync\SyncTransport;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class MessengerContext implements Context
Expand Down Expand Up @@ -62,8 +62,8 @@ public function transportShouldContainMessageWithJson(string $transportName, PyS
throw new Exception(
sprintf(
'The transport doesn\'t contain message with such JSON, actual messages: %s',
$this->getPrettyJson($actualMessageList)
)
$this->getPrettyJson($actualMessageList),
),
);
}

Expand All @@ -73,7 +73,7 @@ public function transportShouldContainMessageWithJson(string $transportName, PyS
public function transportShouldContainMessageWithJsonAndVariableFields(
string $transportName,
string $variableFields,
PyStringNode $expectedMessage
PyStringNode $expectedMessage,
): void {
$variableFields = $variableFields ? array_map('trim', explode(',', $variableFields)) : [];
$expectedMessage = $this->decodeExpectedJson($expectedMessage);
Expand All @@ -92,8 +92,8 @@ public function transportShouldContainMessageWithJsonAndVariableFields(
throw new Exception(
sprintf(
'The transport doesn\'t contain message with such JSON, actual messages: %s',
$this->getPrettyJson($actualMessageList)
)
$this->getPrettyJson($actualMessageList),
),
);
}

Expand All @@ -114,8 +114,8 @@ public function allTransportMessagesShouldBeJson(string $transportName, PyString
throw new Exception(
sprintf(
'The expected transport messages doesn\'t match actual: %s',
$this->getPrettyJson($actualMessageList)
)
$this->getPrettyJson($actualMessageList),
),
);
}
}
Expand All @@ -126,7 +126,7 @@ public function allTransportMessagesShouldBeJson(string $transportName, PyString
public function allTransportMessagesShouldBeJsonWithVariableFields(
string $transportName,
string $variableFields,
PyStringNode $expectedMessageList
PyStringNode $expectedMessageList,
): void {
$variableFields = $variableFields ? array_map('trim', explode(',', $variableFields)) : [];
$expectedMessageList = $this->decodeExpectedJson($expectedMessageList);
Expand All @@ -137,12 +137,12 @@ public function allTransportMessagesShouldBeJsonWithVariableFields(
$actualMessageList[] = $this->convertToArray($envelope->getMessage());
}

if (!$this->isMessagesAreSimilar($expectedMessageList, $actualMessageList, $variableFields)) {
if (!$this->isMessagesAreSimilar($expectedMessageList, $actualMessageList, $variableFields, true)) {
throw new Exception(
sprintf(
'The expected transport messages doesn\'t match actual: %s',
$this->getPrettyJson($actualMessageList)
)
$this->getPrettyJson($actualMessageList),
),
);
}
}
Expand All @@ -160,14 +160,15 @@ public function thereIsCountMessagesInTransport(int $expectedMessageCount, strin
sprintf(
'In transport exist actual count: %s, but expected count: %s',
$actualMessageCount,
$expectedMessageCount
)
$expectedMessageCount,
),
);
}
}

/**
* @param array<mixed> $message
*
* @return string|bool
*/
private function getPrettyJson(array $message)
Expand All @@ -177,28 +178,25 @@ private function getPrettyJson(array $message)

/**
* @param mixed $object
*
* @return array<mixed>
*/
private function convertToArray($object): array
{
return (array) $this->normalizer->normalize($object);
return (array)$this->normalizer->normalize($object);
}

/**
* @return array<mixed>
*/
private function decodeExpectedJson(PyStringNode $expectedJson): array
{
try {
return json_decode(
trim($expectedJson->getRaw()),
true,
512,
JSON_THROW_ON_ERROR
);
} catch (JsonException $e) {
dd(trim($expectedJson->getRaw()), $expectedJson->getRaw(), $e);
}
return json_decode(
trim($expectedJson->getRaw()),
true,
512,
JSON_THROW_ON_ERROR,
);
}

private function getMessengerTransportByName(string $transportName): InMemoryTransport
Expand All @@ -217,22 +215,33 @@ private function getMessengerTransportByName(string $transportName): InMemoryTra
}

throw new Exception(
'In memory transport ' . $fullName . ' not found'
'In memory transport ' . $fullName . ' not found',
);
}

/**
* @param array $actual<mixed>
* @param array $expected<mixed>
* @param array $actual <mixed>
* @param array $expected <mixed>
* @param string[]|null $requiredFields
*
* @return bool
*/
private function isMessagesAreSimilar(
array $actual,
array $expected,
array $actual,
?array $requiredFields = null,
bool $multipleActual = false,
): bool {
if ($multipleActual) {
foreach ($actual as $nextActualItem) {
if (!$this->isMessagesAreSimilar($expected, $nextActualItem, $requiredFields)) {
return false;
}
}

return true;
}

$requiredFields = $requiredFields ?? array_keys($expected);

foreach ($requiredFields as $requiredField) {
Expand All @@ -244,10 +253,10 @@ private function isMessagesAreSimilar(
return false;
}

if (is_string($expected[$requiredField]) && str_starts_with('~', $expected[$requiredField])) {
if (is_string($expected[$requiredField]) && str_starts_with($expected[$requiredField], '~')) {
$pregMatchValue = preg_match(
sprintf('|%s|', substr($expected[$requiredField], 1)),
sprintf('%s', $actual[$requiredField])
sprintf('%s', $actual[$requiredField]),
);

return !($pregMatchValue === 0 || $pregMatchValue === false);
Expand Down
24 changes: 10 additions & 14 deletions src/Context/TransportRetriever.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
use Symfony\Contracts\Service\ServiceProviderInterface;

class TransportRetriever
{
/** @var ContainerInterface */
private ContainerInterface $container;
private ServiceProviderInterface $receiverLocator;

public function __construct(ContainerInterface $container)
{
$this->container = $container;
public function __construct(
ServiceProviderInterface $receiverLocator,
) {
$this->receiverLocator = $receiverLocator;
}

/**
Expand All @@ -24,16 +25,11 @@ public function __construct(ContainerInterface $container)
public function getAllTransports(): array
{
$transports = [];
assert($this->container instanceof Container);

foreach ($this->container->getServiceIds() as $serviceId) {
if (
str_starts_with($serviceId, 'messenger.transport.') &&
$this->container->has($serviceId)
) {
$transports[$serviceId] = $this->container->get($serviceId);
}

foreach ($this->receiverLocator->getProvidedServices() as $name => $service) {
$transports[$name] = $this->receiverLocator->get($name);
}

return $transports;
}
}
2 changes: 1 addition & 1 deletion src/Resources/config/messenger_context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<argument key="$container" type="service" id="test.service_container"/>
</service>
<service public="true" autowire="true" id="BehatMessengerContext\Context\TransportRetriever" class="BehatMessengerContext\Context\TransportRetriever">
<argument key="$container" type="service" id="test.service_container"/>
<argument key="$receiverLocator" type="service" id="messenger.receiver_locator"/>
</service>
</services>
</container>
79 changes: 67 additions & 12 deletions tests/MessengerContextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use BehatMessengerContext\Context\MessengerContext;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Transport\InMemory\InMemoryTransport;
Expand All @@ -33,7 +34,7 @@ class MessengerContextTest extends TestCase

protected function setUp(): void
{
$this->container = $this->createMock(ContainerInterface::class);
$this->container = $this->createMock(Container::class);
$this->normalizer = $this->createMock(NormalizerInterface::class);
$this->transportRetriever = $this->createMock(TransportRetriever::class);
$this->inMemoryTransport = $this->createMock(InMemoryTransport::class);
Expand All @@ -47,16 +48,28 @@ protected function setUp(): void

public function testClearMessenger(): void
{
$this->transportRetriever
$this->container
->expects($this->once())
->method('getAllTransports')
->willReturn([$this->inMemoryTransport]);
->method('getServiceIds')
->willReturn(['messenger.transport.test']);
$this->container
->expects(self::once())
->method('has')
->willReturn(true);
$this->container
->expects(self::once())
->method('get')
->willReturn($this->inMemoryTransport);

$this->inMemoryTransport
->expects($this->once())
->method('reset');

$this->messengerContext->clearMessenger();
(new MessengerContext(
$this->container,
$this->normalizer,
new TransportRetriever($this->container),
))->clearMessenger();
}

public function testTransportShouldContainMessageWithJson(): void
Expand Down Expand Up @@ -360,7 +373,6 @@ public function testFailAllTransportMessagesShouldBeJson(): void

public function testAllTransportMessagesShouldBeJsonWithVariableFields(): void
{
// Create mock messages
$message1 = new \stdClass();
$message1->id = 1;
$message1->name = 'Test';
Expand All @@ -369,15 +381,12 @@ public function testAllTransportMessagesShouldBeJsonWithVariableFields(): void
$message2->id = 2;
$message2->name = 'Test';

// Wrap messages in Envelopes
$envelope1 = new Envelope($message1);
$envelope2 = new Envelope($message2);

// Mock the InMemoryTransport and its `get()` method
$transport = $this->createMock(InMemoryTransport::class);
$transport->method('get')->willReturn([$envelope1, $envelope2]);

// Configure the container to return the mocked transport
$this->container
->expects(self::once())
->method('has')
Expand All @@ -389,7 +398,6 @@ public function testAllTransportMessagesShouldBeJsonWithVariableFields(): void
->with('messenger.transport.test')
->willReturn($transport);

// Configure the normalizer to convert messages to arrays
$this->normalizer
->expects($this->exactly(2))
->method('normalize')
Expand All @@ -399,10 +407,57 @@ public function testAllTransportMessagesShouldBeJsonWithVariableFields(): void
);

$expectedJson = new PyStringNode([
'[{"id": "~\\d+", "name": "Test"}]'
'{"id": "~\\\\d+", "name": "Test"}'
], 1);

$this->messengerContext->allTransportMessagesShouldBeJsonWithVariableFields(
'test',
'id',
$expectedJson
);
}

public function testFailAllTransportMessagesShouldBeJsonWithVariableFields(): void
{
$message1 = new \stdClass();
$message1->id = 1;
$message1->name = 'Test';

$message2 = new \stdClass();
$message2->id = 2;
$message2->name = 'Test';

$envelope1 = new Envelope($message1);
$envelope2 = new Envelope($message2);

$transport = $this->createMock(InMemoryTransport::class);
$transport->method('get')->willReturn([$envelope1, $envelope2]);

$this->container
->expects(self::once())
->method('has')
->with('messenger.transport.test')
->willReturn(true);
$this->container
->expects(self::once())
->method('get')
->with('messenger.transport.test')
->willReturn($transport);

$this->normalizer
->expects($this->exactly(2))
->method('normalize')
->willReturnOnConsecutiveCalls(
['id' => 1, 'name' => 'Test'],
['id' => 'uuid', 'name' => 'Test']
);

$expectedJson = new PyStringNode([
'{"id": "~\\\\d+", "name": "Test"}'
], 1);

// Call the method with variable field "id"
$this->expectException(Exception::class);
$this->expectExceptionMessage('The expected transport messages doesn\'t match actual');
$this->messengerContext->allTransportMessagesShouldBeJsonWithVariableFields(
'test',
'id',
Expand Down

0 comments on commit 7916391

Please sign in to comment.