Skip to content

Commit

Permalink
Merge pull request #9 from PcComponentes/feature/support-for-multiple…
Browse files Browse the repository at this point in the history
…-same-messages

Feature/support for multiple same messages
  • Loading branch information
AaronBernabeu authored Feb 4, 2021
2 parents f8c4a3c + fdc7f17 commit 26eebe7
Show file tree
Hide file tree
Showing 16 changed files with 513 additions and 63 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ composer.phar
###< IDEA ###
###> phpunit ###
.phpunit
.phpunit.result.cache
/phpunit.xml
###< phpunit ###

Expand All @@ -19,4 +20,4 @@ composer.phar

###> ganchudo ###
ganchudo.yml
###< ganchudo ###
###< ganchudo ###
47 changes: 34 additions & 13 deletions src/Behat/MessageValidatorOpenApiContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use PcComponentes\OpenApiMessagingContext\AsyncApi\AsyncApiParser;
use PcComponentes\OpenApiMessagingContext\Messaging\SpyMiddleware;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonSchema;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonValidationCollection;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonValidationException;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonValidator;
use Symfony\Component\Yaml\Yaml;

final class MessageValidatorOpenApiContext implements Context
Expand Down Expand Up @@ -36,12 +39,22 @@ public function theMessageShouldBeValidAccordingToTheSwagger($name, $dumpPath):
$path = realpath($this->rootPath . '/' . $dumpPath);
$this->checkSchemaFile($path);

$eventJson = $this->spyMiddleware->getMessage($name);
$jsonMessages = $this->spyMiddleware->getMessagesFromName($name);

$allSpec = Yaml::parse(file_get_contents($path));
$schema = (new AsyncApiParser($allSpec))->parse($name);

$this->validate($eventJson, new JsonSchema(\json_decode(\json_encode($schema), false)));
$validations = [];

foreach ($jsonMessages as $theJsonMessage) {
$validator = new JsonValidator($theJsonMessage, new JsonSchema(\json_decode(\json_encode($schema), false)));
$validations[] = $validator->validate();
}

$jsonValidation = new JsonValidationCollection(...$validations);
if ($jsonValidation->hasAnyError()) {
throw new JsonValidationException($jsonValidation->buildErrorMessage());
}
}

/**
Expand All @@ -50,7 +63,25 @@ public function theMessageShouldBeValidAccordingToTheSwagger($name, $dumpPath):
public function theMessageShouldBeDispatched(string $name): void
{
if (false === $this->spyMiddleware->hasMessage($name)) {
throw new \Exception(sprintf('Message %s not dispatched', $name));
throw new \Exception(sprintf('Message %s was expected to dispatch, actually not dispatched', $name));
}
}

/**
* @Then the message :name should be dispatched :times times
*/
public function theMessageShouldBeDispatchedManyTimes(string $name, int $times): void
{
$countMessages = $this->spyMiddleware->countMessagesFromName($name);
if ($times !== $countMessages) {
throw new \Exception(
sprintf(
'Message %s was expected to dispatch %d times, actually dispatched %d times.',
$name,
$times,
$countMessages
),
);
}
}

Expand All @@ -62,14 +93,4 @@ private function checkSchemaFile($filename): void
);
}
}

private function validate(string $json, JsonSchema $schema): bool
{
$validator = new \JsonSchema\Validator();

$resolver = new \JsonSchema\SchemaStorage(new \JsonSchema\Uri\UriRetriever(), new \JsonSchema\Uri\UriResolver());
$schema->resolve($resolver);

return $schema->validate(\json_decode($json, false), $validator);
}
}
27 changes: 15 additions & 12 deletions src/Behat/ResponseValidatorOpenApiContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\MinkExtension\Context\MinkContext;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonSchema;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonValidationCollection;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonValidationException;
use PcComponentes\OpenApiMessagingContext\OpenApi\JsonValidator;
use PcComponentes\OpenApiMessagingContext\OpenApi\OpenApiSchemaParser;
use Symfony\Component\Yaml\Yaml;

Expand Down Expand Up @@ -44,7 +47,12 @@ public function theJsonResponseShouldBeValidAccordingToOpenApiSchema($dumpPath,
$allSpec = Yaml::parse(file_get_contents($path));
$schemaSpec = (new OpenApiSchemaParser($allSpec))->parse($schema);

$this->validate($responseJson, new JsonSchema(\json_decode(\json_encode($schemaSpec), false)));
$validator = new JsonValidator($responseJson, new JsonSchema(\json_decode(\json_encode($schemaSpec), false)));
$validation = $validator->validate();

if ($validation->hasError()) {
throw new JsonValidationException($validation->errorMessage());
}
}

