diff --git a/.travis.yml b/.travis.yml index dcdd35ac..7104710a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ matrix: - php: 7.3 env: - - SYMFONY_VERSION=2.8.* SYMFONY_PHPUNIT_VERSION=6.5 + - SYMFONY_VERSION=2.8.* SYMFONY_PHPUNIT_VERSION=6.5 COMPOSER_ROOT_VERSION=v1.26.0 before_install: - phpenv config-add travis/z_php.ini diff --git a/Core/Handlers/MessageHandler.php b/Core/Handlers/MessageHandler.php index 6f5f0b7b..19eaf6ea 100644 --- a/Core/Handlers/MessageHandler.php +++ b/Core/Handlers/MessageHandler.php @@ -485,7 +485,7 @@ private function addCommonErrorHeadersToEnvelope(ErrorExchangeEnvelope $envelope $envelope->setHeader(RetryExchangeEnvelope::HEADER_LAST_RETRY_AT, round(microtime(true) * 1000)); } - $envelope->setHeader(ErrorExchangeEnvelope::HEADER_CREATED_AT, round(microtime(true) * 1000)); + $envelope->setHeader(ErrorExchangeEnvelope::HEADER_CREATED_AT, \round(\microtime(true) * 1000)); $envelope->setHeader(ErrorExchangeEnvelope::HEADER_ERROR_MESSAGE, $errorDescription); $envelope->setHeader(ErrorExchangeEnvelope::HEADER_ERROR_PROCESSOR_ID, $processor->getId()); $envelope->setHeader(ErrorExchangeEnvelope::HEADER_ERROR_PROCESSOR_DESCRIPTION, $processor->getDescription()); diff --git a/Resources/config/default_configured_consumers.yml b/Resources/config/default_configured_consumers.yml index 192ce6e4..3f3acd39 100644 --- a/Resources/config/default_configured_consumers.yml +++ b/Resources/config/default_configured_consumers.yml @@ -9,7 +9,7 @@ smartbox_integration_framework: description: A Generic Consumer for csv files calls: - [ setId, ['smartesb.consumers.generic_csv']] - - [setConfigurableStepsProvider,[@smartesb.steps_provider.csv_file]] + - [setConfigurableStepsProvider,['@smartesb.steps_provider.csv_file']] options: stop_on_eof: true @@ -22,9 +22,9 @@ smartbox_integration_framework: query_steps: - read_lines: result_name: processed_lines - max_lines: eval: options['batch_size'] + max_lines: "eval: options['batch_size']" query_result: - lines: eval: results['processed_lines'] + lines: "eval: results['processed_lines']" on_consume: ~ \ No newline at end of file diff --git a/Resources/config/default_configured_producers.yml b/Resources/config/default_configured_producers.yml index a543be1b..abf16f8c 100644 --- a/Resources/config/default_configured_producers.yml +++ b/Resources/config/default_configured_producers.yml @@ -8,7 +8,7 @@ smartbox_integration_framework: class: "%smartesb.producers.csv.class%" description: Producer to write out csv files calls: - - [ setId, ['smartesb.producers.generic_csv']] + - [ setId, ["smartesb.producers.generic_csv"]] - [setConfigurableStepsProvider,["@smartesb.steps_provider.csv_file"]] options: @@ -22,5 +22,5 @@ smartbox_integration_framework: description: Append lines to the csv file steps: - append_lines: - rows: eval: body['lines'] + rows: "eval: body['lines']" response: [] diff --git a/Resources/config/default_routing_itineraries.yml b/Resources/config/default_routing_itineraries.yml index 3f0eb569..8d4cec7d 100644 --- a/Resources/config/default_routing_itineraries.yml +++ b/Resources/config/default_routing_itineraries.yml @@ -1,3 +1,3 @@ auto_generated_routes: - resource: @smartesb.map.itineraries + resource: '@smartesb.map.itineraries' type: itineraries \ No newline at end of file diff --git a/SmartboxIntegrationFrameworkBundle.php b/SmartboxIntegrationFrameworkBundle.php index a9cd679a..8b82abb6 100644 --- a/SmartboxIntegrationFrameworkBundle.php +++ b/SmartboxIntegrationFrameworkBundle.php @@ -4,6 +4,7 @@ use Smartbox\Integration\FrameworkBundle\DependencyInjection\CompilerPasses\EventDeferringCompilerPass; use Smartbox\Integration\FrameworkBundle\DependencyInjection\CompilerPasses\MockWebserviceClientsCompilerPass; +use Smartbox\Integration\FrameworkBundle\DependencyInjection\CompilerPasses\SmokeTestCleaningPass; use Smartbox\Integration\FrameworkBundle\DependencyInjection\CompilerPasses\SmokeTestConnectivityCompilerPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; diff --git a/Tests/App/AppKernel.php b/Tests/App/AppKernel.php index e9fa6fd6..39701bf6 100644 --- a/Tests/App/AppKernel.php +++ b/Tests/App/AppKernel.php @@ -15,9 +15,11 @@ public function registerBundles() new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new \JMS\SerializerBundle\JMSSerializerBundle(), new \BeSimple\SoapBundle\BeSimpleSoapBundle(), + new \Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), new \Smartbox\CoreBundle\SmartboxCoreBundle(), new \Smartbox\Integration\FrameworkBundle\SmartboxIntegrationFrameworkBundle(), + new \Smartbox\Integration\CamelConfigBundle\SmartboxIntegrationCamelConfigBundle(), ]; } diff --git a/Tests/App/Entity/EntityX.php b/Tests/App/Entity/EntityX.php new file mode 100644 index 00000000..1c34f0fa --- /dev/null +++ b/Tests/App/Entity/EntityX.php @@ -0,0 +1,42 @@ +x = $x; + } + + /** + * @JMS\Type("integer") + * @JMS\Expose + * @JMS\Groups({"logs"}) + * + * @var int + */ + protected $x = 0; + + /** + * @return int + */ + public function getX() + { + return $this->x; + } + + /** + * @param int $x + */ + public function setX($x) + { + $this->x = $x; + } +} diff --git a/Tests/App/Producers/ErrorTriggerProducer.php b/Tests/App/Producers/ErrorTriggerProducer.php new file mode 100644 index 00000000..41a7bd8d --- /dev/null +++ b/Tests/App/Producers/ErrorTriggerProducer.php @@ -0,0 +1,88 @@ +getOptions(); + + if (self::$count < @$options[self::OPTION_NUMBER_ERRORS] || @$options[self::OPTION_FORCE]) { + $ex->getIn()->setBody(new EntityX(666)); + ++self::$count; + + if (@$options[self::OPTION_RECOVERABLE]) { + throw new SampleRecoverableException('test recoverable exception'); + } + + throw new \RuntimeException('test exception'); + } + } + + /** + * Key-Value array with the option name as key and the details as value. + * + * [OptionName => [description, array of valid values],..] + * + * @return array + */ + public function getOptionsDescriptions() + { + $options = [ + self::OPTION_RECOVERABLE => ['Whether the errors triggered are recoverable or not', []], + self::OPTION_FORCE => ['Force to throw the exception every time, not only "n" number of times', []], + self::OPTION_NUMBER_ERRORS => ['Define the number of times the exception will be throw', []] + ]; + + return $options; + } + + /** + * With this method this class can configure an OptionsResolver that will be used to validate the options. + * + * @param OptionsResolver $resolver + * + * @return mixed + */ + public function configureOptionsResolver(OptionsResolver $resolver) + { + $resolver->setRequired(self::OPTION_RECOVERABLE); + $resolver->setDefault(Protocol::OPTION_EXCHANGE_PATTERN, Protocol::EXCHANGE_PATTERN_IN_ONLY); + $resolver->setDefault(self::OPTION_RECOVERABLE, false); + $resolver->setDefault(self::OPTION_FORCE, false); + $resolver->setAllowedValues(Protocol::OPTION_EXCHANGE_PATTERN, [Protocol::EXCHANGE_PATTERN_IN_ONLY]); + $resolver->setDefault(self::OPTION_NUMBER_ERRORS, 1); + } +} diff --git a/Tests/App/Producers/HelperProducer.php b/Tests/App/Producers/HelperProducer.php new file mode 100644 index 00000000..e560cc19 --- /dev/null +++ b/Tests/App/Producers/HelperProducer.php @@ -0,0 +1,93 @@ +getOptions(); + + /** @var EntityX $x */ + $x = $ex->getIn()->getBody(); + if (empty($x) || !($x instanceof EntityX)) { + throw new \InvalidArgumentException('Expected entity of type EntityX'); + } + + $operand = (int) @$options[self::OPTION_OPERAND]; + + switch (@$options[self::OPTION_OPERATION]) { + case self::OPERATION_MULTIPLY: + $message = $this->messageFactory->createMessage(new EntityX($x->getX() * $operand)); + $ex->setOut($message); + break; + case self::OPERATION_ADD: + $message = $this->messageFactory->createMessage(new EntityX($x->getX() + $operand)); + $ex->setOut($message); + break; + } + } + + /** + * Key-Value array with the option name as key and the details as value. + * + * [OptionName => [description, array of valid values],..] + * + * @return array + */ + public function getOptionsDescriptions() + { + $options = [ + self::OPTION_OPERATION => ['Operation to apply to the EntityX in the body of the incoming messages', [ + self::OPERATION_ADD => 'Adds operand to the entityX value', + self::OPERATION_MULTIPLY => 'Multiplies operand by the entityX value', + ]], + self::OPTION_OPERAND => ['Operand to use (number)', []], + ]; + + return $options; + } + + /** + * With this method this class can configure an OptionsResolver that will be used to validate the options. + * + * @param OptionsResolver $resolver + * + * @return mixed + */ + public function configureOptionsResolver(OptionsResolver $resolver) + { + $resolver->setRequired(self::OPTION_OPERATION); + $resolver->setAllowedValues(self::OPTION_OPERATION, [self::OPERATION_ADD, self::OPERATION_MULTIPLY]); + + $resolver->setRequired(self::OPTION_OPERAND); + $resolver->setAllowedTypes(self::OPTION_OPERAND, ['numeric']); + + $resolver->setDefault(Protocol::OPTION_EXCHANGE_PATTERN, Protocol::EXCHANGE_PATTERN_IN_OUT); + } +} diff --git a/Tests/App/Producers/SpyProducer.php b/Tests/App/Producers/SpyProducer.php new file mode 100644 index 00000000..d4f65818 --- /dev/null +++ b/Tests/App/Producers/SpyProducer.php @@ -0,0 +1,94 @@ +getOptions(); + /** @var EntityX $x */ + $x = $ex->getIn()->getBody(); + + $path = $options[self::OPTION_PATH]; + + if (!array_key_exists($path, $this->array)) { + $this->array[$path] = []; + } + + if ($x instanceof SerializableArray) { + $this->array[$path] = $x->toArray(); + } else { + $this->array[$path][] = $x->getX(); + } + + } + + /** + * @return array + */ + public function getData($path) + { + if (array_key_exists($path, $this->array)) { + return $this->array[$path]; + } else { + return []; + } + } + + /** + * Key-Value array with the option name as key and the details as value. + * + * [OptionName => [description, array of valid values],..] + * + * @return array + */ + public function getOptionsDescriptions() + { + $options = [ + self::OPTION_PATH => ['Path to store the messages crossing this spy', []], + ]; + + return $options; + } + + /** + * With this method this class can configure an OptionsResolver that will be used to validate the options. + * + * @param OptionsResolver $resolver + * + * @return mixed + */ + public function configureOptionsResolver(OptionsResolver $resolver) + { + $resolver->setDefault(Protocol::OPTION_EXCHANGE_PATTERN, Protocol::EXCHANGE_PATTERN_IN_ONLY); + $resolver->setAllowedValues(Protocol::OPTION_EXCHANGE_PATTERN, [Protocol::EXCHANGE_PATTERN_IN_ONLY]); + + $resolver->setRequired(self::OPTION_PATH); + $resolver->setAllowedTypes(self::OPTION_PATH, ['string']); + } +} diff --git a/Tests/App/Resources/Fixtures/Csv/happy-comma.csv b/Tests/App/Resources/Fixtures/Csv/happy-comma.csv new file mode 100755 index 00000000..ce7db7b6 --- /dev/null +++ b/Tests/App/Resources/Fixtures/Csv/happy-comma.csv @@ -0,0 +1,8 @@ +a1,b1,c1 +a2,b2,c2 +a3,b3,c3 +a4,b4,c4 +a5,b5,c5 +a6,b6,c6 +a7,b7,c7 +hello diff --git a/Tests/App/Resources/Fixtures/Csv/happy.csv b/Tests/App/Resources/Fixtures/Csv/happy.csv new file mode 100755 index 00000000..a88f868f --- /dev/null +++ b/Tests/App/Resources/Fixtures/Csv/happy.csv @@ -0,0 +1,8 @@ +a1|b1|c1 +a2|b2|c2 +a3|b3|c3 +a4|b4|c4 +a5|b5|c5 +a6|b6|c6 +a7|b7|c7 +hello diff --git a/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/InvalidURI/CamelTestFlowInvalidURIError.xml b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/InvalidURI/CamelTestFlowInvalidURIError.xml new file mode 100644 index 00000000..589acc81 --- /dev/null +++ b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/InvalidURI/CamelTestFlowInvalidURIError.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/XMLBadFormed/CamelTestFlowXMLError.xml b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/XMLBadFormed/CamelTestFlowXMLError.xml new file mode 100644 index 00000000..e2640379 --- /dev/null +++ b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/XMLBadFormed/CamelTestFlowXMLError.xml @@ -0,0 +1,6 @@ + + + + to uri="custom://business_demo/getBoxInTransit"/> + + \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/findAbstractEndpointException/CamelTestFlowError.xml b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/findAbstractEndpointException/CamelTestFlowError.xml new file mode 100644 index 00000000..f61174e6 --- /dev/null +++ b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassFailures/findAbstractEndpointException/CamelTestFlowError.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassSuccess/CamelTestFlowSuccess.xml b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassSuccess/CamelTestFlowSuccess.xml new file mode 100644 index 00000000..f61174e6 --- /dev/null +++ b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassSuccess/CamelTestFlowSuccess.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassSuccess/Frozen/.gitkeep b/Tests/App/Resources/Fixtures/FlowsBuilderCompilerPassSuccess/Frozen/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Tests/App/Resources/Fixtures/FrozenFlows/.gitkeep b/Tests/App/Resources/Fixtures/FrozenFlows/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Tests/App/Resources/Fixtures/TestFlows/.project b/Tests/App/Resources/Fixtures/TestFlows/.project new file mode 100644 index 00000000..ae845e9f --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/.project @@ -0,0 +1,11 @@ + + + TestFlows + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecovery/recoverable.xml b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecovery/recoverable.xml new file mode 100644 index 00000000..deb819cf --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecovery/recoverable.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecovery/test.yml~ b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecovery/test.yml~ new file mode 100644 index 00000000..122cab00 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecovery/test.yml~ @@ -0,0 +1,17 @@ +steps: + - {type: configureHandler, name: async, retryDelay: 0 } + + - {type: handle, from: test://queues/recoveryAsyncRecoverable, in: 1, out: 1} + - {type: consume, uri: queue://main/recoveryAsyncRecoverable, amount: 1 } + + # Check that the message was partially processed + - { type: checkSpy, path: before_error/recoverable, values: [6]} + + # Check that the message didn't succeed + - { type: checkSpy, path: after_error/recoverable, values: []} + + # Check that we can recover and that previous steps are not repeated + - {type: consume, uri: queue://main/recoveryAsyncRecoverable, amount: 1 } + + - { type: checkSpy, path: before_error/recoverable, values: [6]} + - { type: checkSpy, path: after_error/recoverable, values: [11]} \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelay/recoverableWithDelay.xml b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelay/recoverableWithDelay.xml new file mode 100644 index 00000000..468e2d6a --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelay/recoverableWithDelay.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelay/test.yml b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelay/test.yml new file mode 100644 index 00000000..3c73509f --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelay/test.yml @@ -0,0 +1,34 @@ +steps: + - {type: handle, from: test://queues/delayRecoveryAsyncRecoverableWithDelay, in: 1, out: 1 } + + # Configure the handler with a 5 second delay + - {type: configureHandler, name: async, retryDelay: 5 } + + # Consume a first time + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelay, amount: 1 } + + # Check that the message was partially processed + - { type: checkSpy, path: before_error/recoverableDelay, values: [6]} + + # Try to consume the message again + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelay, amount: 1 } + + # Check that the message has still the same value + - { type: checkSpy, path: before_error/recoverableDelay, values: [6] } + + # Check that the message still didn't succeed + - { type: checkSpy, path: after_error/recoverableDelay, values: [] } + + # Wait until the end of the delay + - {type: wait, delay: 5 } + + # Check that we can recover and that previous steps are not repeated + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelay, amount: 1 } + + - { type: checkSpy, path: before_error/recoverableDelay, values: [6] } + + # Check the final value + - { type: checkSpy, path: after_error/recoverableDelay, values: [11] } + + # Reset the handler + - {type: configureHandler, name: async, retryDelay: 0 } diff --git a/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelayProgressive/recoverableWithDelayProgressive.xml b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelayProgressive/recoverableWithDelayProgressive.xml new file mode 100644 index 00000000..16feb8ed --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelayProgressive/recoverableWithDelayProgressive.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelayProgressive/test.yml b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelayProgressive/test.yml new file mode 100644 index 00000000..8970d6da --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/AsyncErrorRecoveryWithDelayProgressive/test.yml @@ -0,0 +1,51 @@ +steps: + - {type: handle, from: test://queues/delayRecoveryAsyncRecoverableWithDelayProgressive, in: 1, out: 1} + + # Configure the handler with a 5 second delay + - {type: configureHandler, name: async, retryDelay: 5, retryStrategy: progressive, retryDelayFactor: 2} + + # Consume a first time + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelayProgressive, amount: 1} + + # Check that the message was partially processed + - {type: checkSpy, path: before_error/recoverableDelayProgressive, values: [6]} + + # Check that we can recover and that previous steps are not repeated + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelayProgressive, amount: 1} + + - {type: checkSpy, path: before_error/recoverableDelayProgressive, values: [6]} + + # Check that the message still didn't succeed + - {type: checkSpy, path: after_error/recoverableDelayProgressive, values: []} + + # Wait until the end of the delay + - {type: wait, delay: 5} + + # Check that we can recover and that previous steps are not repeated + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelayProgressive, amount: 1} + + - {type: checkSpy, path: before_error/recoverableDelayProgressive, values: [6]} + + - {type: checkSpy, path: after_error/recoverableDelayProgressive, values: []} + + - {type: wait, delay: 5} + + # Check that we can the delay is still not finished + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelayProgressive, amount: 1} + + - {type: checkSpy, path: before_error/recoverableDelayProgressive, values: [6]} + + - {type: checkSpy, path: after_error/recoverableDelayProgressive, values: []} + + - {type: wait, delay: 5} + + # Check that we can recover and that previous steps are not repeated + - {type: consume, uri: queue://main/delayRecoveryAsyncRecoverableWithDelayProgressive, amount: 1} + + - {type: checkSpy, path: before_error/recoverableDelayProgressive, values: [6]} + + # Check the final value + - {type: checkSpy, path: after_error/recoverableDelayProgressive, values: [11]} + + # Reset the handler + - {type: configureHandler, name: async, retryDelay: 0} diff --git a/Tests/App/Resources/Fixtures/TestFlows/Csv/csv.xml b/Tests/App/Resources/Fixtures/TestFlows/Csv/csv.xml new file mode 100644 index 00000000..3274ab43 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Csv/csv.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Csv/csv2.xml b/Tests/App/Resources/Fixtures/TestFlows/Csv/csv2.xml new file mode 100644 index 00000000..91eb06d7 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Csv/csv2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Csv/csvGeneric.xml b/Tests/App/Resources/Fixtures/TestFlows/Csv/csvGeneric.xml new file mode 100644 index 00000000..ebbf0bb4 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Csv/csvGeneric.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Csv/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Csv/test.yml new file mode 100644 index 00000000..b8fb44b0 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Csv/test.yml @@ -0,0 +1,45 @@ +steps: + - {type: configureHandler, name: async, retryDelay: 0 } + + # We need to copy the recource we will use for the generic test + - {type: copyTmpResource, from: Csv/happy-comma.csv } + + # Consume one set of rows from the csv, the amount here is 1, but the consumer is set to read 3 lines + - {type: consume, uri: csv://test/consumer/read_happy, amount: 1 } + + # Test that we returned 3 lines as expected from the csv + - + type: checkSpyArray + path: csv/read_file1 + values: + lines: + - ['a1', 'b1', 'c1' ] + - ['a2', 'b2', 'c2' ] + - ['a3', 'b3', 'c3' ] + + # Consume one set of rows from the csv, the amount here is 1, but the consumer is set to read 2 lines + - {type: consume, uri: csv://test/consumer_2/read_happy, amount: 1 } + + # Test that we returned 2 lines as expected from the csv + - + type: checkSpyArray + path: csv/read_file2 + values: + lines: + - ['a1', 'b1', 'c1' ] + - ['a2', 'b2', 'c2' ] + + # And the csvGeneric + # Consume one set of rows from the csv, the amount here is 1, but the consumer is set to read 2 lines + - {type: consume, uri: csv://generic//tmp/integration-framework-test/happy-comma.csv, amount: 1 } + + # Test that we returned 1 lines as expected from the csv + - + type: checkSpyArray + path: csv/read_file3 + values: + lines: + - ['a1', 'b1', 'c1' ] + + # Clean up all the tmp files after our selves + - {type: cleanTmpResources } \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Logging/exception.xml b/Tests/App/Resources/Fixtures/TestFlows/Logging/exception.xml new file mode 100644 index 00000000..ceacb8f2 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Logging/exception.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Logging/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Logging/test.yml new file mode 100644 index 00000000..5e177a60 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Logging/test.yml @@ -0,0 +1,8 @@ +steps: + - {type: expectedException, class: 'Smartbox\Integration\FrameworkBundle\Tests\Fixtures\Exceptions\TestException'} + - {type: handle, from: test://logging/exception, in: 1} + - {type: checkLogs, message: "Event \"smartesb.handler.before_handle\" occurred", level: "Debug"} + - {type: checkLogs, message: "Event \"smartesb.handler.before_process\" occurred", level: "Debug"} + - {type: checkLogs, message: "Event \"smartesb.handler.after_process\" occurred", level: "Debug"} + - {type: checkLogs, message: "Event \"smartesb.handler.after_handle\" occurred", level: "Debug"} + - {type: checkLogs, message: "This is a test exception", level: "Error"} diff --git a/Tests/App/Resources/Fixtures/TestFlows/LoggingWithCustomErrorMessage/exception.xml b/Tests/App/Resources/Fixtures/TestFlows/LoggingWithCustomErrorMessage/exception.xml new file mode 100644 index 00000000..62989344 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/LoggingWithCustomErrorMessage/exception.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/LoggingWithCustomErrorMessage/test.yml b/Tests/App/Resources/Fixtures/TestFlows/LoggingWithCustomErrorMessage/test.yml new file mode 100644 index 00000000..72ca3a83 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/LoggingWithCustomErrorMessage/test.yml @@ -0,0 +1,8 @@ +steps: + - {type: expectedException, class: 'Smartbox\Integration\FrameworkBundle\Tests\Fixtures\Exceptions\TestException'} + - {type: handle, from: test://logging/exceptionWithMessage, in: 1} + - {type: checkLogs, message: 'Event "smartesb.handler.before_handle" occurred', level: 'Debug'} + - {type: checkLogs, message: 'Event "smartesb.handler.before_process" occurred', level: 'Debug'} + - {type: checkLogs, message: 'Event "smartesb.handler.after_process" occurred', level: 'Debug'} + - {type: checkLogs, message: 'Event "smartesb.handler.after_handle" occurred', level: 'Debug'} + - {type: checkLogs, message: 'This is a custom message', level: 'Error'} diff --git a/Tests/App/Resources/Fixtures/TestFlows/Multicast/multicast.xml b/Tests/App/Resources/Fixtures/TestFlows/Multicast/multicast.xml new file mode 100644 index 00000000..85c05ae2 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Multicast/multicast.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Multicast/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Multicast/test.yml new file mode 100644 index 00000000..3f39396c --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Multicast/test.yml @@ -0,0 +1,4 @@ +steps: + - { type: handle, from: "test://multicast", in: 1, out: 1} # We perform no aggregation, so the main exchange doesn't change + - { type: checkSpy, path: "multicast/a", values: [5]} + - { type: checkSpy, path: "multicast/b", values: [2]} \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Pipeline/pipeline.xml b/Tests/App/Resources/Fixtures/TestFlows/Pipeline/pipeline.xml new file mode 100644 index 00000000..bfeaad77 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Pipeline/pipeline.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Pipeline/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Pipeline/test.yml new file mode 100644 index 00000000..3a92676c --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Pipeline/test.yml @@ -0,0 +1,3 @@ +steps: + - { type: handle, from: "test://pipeline", in: 1, out: 5} + - { type: checkSpy, path: "pipeline/a", values: [5]} \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Queue/enqueue.xml b/Tests/App/Resources/Fixtures/TestFlows/Queue/enqueue.xml new file mode 100644 index 00000000..9d1c2145 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Queue/enqueue.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Queue/queueMulticast.xml b/Tests/App/Resources/Fixtures/TestFlows/Queue/queueMulticast.xml new file mode 100644 index 00000000..85f6823c --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Queue/queueMulticast.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Queue/queueSimple.xml b/Tests/App/Resources/Fixtures/TestFlows/Queue/queueSimple.xml new file mode 100644 index 00000000..e1f96fd3 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Queue/queueSimple.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Queue/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Queue/test.yml new file mode 100644 index 00000000..7328c7ea --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Queue/test.yml @@ -0,0 +1,10 @@ +steps: + - { type: handle, from: "test://queues/simple", in: 1, out: in } + - { type: consume, uri: "queue://main/simple", amount: 1} + - { type: checkSpy, path: "queue/simple/result", values: [1+10] } + + - { type: handle, from: "test://queues/multicast", in: 2, out: in } # Enqueue the message + - { type: consume, uri: "queue://main/multicast", amount: 3} # ...we need to consume also the 2 child exchanges + - { type: checkSpy, path: "queue/multicast/result", values: [2] } + - { type: checkSpy, path: "multicast/a", values: [2*5] } + - { type: checkSpy, path: "multicast/b", values: [2*2] } \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Router/router.xml b/Tests/App/Resources/Fixtures/TestFlows/Router/router.xml new file mode 100644 index 00000000..24248d59 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Router/router.xml @@ -0,0 +1,23 @@ + + + + + + + + + (msg.getBody().getX() % 2) == 0 + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Router/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Router/test.yml new file mode 100644 index 00000000..f26c389f --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Router/test.yml @@ -0,0 +1,3 @@ +steps: + - { type: handle, from: "test://router", in: 1, out: 2} + - { type: handle, from: "test://router", in: 2, out: 10} \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Simple/simple.xml b/Tests/App/Resources/Fixtures/TestFlows/Simple/simple.xml new file mode 100644 index 00000000..74cc581c --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Simple/simple.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Simple/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Simple/test.yml new file mode 100644 index 00000000..a5c46241 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Simple/test.yml @@ -0,0 +1,5 @@ +steps: + - { type: handle, from: "test://simple1", in: 1, out: 5} + - { type: handle, from: "test://simple2", in: 1, out: 2} + - { type: handle, from: "test://simple3", in: 1, out: 6} + - { type: handle, from: "test://simple4", in: 1, out: 12} \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Subroutines/multicastSubroutine.xml b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/multicastSubroutine.xml new file mode 100644 index 00000000..267411bc --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/multicastSubroutine.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Subroutines/routerSubroutine.xml b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/routerSubroutine.xml new file mode 100644 index 00000000..6fdf6467 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/routerSubroutine.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Subroutines/simpleSubroutine.xml b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/simpleSubroutine.xml new file mode 100644 index 00000000..6ee8f084 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/simpleSubroutine.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Subroutines/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/test.yml new file mode 100644 index 00000000..ab3ba39b --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Subroutines/test.yml @@ -0,0 +1,10 @@ +steps: + - { type: handle, from: "test://subroutines/simple", in: 1, out: 1 + 10} + + - { type: handle, from: "test://subroutines/router", in: 1, out: (1*2)+10 } + - { type: handle, from: "test://subroutines/router", in: 2, out: (2*5)+10 } + + + - { type: handle, from: "test://subroutines/multicast", in: 1, out: 1+10 } + - { type: checkSpy, path: "multicast/a", values: [1*5] } + - { type: checkSpy, path: "multicast/b", values: [1*2] } \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Throttler/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Throttler/test.yml new file mode 100644 index 00000000..e6a4b63e --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Throttler/test.yml @@ -0,0 +1,27 @@ +steps: + +# Send 2 messages, consume 2 messages, first one should pass, second one not + - { type: handle, from: "test://queues/throttler", in: 1, out: in } # Enqueue the message + - { type: handle, from: "test://queues/throttler", in: 1, out: in } # Enqueue the message + + - { type: consume, uri: "queue://main/throttler", amount: 2} # Consume the two messages + - { type: checkSpy, path: "throttler/before", values: [1,1] } + - { type: checkSpy, path: "throttler/after", values: [5] } # spy contains only one result (second message is moved to the failed transactions) + + - { type: wait, delay: 3 } # Wait until the end of the reset, then consume again and check that the message passes + + - { type: handle, from: "test://queues/throttler", in: 2, out: in } # The queue will be empty so we enqueue a new message + - { type: consume, uri: "queue://main/throttler", amount: 1} # we consume the new message + + - { type: checkSpy, path: "throttler/before", values: [1,1,2] } # We check the inputs of the three consumed messages + - { type: checkSpy, path: "throttler/after", values: [5,10] } # only the first and the last produced an output (the second was moved to the failed transactions) + + +## Sync + +# First message should pass + - { type: handle, from: "test://throttler", in: 1, out: 5} + +# Second message should fail + - { type: expectedException, class: 'Smartbox\Integration\FrameworkBundle\Core\Processors\Exceptions\ThrottlingLimitReachedException'} + - { type: handle, from: "test://throttler", in: 1, out: 1} diff --git a/Tests/App/Resources/Fixtures/TestFlows/Throttler/test_async_delayed.yml b/Tests/App/Resources/Fixtures/TestFlows/Throttler/test_async_delayed.yml new file mode 100644 index 00000000..1a6c6a0e --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Throttler/test_async_delayed.yml @@ -0,0 +1,23 @@ +steps: +#Async + +# Send 2 messages, consume 2 messages, first one should pass, second one not + - { type: handle, from: "test://queues/throttlerAsync", in: 1, out: in } # Enqueue the message + - { type: handle, from: "test://queues/throttlerAsync", in: 1, out: in } # Enqueue the message + + - { type: consume, uri: "queue://main/throttlerAsync", amount: 2} # Consume the two messages + - { type: checkSpy, path: "throttlerAsync/before", values: [1,1] } + - { type: checkSpy, path: "throttlerAsync/after", values: [5] } # spy contains only one result (second message is throttled) + + - { type: wait, delay: 4 } # Wait until the end of the reset, then consume again and check that the message passes + + - { type: consume, uri: "queue://main/throttle", amount: 1} # When a message gets throttled it will be put in the uri defined in smartesb.handlers.async + - { type: checkSpy, path: "throttlerAsync/before", values: [1,1] } + - { type: checkSpy, path: "throttlerAsync/after", values: [5,5] } # spy now contains both results + +# First message should pass + - { type: handle, from: "test://throttlerAsync", in: 1, out: 5} + +# Second message should throw an exception + - { type: expectedException, class: 'Smartbox\Integration\FrameworkBundle\Core\Processors\Exceptions\ThrottledException'} + - { type: handle, from: "test://throttlerAsync", in: 1, out: 1} diff --git a/Tests/App/Resources/Fixtures/TestFlows/Throttler/throttler.xml b/Tests/App/Resources/Fixtures/TestFlows/Throttler/throttler.xml new file mode 100644 index 00000000..0e072b7c --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Throttler/throttler.xml @@ -0,0 +1,25 @@ + + + + + + + 1+0 + + + + + + + + 1+0 + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Throttler/throttler_async_delayed.xml b/Tests/App/Resources/Fixtures/TestFlows/Throttler/throttler_async_delayed.xml new file mode 100644 index 00000000..31524905 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Throttler/throttler_async_delayed.xml @@ -0,0 +1,27 @@ + + + + + + + + 1+0 + + + + + + + + 1+0 + + + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/Transformer/test.yml b/Tests/App/Resources/Fixtures/TestFlows/Transformer/test.yml new file mode 100644 index 00000000..5d5faf00 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Transformer/test.yml @@ -0,0 +1,2 @@ +steps: + - { type: handle, from: "test://transformer", in: 1, out: 11} \ No newline at end of file diff --git a/Tests/App/Resources/Fixtures/TestFlows/Transformer/transformer.xml b/Tests/App/Resources/Fixtures/TestFlows/Transformer/transformer.xml new file mode 100644 index 00000000..acbc01ad --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/Transformer/transformer.xml @@ -0,0 +1,17 @@ + + + + + + + + msg.getBody().setX(msg.getBody().getX()+10) + + + + + diff --git a/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_catched.yml b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_catched.yml new file mode 100644 index 00000000..07669824 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_catched.yml @@ -0,0 +1,3 @@ +steps: + - { type: handle, from: "test://try1", in: 1, out: 22 } + - { type: handle, from: "test://try2", in: 1, out: 42 } diff --git a/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_not_catched.yml b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_not_catched.yml new file mode 100644 index 00000000..693b27c0 --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_not_catched.yml @@ -0,0 +1,4 @@ +steps: + - { type: expectedException, class: 'Smartbox\Integration\FrameworkBundle\Tests\Fixtures\Exceptions\SampleRecoverableException'} + - { type: handle, from: "test://try3", in: 1, out: 21 } + diff --git a/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_with_recovery.yml b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_with_recovery.yml new file mode 100644 index 00000000..177d164f --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/test_with_recovery.yml @@ -0,0 +1,17 @@ +steps: + - {type: configureHandler, name: async, retryDelay: 0 } + + - {type: handle, from: test://queues/tryAsync, in: 1, out: 1} + - {type: consume, uri: queue://main/tryAsync, amount: 1 } + + # Check that the message was partially processed + - { type: checkSpy, path: try/async/before_first_error, values: [6]} + - { type: checkSpy, path: try/async/after_first_error, values: []} + - { type: checkSpy, path: try/async/after_all, values: []} + + # Check that we can recover, that previous steps are not repeated and that the try/catch still works + - {type: consume, uri: queue://main/tryAsync, amount: 1 } + + - { type: checkSpy, path: try/async/before_first_error, values: [6]} + - { type: checkSpy, path: try/async/after_first_error, values: [11]} + - { type: checkSpy, path: try/async/after_all, values: [61]} diff --git a/Tests/App/Resources/Fixtures/TestFlows/TryCatch/tryCatch.xml b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/tryCatch.xml new file mode 100644 index 00000000..75bad74b --- /dev/null +++ b/Tests/App/Resources/Fixtures/TestFlows/TryCatch/tryCatch.xml @@ -0,0 +1,100 @@ + + + + + + + + + try/catch description + + + + + 1 == 2 + + + + + + true + + + + + + + + + + + + + + + + + + 1 == 2 + + + + + + true + + + + + + + + + + + + + + + + + + + 1 == 2 + + + + + + + + + + + + + + + + + + + + + + not isRecoverable(exception) + + + + + + + + + + + diff --git a/Tests/App/Resources/Fixtures/default/ok.json b/Tests/App/Resources/Fixtures/default/ok.json new file mode 100644 index 00000000..77588190 --- /dev/null +++ b/Tests/App/Resources/Fixtures/default/ok.json @@ -0,0 +1 @@ +{"_type":"Smartbox\\ApiBundle\\Entity\\OK","_apiVersion":"v1"} \ No newline at end of file diff --git a/Tests/App/config/config.yml b/Tests/App/config/config.yml index 94839ed4..88b3609b 100644 --- a/Tests/App/config/config.yml +++ b/Tests/App/config/config.yml @@ -1,6 +1,8 @@ imports: - { resource: parameters.yml } - { resource: services.yml } + - { resource: "@SmartboxIntegrationFrameworkBundle/Resources/config/default_configured_consumers.yml" } + - { resource: "@SmartboxIntegrationFrameworkBundle/Resources/config/default_configured_producers.yml" } framework: secret: "5yg6u543wrftg34" @@ -24,12 +26,17 @@ monolog: smartbox_core: cache_drivers: - null: - service: ~ - fixtures_path: "%kernel.root_dir%/JsonFixtures" + array: + service: "@array_cache_service" + fixtures_path: '%kernel.root_dir%/JsonFixtures' entities_namespaces: - 'Smartbox\Integration\Framework\Entity' +smartbox_integration_camel_config: + flows_directories: + - "%kernel.root_dir%/Resources/Fixtures/TestFlows" + frozen_flows_directory: "%kernel.root_dir%/Resources/Fixtures/FrozenFlows" + smartbox_integration_framework: defer_events_to_uri: queue://events queue_drivers: @@ -57,18 +64,104 @@ smartbox_integration_framework: retry_uri: queue://main/sync/retry throw_exceptions: true defer_new_exchanges: false + throttle_delay: 1 + throttle_delay_factor: 1 + throttle_strategy: fixed + throttle_uri: queue://main/throttle async: description: Message hanlder to handle messages comming from a queue retries_max: 5 retry_delay: 0 - failed_uri: queue://main/async/failed + failed_uri: queue://main/failed/async retry_uri: original throw_exceptions: false defer_new_exchanges: true throttle_delay: 1 throttle_delay_factor: 1 throttle_strategy: fixed - throttle_uri: queue://main/throttlerAsync + throttle_uri: queue://main/throttle + + flows_version: 0 + + consumers: + test_csv: + class: Smartbox\Integration\FrameworkBundle\Components\FileService\Csv\CsvConfigurableConsumer + description: Consume from csv file + calls: + - [setConfigurableStepsProvider,['@smartesb.steps_provider.csv_file']] + options: + delimiter: '|' + enclosure: '"' + escape_char: '\' + stop_on_eof: true + methods: + read_happy: + description: Read out some | csv file, 3 lines at a time + query_steps: + - read_lines: + result_name: xyxx + max_lines: 3 + filename: happy.csv + query_result: + lines: "eval: results['xyxx']" + on_consume: ~ + + test_csv_2: + class: Smartbox\Integration\FrameworkBundle\Components\FileService\Csv\CsvConfigurableConsumer + description: Consume from csv file + calls: + - [setConfigurableStepsProvider,['@smartesb.steps_provider.csv_file']] + options: + delimiter: ',' + enclosure: '"' + escape_char: '\' + stop_on_eof: true + methods: + read_happy: + description: Read from a , csv file, 2 lines at a time + # The path to where our write flow test should have written + query_steps: + - read_lines: + result_name: xyxx + max_lines: 2 + filename: happy-comma.csv + query_result: + lines: "eval: results['xyxx']" + on_consume: ~ + + producers: + test_csv: + class: Smartbox\Integration\FrameworkBundle\Components\FileService\Csv\CsvConfigurableProducer + description: Producer to write out csv files + calls: + - [setConfigurableStepsProvider,["@smartesb.steps_provider.csv_file"]] + options: + delimiter: ',' + enclosure: '"' + escape_char: '\' + methods: + write_happy: + description: Append lines to the csv file + steps: + - append_lines: + rows: "eval: body['lines']" + response: [] + mongo: + class: Smartbox\Integration\FrameworkBundle\Components\DB\DBConfigurableProducer + description: Producer to send messages to mongo + calls: + - [setConfigurableStepsProvider, ["@smartesb.steps_provider.nosql"]] + options: ~ + + methods: + insert: + description: Inserts a message in a mongo collection + steps: + - insertOne: + data: "eval: msg" + - flows_version: 0 \ No newline at end of file +doctrine: + dbal: + default_connection: default diff --git a/Tests/App/config/parameters.yml.dist b/Tests/App/config/parameters.yml.dist index 079e4893..b35f6b67 100644 --- a/Tests/App/config/parameters.yml.dist +++ b/Tests/App/config/parameters.yml.dist @@ -10,3 +10,4 @@ parameters: rabbitmq.password: guest rabbitmq.vhost: / rabbitmq.timeout: 15 + logging_exception_test.class: \Smartbox\Integration\FrameworkBundle\Tests\Fixtures\Exceptions\TestException diff --git a/Tests/App/config/routing_endpoints_test.yml b/Tests/App/config/routing_endpoints_test.yml index 56831e1d..8434f53b 100644 --- a/Tests/App/config/routing_endpoints_test.yml +++ b/Tests/App/config/routing_endpoints_test.yml @@ -1,3 +1,88 @@ smartbox_integration_platform: resource: "@SmartboxIntegrationFrameworkBundle/Resources/config/default_endpoint_routes.yml" - prefix: / \ No newline at end of file + prefix: / + +queues.generic: + path: "queue://{queue_driver}/{queue}" + defaults: + _protocol: "@smartesb.protocols.queue" + prefix: "test/" + persistent: false # For the tests we don't really want to persist our messages + + requirements: + queue: "[a-zA-Z0-9/]+" + +nosql.configurable: + path: "nosql://{nosql_driver}/{method}/{collection}" + defaults: + _protocol: "@smartesb.protocols.configurable.nosql" + _producer: "@smartesb.producers.mongo" + _handler: "@smartesb.handlers.sync" + prefix: "" + nosql_driver: main + requirements: + collection: "[_-a-zA-Z0-9]+" + nosql_driver: "[a-zA-Z0-9]+" + +## For testing purposes +test: + path: "test://{path}" + defaults: + _protocol: "@smartesb.protocols.direct" + requirements: + path: "[a-zA-Z0-9/_-]+" + +helper: + path: "helper://{operation}/{operand}" + defaults: + _protocol: "@smartesb.protocols.base" + _producer: "@producer.helper" + +spy: + path: "spy://{path}" + defaults: + _protocol: "@smartesb.protocols.base" + _producer: "@producer.spy" + requirements: + path: "[a-zA-Z0-9/_-]+" + +error.triggerer.recoverable: + path: "error://recoverable/{nb_errors}" + defaults: + _protocol: "@smartesb.protocols.base" + _producer: "@producer.error.triggerer" + recoverable: true + nb_errors: 1 + +error.triggerer.unrecoverable: + path: "error://unrecoverable" + defaults: + _protocol: "@smartesb.protocols.base" + _producer: "@producer.error.triggerer" + recoverable: false + +csv.test.consumer.read: + path: "csv://test/consumer/{method}" + defaults: + _protocol: "@smartesb.protocols.configurable.csv_file" + _consumer: "@smartesb.consumers.test_csv" + _handler: "@smartesb.handlers.async" + path: "%kernel.root_dir%/Resources/Fixtures/Csv/" + +csv.test.consumer.read_2: + path: "csv://test/consumer_2/{method}" + defaults: + _protocol: "@smartesb.protocols.configurable.csv_file" + _consumer: "@smartesb.consumers.test_csv_2" + _handler: "@smartesb.handlers.async" + path: "/tmp/integration-framework-test" + +csv.test.producer.write: + path: "csv://test/producer/{method}/{default_path}" + defaults: + _protocol: "@smartesb.protocols.configurable.csv_file" + _producer: "@smartesb.producers.test_csv" + root_path: "/tmp/integration-framework-test" + delimiter: "," + requirements: + default_path: ".*" \ No newline at end of file diff --git a/Tests/App/config/routing_itineraries.yml b/Tests/App/config/routing_itineraries.yml index 3f0eb569..8d4cec7d 100644 --- a/Tests/App/config/routing_itineraries.yml +++ b/Tests/App/config/routing_itineraries.yml @@ -1,3 +1,3 @@ auto_generated_routes: - resource: @smartesb.map.itineraries + resource: '@smartesb.map.itineraries' type: itineraries \ No newline at end of file diff --git a/Tests/App/config/services.yml b/Tests/App/config/services.yml index 8583b7e1..28fc73f2 100644 --- a/Tests/App/config/services.yml +++ b/Tests/App/config/services.yml @@ -1,4 +1,14 @@ services: - doctrine: - class: Symfony\Bridge\Doctrine\RegistryInterface - synthetic: true \ No newline at end of file + producer.error.triggerer: + class: Smartbox\Integration\FrameworkBundle\Tests\App\Producers\ErrorTriggerProducer + + producer.helper: + class: Smartbox\Integration\FrameworkBundle\Tests\App\Producers\HelperProducer + calls: + - [setMessageFactory, ['@smartesb.message_factory']] + + producer.spy: + class: Smartbox\Integration\FrameworkBundle\Tests\App\Producers\SpyProducer + + array_cache_service: + class: Smartbox\Integration\CamelConfigBundle\Tests\App\ArrayCacheService \ No newline at end of file diff --git a/Tests/Fixtures/Exceptions/TestException.php b/Tests/Fixtures/Exceptions/TestException.php new file mode 100644 index 00000000..55943066 --- /dev/null +++ b/Tests/Fixtures/Exceptions/TestException.php @@ -0,0 +1,14 @@ +setUp(); + $parser = new Parser(); + $finder = new Finder(); + $flowsDir = $this->getContainer()->getParameter('smartesb.flows_directories'); + $finder->name('*.yml'); + $finder->files()->in($flowsDir); + + $res = []; + + /** @var SplFileInfo $file */ + foreach ($finder as $file) { + $res[] = [$file->getRelativePath(), $parser->parse(file_get_contents($file->getRealpath()))]; + } + + return $res; + } + + /** + * @dataProvider flowsDataProvider + * + * @param $path + * @param array $conf + * + * @throws \Exception + */ + public function testFlow($path, array $conf) + { + if (!array_key_exists('steps', $conf)) { + throw new \Exception('Missing steps'); + } + + /** @var Logger $logger */ + $logger = $this->getContainer()->get('logger'); + foreach ($logger->getHandlers() as $handler) { + if ($handler instanceof DebugHandler) { + $this->loggerHandler = $handler; + break; + } + } + + foreach ($conf['steps'] as $step) { + $type = $step['type']; + switch ($type) { + case 'handle': + $this->handle($step); + break; + case 'checkSpy': + $this->checkSpy($step); + break; + case 'checkSpyArray': + $this->checkSpyArray($step); + break; + case 'consume': + $this->consume($step); + break; + case 'expectedException': + $this->expectedException($step); + break; + case 'checkLogs': + $this->checkLogs($step); + break; + case 'wait': + $this->wait($step); + break; + case 'configureHandler': + $this->configureHandler($step); + break; + case 'copyTmpResource': + $this->copyTmpResource($step); + break; + case 'cleanTmpResources': + $this->cleanTmpResources($step); + break; + } + } + } + + /** + * @param array $conf + * + * @throws \Exception + * @throws \Smartbox\Integration\FrameworkBundle\Core\Handlers\HandlerException + */ + private function handle(array $conf) + { + if (!array_key_exists('in', $conf) || !array_key_exists('from', $conf)) { + throw new \Exception('Missing parameter in handle step'); + } + + /** @var ExpressionEvaluator $evaluator */ + $evaluator = $this->getContainer()->get('smartesb.util.evaluator'); + + $in = $evaluator->evaluateWithVars($conf['in'], []); + + $message = $this->createMessage(new EntityX($in)); + $handler = $this->getContainer()->get('smartesb.helper')->getHandler('sync'); + $endpointFactory = $this->getContainer()->get('smartesb.endpoint_factory'); + $endpoint = $endpointFactory->createEndpoint($conf['from'], EndpointFactory::MODE_CONSUME); + + /** @var EntityX $result */ + $result = $handler->handle($message, $endpoint)->getBody(); + + if (isset($conf['out'])) { + $out = $evaluator->evaluateWithVars($conf['out'], ['in' => $in]); + $this->assertEquals($out, $result->getX(), 'Unexpected result when handling message from: '.$conf['from']); + } + } + + /** + * @param array $conf + * + * @throws \Exception + * @throws \Smartbox\Integration\FrameworkBundle\Core\Handlers\HandlerException + */ + private function checkSpy(array $conf) + { + if (!array_key_exists('path', $conf) || !array_key_exists('values', $conf)) { + throw new \Exception('Missing parameter in checkSpy step'); + } + $evaluator = $this->getContainer()->get('smartesb.util.evaluator'); + + $expectedValues = []; + foreach ($conf['values'] as $value) { + $expectedValues[] = $evaluator->evaluateWithVars($value, []); + } + + $values = $this->getContainer()->get('producer.spy')->getData($conf['path']); + + $this->assertEquals($expectedValues, $values, 'The spy '.$conf['path']." didn't contain the expected data"); + } + /** + * @param array $conf + * + * @throws \Exception + * @throws \Smartbox\Integration\FrameworkBundle\Core\Handlers\HandlerException + */ + private function checkSpyArray(array $conf) + { + if (!array_key_exists('path', $conf) || !array_key_exists('values', $conf)) { + throw new \Exception('Missing parameter in checkSpy step'); + } + + $expectedValues = $conf['values']; + + $values = $this->getContainer()->get('producer.spy')->getData($conf['path']); + + $this->assertSame($expectedValues, $values, 'The spy '.$conf['path']." didn't contain the expected data"); + } + + /** + * @param array $conf + * + * @throws \Exception + * @throws \Smartbox\Integration\FrameworkBundle\Core\Handlers\HandlerException + */ + private function consume(array $conf) + { + if (!array_key_exists('uri', $conf) || !array_key_exists('amount', $conf)) { + throw new \Exception('Missing parameter uri in consume step'); + } + + $uri = $conf['uri']; + + /** @var EndpointInterface $endpoint */ + $endpoint = $this->getContainer()->get('smartesb.endpoint_factory')->createEndpoint($uri, EndpointFactory::MODE_CONSUME); + $endpoint->consume($conf['amount']); + } + + private function expectedException(array $conf) + { + if (!array_key_exists('class', $conf)) { + $conf['class'] = ProcessingException::class; + } + + $this->expectException($conf['class']); + } + + private function checkLogs(array $conf) + { + if (!array_key_exists('level', $conf) || !array_key_exists('message', $conf)) { + throw new \Exception('Missing parameter in checkLogs step'); + } + + $level = $conf['level']; + $message = $conf['message']; + + $this->assertTrue($this->loggerHandler->hasRecordThatContains($message, $level)); + } + + /** + * @param array $conf + * + * @throws \Exception + */ + private function wait(array $conf) + { + if (!array_key_exists('delay', $conf)) { + throw new \Exception('Missing parameter in wait step'); + } + + $delay = $conf['delay']; + sleep($delay); + } + + /** + * @param array $conf + * + * @throws \Exception + */ + private function configureHandler(array $conf) + { + if (!array_key_exists('name', $conf)) { + throw new \Exception('Missing parameter in configureHandler step'); + } + + /** @var MessageHandler $handler */ + $handler = $this->getContainer()->get('smartesb.helper')->getHandler($conf['name']); + if (array_key_exists('retryDelay', $conf)) { + $handler->setRetryDelay($conf['retryDelay']); + } + + if (array_key_exists('retryStrategy', $conf)) { + $handler->setRetryStrategy($conf['retryStrategy']); + $handler->setRetryDelayFactor($conf['retryDelayFactor']); + } + } + + /** + * Copy a test resource to self::TMP_FOLDER for using + * + * conf['from'] The resource in %kernel.root_path%/Test/App/Resources you would like copied + * to self::TMP_FOLDER + * + * If the self::TMP_FOLDER does not exist it will be created + * + * @param array $conf + * + * @throws \Exception + */ + private function copyTmpResource(array $conf) + { + if (!array_key_exists('from', $conf)) { + throw new \Exception('Missing from parameter in copyTmpResource step'); + } + + $from_path = self::$kernel->getRootDir() . DIRECTORY_SEPARATOR . self::RESOURCE_FOLDER . DIRECTORY_SEPARATOR . $conf['from']; + + if (!is_file($from_path)) { + throw new \Exception("The resource path {$from_path} is not a file!"); + } + + + if (!is_dir(self::TMP_FOLDER)) { + mkdir(self::TMP_FOLDER); + } + + $to_path = self::TMP_FOLDER . DIRECTORY_SEPARATOR . basename($conf['from']); + + copy( $from_path, $to_path ); + } + + /** + * Clean up the copied and created file resources by removeing the folder self::TMP_FOLDER + * + * @param array $conf + * + * @throws \Exception + */ + private function cleanTmpResources(array $conf) + { + $files = glob(self::TMP_FOLDER . DIRECTORY_SEPARATOR . '*'); + foreach ($files as $file) { + if (is_file($file)) + unlink($file); // delete file + } + //remove the folder + rmdir(self::TMP_FOLDER); + } + + /** + * {@inheritdoc} + */ + protected function tearDown() + { + parent::tearDown(); + ErrorTriggerProducer::$count = 0; + } +} diff --git a/composer.json b/composer.json index 4837e945..527b8ec9 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,9 @@ "doctrine/doctrine-bundle": "^1.6", "sensio/generator-bundle": "~2.3", "mongodb/mongodb": "~1.0", - "jms/serializer-bundle": "*" + "jms/serializer-bundle": "*", + "smartbox/camel-config-bundle": "dev-tidy/move-tests as v1.0.0", + "ext-mongodb": "*" }, "scripts": { "post-install-cmd": [