/**
Expand All @@ -64,7 +72,12 @@ public function theResponseShouldBeValidAccordingToOpenApiWithPath(string $dumpP
$allSpec = Yaml::parse(\file_get_contents($path));
$schemaSpec = (new OpenApiSchemaParser($allSpec))->fromResponse($openApiPath, $method, $statusCode, $contentType);

$this->validate($responseJson, new JsonSchema(\json_decode(\json_encode($schemaSpec), false)));
$validator = new JsonValidator($responseJson, new JsonSchema(\json_decode(\json_encode($schemaSpec), false)));
$validation = $validator->validate();

if ($validation->hasError()) {
throw new JsonValidationException($validation->errorMessage());
}
}

private function checkSchemaFile($filename): void
Expand All @@ -76,16 +89,6 @@ private function checkSchemaFile($filename): void
}
}

private function validate(string $json, JsonSchema $schema): bool
{
$validator = new \JsonSchema\Validator();

$resolver = new \JsonSchema\SchemaStorage(new \JsonSchema\Uri\UriRetriever(), new \JsonSchema\Uri\UriResolver());
$schema->resolve($resolver);

return $schema->validate(\json_decode($json, false), $validator);
}

private function extractMethod(): string
{
/** @var Client $requestClient */
Expand Down
20 changes: 17 additions & 3 deletions src/Messaging/SpyMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

final class SpyMiddleware implements MiddlewareInterface, MessageVisitor
{
private static array $messages;
private static array $messages = [];
private SchemaValidatorSimpleMessageSerializable $simpleMessageSerializable;
private SchemaValidatorAggregateMessageSerializable $aggregateMessageSerializable;

Expand All @@ -36,10 +36,19 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope

private function save($key, $data): void
{
self::$messages[$key] = $data;
self::$messages[$key][] = $data;
}

public function getMessage(string $name)
{
if ($this->hasMessage($name)) {
return self::$messages[$name][0];
}

throw new \Exception('Message ' . $name . ' not dispatched');
}

public function getMessagesFromName(string $name): array
{
if ($this->hasMessage($name)) {
return self::$messages[$name];
Expand All @@ -50,7 +59,12 @@ public function getMessage(string $name)

public function hasMessage(string $name): bool
{
return \array_key_exists($name, self::$messages);
return true === \array_key_exists($name, self::$messages) && \count(self::$messages[$name]) > 0;
}

public function countMessagesFromName(string $name): int
{
return $this->hasMessage($name) ? \count(self::$messages[$name]) : 0;
}

public function reset(): void
Expand Down
34 changes: 6 additions & 28 deletions src/OpenApi/JsonSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,47 +9,25 @@
final class JsonSchema
{
private $schema;
private ?string $uri;

public function __construct($schema, ?string $uri = null)
public function __construct($schema)
{
$this->schema = $schema;
$this->uri = $uri;
}

public function resolve(SchemaStorage $resolver): JsonSchema
public function validate(string $json, Validator $validator): JsonValidation
{
if (!$this->hasUri()) {
return $this;
}

$this->schema = $resolver->resolveRef($this->uri);

return $this;
}
$validator->check(\json_decode($json), $this->schema);

public function validate($json, Validator $validator): bool
{
$validator->check($json, $this->schema);
$msg = null;

if (!$validator->isValid()) {
$msg = "JSON does not validate. Violations:".\PHP_EOL;
$msg = 'Violations:'.\PHP_EOL;
foreach ($validator->getErrors() as $error) {
$msg .= sprintf(" - [%s] %s".\PHP_EOL, $error['property'], $error['message']);
}
throw new \Exception($msg);
}

return true;
}

public function schema(): string
{
return $this->schema;
}

private function hasUri(): bool
{
return null !== $this->uri;
return new JsonValidation($json, $msg);
}
}
30 changes: 30 additions & 0 deletions src/OpenApi/JsonValidation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types=1);

namespace PcComponentes\OpenApiMessagingContext\OpenApi;

final class JsonValidation
{
private string $json;
private ?string $errorMessage;

public function __construct(string $json, ?string $errorMessage)
{
$this->json = $json;
$this->errorMessage = $errorMessage;
}

public function hasError(): bool
{
return null !== $this->errorMessage;
}

public function json(): string
{
return $this->json;
}

public function errorMessage(): ?string
{
return $this->errorMessage;
}
}
44 changes: 44 additions & 0 deletions src/OpenApi/JsonValidationCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php declare(strict_types=1);

namespace PcComponentes\OpenApiMessagingContext\OpenApi;

final class JsonValidationCollection
{
private array $jsonValidations;

public function __construct(JsonValidation ...$jsonValidations)
{
$this->jsonValidations = $jsonValidations;
}

public function hasAnyError()
{
foreach ($this->jsonValidations as $theJsonValidation) {
if (true === $theJsonValidation->hasError()) {
return true;
}
}

return false;
}

public function buildErrorMessage(): string
{
if (false === $this->hasAnyError()) {
return '';
}

$validationsWithErrors = \array_filter(
$this->jsonValidations,
static fn(JsonValidation $elem) => $elem->hasError()
);

$msg = PHP_EOL;
foreach ($validationsWithErrors as $index => $validation) {
$msg .= sprintf('JSON message %d does not validate. ' . PHP_EOL, $index);
$msg .= $validation->errorMessage();
}

return $msg;
}
}
8 changes: 8 additions & 0 deletions src/OpenApi/JsonValidationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php declare(strict_types=1);

namespace PcComponentes\OpenApiMessagingContext\OpenApi;

final class JsonValidationException extends \Exception
{

}
22 changes: 22 additions & 0 deletions src/OpenApi/JsonValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare(strict_types=1);

namespace PcComponentes\OpenApiMessagingContext\OpenApi;

use JsonSchema\Validator;

final class JsonValidator
{
private string $jsonToValidate;
private JsonSchema $jsonSchema;

public function __construct(string $jsonToValidate, JsonSchema $jsonSchema)
{
$this->jsonToValidate = $jsonToValidate;
$this->jsonSchema = $jsonSchema;
}

public function validate(): JsonValidation
{
return $this->jsonSchema->validate($this->jsonToValidate, new Validator());
}
}
5 changes: 3 additions & 2 deletions tests/AsyncApi/AsyncApiParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PcComponentes\OpenApiMessagingContext\Tests\AsyncApi;

use PcComponentes\OpenApiMessagingContext\AsyncApi\AsyncApiParser;
use PcComponentes\OpenApiMessagingContext\Tests\Messaging\DomainEventFake;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Yaml;

Expand All @@ -12,7 +13,7 @@ final class AsyncApiParserTest extends TestCase
public function given_valid_schema_when_parse_v12_then_get_parsed_schema(): void
{
$allSpec = Yaml::parse(file_get_contents(__DIR__ . '/valid-asyncapi-v12-spec.yaml'));
$schema = (new AsyncApiParser($allSpec))->parse('pccomponentes.test.testtopic');
$schema = (new AsyncApiParser($allSpec))->parse(DomainEventFake::messageName());
$jsonCompleted = '{"type":"object","required":["message_id","type"],"properties":{"message_id":{"type":"string"},"type":{"type":"string"},"attributes":{"type":"object","required":["some_attribute"],"properties":{"some_attribute":{"type":"string"}}}}}';
$this->assertJsonStringEqualsJsonString(\json_encode($schema), $jsonCompleted);
}
Expand All @@ -30,7 +31,7 @@ public function given_valid_v12_schema_when_parse_non_existent_topic_then_except
public function given_valid_schema_when_parse_v20_then_get_parsed_schema(): void
{
$allSpec = Yaml::parse(file_get_contents(__DIR__ . '/valid-asyncapi-v20-spec.yaml'));
$schema = (new AsyncApiParser($allSpec))->parse('pccomponentes.test.testtopic');
$schema = (new AsyncApiParser($allSpec))->parse(DomainEventFake::messageName());
$jsonCompleted = '{"type":"object","required":["message_id","type"],"properties":{"message_id":{"type":"string"},"type":{"type":"string"},"attributes":{"type":"object","required":["some_attribute"],"properties":{"some_attribute":{"type":"string"}}}}}';
$this->assertJsonStringEqualsJsonString(\json_encode($schema), $jsonCompleted);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/AsyncApi/valid-asyncapi-v12-spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ info:
baseTopic: 'pccomponentes.test'

topics:
testtopic:
1.domain_event.test_context.test_name:
publish:
$ref: "#/components/messages/TestTopicRef"

Expand Down Expand Up @@ -35,4 +35,4 @@ components:
- some_attribute
properties:
some_attribute:
type: string
type: string
Loading

0 comments on commit 26eebe7

Please sign in to comment.