From 3feaf6c5f77bb8b18747473e33e8a333de313f5e Mon Sep 17 00:00:00 2001 From: Nicolas Filotto Date: Thu, 30 May 2024 16:00:07 +0200 Subject: [PATCH] Ref #276: Propose an integration test framework (#277) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add camel jetty integration test * Provide a way to easily deploy Camel routes written in Java by implementing `CamelRouteSupplier` * Provide a way to easily deploy Camel routes written using Blueprint DSL by adding them into `OSGI-INF/blueprint` * Allow remote debug by adding the system property `camel.karaf.itest.debug` to the build command * Allow providing hints to the framework thanks to the annotation `CamelKarafTestHint` * Allow to manage several Camel contexts * Provide samples to show how to use it --------- Co-authored-by: François de Parscau <116000379+f2par0@users.noreply.github.com> Co-authored-by: Francois de Parscau --- components/camel-blueprint/pom.xml | 4 +- components/pom.xml | 17 + features/src/main/feature/camel-features.xml | 16 +- tests/camel-integration-test/pom.xml | 31 +- .../camel/itests/AbstractCamelComponent.java | 97 ---- ...AbstractCamelComponentResultFileBased.java | 28 -- .../camel/itests/AbstractCamelKarafITest.java | 160 ------ ...bstractCamelKarafResultFileBasedITest.java | 74 --- .../camel/itests/AbstractCamelRouteITest.java | 466 ++++++++++++++++++ .../itests/AbstractCamelRouteLauncher.java | 65 +++ .../AbstractCamelRouteWithBundleITest.java | 55 +++ ...amelSingleFeatureResultFileBasedRoute.java | 54 ++ ...ingleFeatureResultFileBasedRouteITest.java | 29 ++ ...leFeatureResultFileBasedRouteSupplier.java | 36 ++ ...amelSingleFeatureResultMockBasedRoute.java | 54 ++ ...ngleFeatureResultMockBasedRouteITest.java} | 21 +- ...eFeatureResultMockBasedRouteSupplier.java} | 4 +- .../AbstractCamelSingleFeatureRouteITest.java | 51 ++ ...stractCamelSingleFeatureRouteSupplier.java | 93 ++++ .../camel/itests/AvailablePortProvider.java | 68 +++ .../camel/itests/CamelContextProvider.java | 59 +++ .../camel/itests/CamelKarafTestHint.java | 50 ++ .../camel/itests/CamelRouteSupplier.java | 26 + ...amelSingleFeatureResultFileBasedRoute.java | 56 +++ ...amelSingleFeatureResultMockBasedRoute.java | 28 ++ .../camel/itests/CamelSingleFeatureRoute.java | 62 +++ .../itests/CamelSuppliedRouteLauncher.java | 140 ++++++ .../karaf/camel/itests/ExternalResource.java | 46 ++ .../itests/GenericContainerResource.java | 97 ++++ .../itests/PaxExamWithExternalResource.java | 139 ++++++ .../karaf/camel/itests/TemporaryFile.java | 74 +++ .../org/apache/karaf/camel/itests/Utils.java | 97 +++- .../karaf/camel/itests/CamelFileITest.java | 31 -- tests/examples/blueprint-dsl-only/pom.xml | 32 ++ .../resources/OSGI-INF/blueprint/route1.xml | 27 + .../resources/OSGI-INF/blueprint/route2.xml | 27 + .../CamelBlueprintExampleWithBothTest.java | 65 +++ tests/examples/java-dsl-only/pom.xml | 32 ++ .../test/CamelExampleRouteFirstSupplier.java | 35 ++ .../test/CamelExampleRouteSecondSupplier.java | 35 ++ .../itest/CamelJavaExampleFirstOnlyTest.java | 57 +++ .../itest/CamelJavaExampleWithBothTest.java | 63 +++ tests/examples/mixed/pom.xml | 32 ++ .../test/CamelExampleRouteSupplier.java | 73 +++ .../resources/OSGI-INF/blueprint/route.xml | 76 +++ .../examples/itest/CamelExampleTest.java | 114 +++++ tests/{components => examples}/pom.xml | 20 +- tests/features/camel-amqp/pom.xml | 41 ++ .../resources/OSGI-INF/blueprint/route.xml | 75 +++ .../karaf/camel/itest/CamelAmqpITest.java | 67 +++ .../camel-core}/pom.xml | 8 +- .../camel/test/CamelFileRouteSupplier.java} | 10 +- .../camel/test/CamelSedaRouteSupplier.java} | 32 +- .../karaf/camel/itest/CamelCoreITest.java | 77 +++ tests/features/camel-elasticsearch/pom.xml | 49 ++ .../test/CamelElasticsearchRouteSupplier.java | 80 +++ .../camel/itest/CamelElasticsearchITest.java | 82 +++ .../camel-jetty}/pom.xml | 6 +- .../camel/test/CamelJettyRouteSupplier.java | 72 +++ .../karaf/camel/itest/CamelJettyITest.java} | 11 +- tests/features/pom.xml | 248 ++++++++++ tests/pom.xml | 4 +- 62 files changed, 3415 insertions(+), 463 deletions(-) delete mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponent.java delete mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponentResultFileBased.java delete mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafITest.java delete mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafResultFileBasedITest.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteITest.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteLauncher.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteWithBundleITest.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRoute.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteITest.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteSupplier.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRoute.java rename tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/{AbstractCamelKarafResultMockBasedITest.java => AbstractCamelSingleFeatureResultMockBasedRouteITest.java} (61%) rename tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/{AbstractCamelComponentResultMockBased.java => AbstractCamelSingleFeatureResultMockBasedRouteSupplier.java} (90%) create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteITest.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteSupplier.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AvailablePortProvider.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelContextProvider.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelKarafTestHint.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelRouteSupplier.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultFileBasedRoute.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultMockBasedRoute.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureRoute.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSuppliedRouteLauncher.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/ExternalResource.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/GenericContainerResource.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/PaxExamWithExternalResource.java create mode 100644 tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/TemporaryFile.java delete mode 100644 tests/components/camel-file/src/test/java/org/apache/karaf/camel/itests/CamelFileITest.java create mode 100644 tests/examples/blueprint-dsl-only/pom.xml create mode 100644 tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route1.xml create mode 100644 tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route2.xml create mode 100644 tests/examples/blueprint-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelBlueprintExampleWithBothTest.java create mode 100644 tests/examples/java-dsl-only/pom.xml create mode 100644 tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteFirstSupplier.java create mode 100644 tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSecondSupplier.java create mode 100644 tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleFirstOnlyTest.java create mode 100644 tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleWithBothTest.java create mode 100644 tests/examples/mixed/pom.xml create mode 100644 tests/examples/mixed/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSupplier.java create mode 100644 tests/examples/mixed/src/main/resources/OSGI-INF/blueprint/route.xml create mode 100644 tests/examples/mixed/src/test/java/org/apache/karaf/camel/examples/itest/CamelExampleTest.java rename tests/{components => examples}/pom.xml (92%) create mode 100644 tests/features/camel-amqp/pom.xml create mode 100644 tests/features/camel-amqp/src/main/resources/OSGI-INF/blueprint/route.xml create mode 100644 tests/features/camel-amqp/src/test/java/org/apache/karaf/camel/itest/CamelAmqpITest.java rename tests/{components/camel-file => features/camel-core}/pom.xml (87%) rename tests/{components/camel-file/src/main/java/org/apache/karaf/camel/test/CamelFileComponent.java => features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelFileRouteSupplier.java} (84%) rename tests/{components/camel-seda/src/main/java/org/apache/karaf/camel/test/CamelSedaComponent.java => features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelSedaRouteSupplier.java} (55%) create mode 100644 tests/features/camel-core/src/test/java/org/apache/karaf/camel/itest/CamelCoreITest.java create mode 100644 tests/features/camel-elasticsearch/pom.xml create mode 100644 tests/features/camel-elasticsearch/src/main/java/org/apache/karaf/camel/test/CamelElasticsearchRouteSupplier.java create mode 100644 tests/features/camel-elasticsearch/src/test/java/org/apache/karaf/camel/itest/CamelElasticsearchITest.java rename tests/{components/camel-seda => features/camel-jetty}/pom.xml (87%) create mode 100644 tests/features/camel-jetty/src/main/java/org/apache/karaf/camel/test/CamelJettyRouteSupplier.java rename tests/{components/camel-seda/src/test/java/org/apache/karaf/camel/itests/CamelSedaITest.java => features/camel-jetty/src/test/java/org/apache/karaf/camel/itest/CamelJettyITest.java} (73%) create mode 100644 tests/features/pom.xml diff --git a/components/camel-blueprint/pom.xml b/components/camel-blueprint/pom.xml index ed10035ac..c4f8838a2 100644 --- a/components/camel-blueprint/pom.xml +++ b/components/camel-blueprint/pom.xml @@ -35,8 +35,8 @@ 2.4.0 - org.apache.camel.blueprint*, - org.apache.camel.util.blueprint* + org.apache.camel.blueprint*;version=${project.version}, + org.apache.camel.util.blueprint*;version=${project.version} jakarta.xml.bind*;version="[3,5)", diff --git a/components/pom.xml b/components/pom.xml index 9d8b5f7e9..da50a5644 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -319,4 +319,21 @@ camel-zookeeper-master + + + + + org.apache.servicemix.tooling + depends-maven-plugin + + + generate-depends-file + + generate-depends-file + + + + + + diff --git a/features/src/main/feature/camel-features.xml b/features/src/main/feature/camel-features.xml index f89886cca..c143d609f 100644 --- a/features/src/main/feature/camel-features.xml +++ b/features/src/main/feature/camel-features.xml @@ -832,12 +832,18 @@ camel-core wrap + spifly jackson http-client + mvn:org.glassfish.hk2/osgi-resource-locator/2.5.0-b42 + mvn:org.eclipse.parsson/parsson/${parson-version} + mvn:jakarta.json/jakarta.json-api/${jakarta-json-api-version} + wrap:mvn:io.opentelemetry/opentelemetry-api/${opentelemetry-version} + wrap:mvn:io.opentelemetry/opentelemetry-context/${opentelemetry-version} wrap:mvn:org.apache.httpcomponents/httpasyncclient/${httpasyncclient-version} - wrap:mvn:co.elastic.clients/elasticsearch-java/8.12.1 - wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client/8.12.1 - wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client-sniffer/8.12.1 + wrap:mvn:co.elastic.clients/elasticsearch-java/${elasticsearch-java-client-version} + wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client/${elasticsearch-java-client-version} + wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client-sniffer/${elasticsearch-java-client-sniffer-version} mvn:org.apache.camel.karaf/camel-elasticsearch/${project.version} @@ -846,8 +852,8 @@ jackson http-client wrap:mvn:org.apache.httpcomponents/httpasyncclient/${httpasyncclient-version} - wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client/8.12.1 - wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client-sniffer/8.12.1 + wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client/${elasticsearch-java-client-version} + wrap:mvn:org.elasticsearch.client/elasticsearch-rest-client-sniffer/${elasticsearch-java-client-sniffer-version} mvn:org.apache.camel.karaf/camel-elasticsearch-rest-client/${project.version} diff --git a/tests/camel-integration-test/pom.xml b/tests/camel-integration-test/pom.xml index c224d09fd..dab1b53a8 100644 --- a/tests/camel-integration-test/pom.xml +++ b/tests/camel-integration-test/pom.xml @@ -71,13 +71,21 @@ - org.apache.karaf.camel.itests;version=${camel.version} + org.apache.karaf.camel.itests;version=${project.version}, - org.apache.karaf.itests,org.ops4j.pax.exam,org.osgi.framework,org.junit, + org.apache.karaf.itests, + org.ops4j.pax.exam, + org.ops4j.pax.exam.options, + org.osgi.framework, + org.junit*, + org.apache.camel.blueprint;resolution:=optional;${camel.osgi.import.camel.version}, org.apache.camel*;${camel.osgi.import.camel.version}, + org.apache.karaf.features, + org.ops4j.pax.swissbox.tracker, org.osgi.service.*, - org.awaitility* + org.awaitility*, + org.slf4j geronimo-atinject_1.0_spec @@ -108,6 +116,11 @@ org.ops4j.pax.exam pax-exam-container-karaf + + org.ops4j.pax.exam + pax-exam-junit4 + compile + org.awaitility awaitility @@ -165,5 +178,17 @@ ${project.version} provided + + org.apache.camel.karaf + camel-blueprint + ${project.version} + provided + + + org.testcontainers + testcontainers + ${testcontainers-version} + provided + \ No newline at end of file diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponent.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponent.java deleted file mode 100644 index 47ca0254d..000000000 --- a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponent.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.karaf.camel.itests; - -import java.util.function.Function; - -import org.apache.camel.CamelContext; -import org.apache.camel.builder.RouteBuilder; -import org.apache.camel.karaf.core.OsgiDefaultCamelContext; -import org.apache.camel.model.ModelCamelContext; -import org.apache.camel.model.RouteDefinition; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.component.ComponentContext; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Deactivate; - -import static org.apache.karaf.camel.itests.Utils.toKebabCase; - -public abstract class AbstractCamelComponent { - - protected ModelCamelContext camelContext; - protected ServiceRegistration serviceRegistration; - - public String getBaseDir() { - return System.getProperty("project.target"); - } - - @Activate - public void activate(ComponentContext componentContext) throws Exception { - BundleContext bundleContext = componentContext.getBundleContext(); - camelContext = new OsgiDefaultCamelContext(bundleContext); - serviceRegistration = bundleContext.registerService(CamelContext.class, camelContext, null); - camelContext.start(); - camelContext.addRoutes(createRouteBuilder()); - } - - @Deactivate - public void deactivate() { - camelContext.stop(); - serviceRegistration.unregister(); - } - - public String getTestComponentName() { - return toKebabCase(this.getClass().getSimpleName()).replace("-component", "-test"); - } - - protected RouteBuilder createRouteBuilder() { - return new RouteBuilder() { - @Override - public void configure() { - if (producerEnabled()) { - configureProducer( - this, from("timer:producer?repeatCount=1").routeId("producer-%s".formatted(getTestComponentName())) - ); - } - if (consumerEnabled()) { - configureConsumer(consumerRoute().apply(this)); - } - } - }; - } - - protected boolean producerEnabled() { - return true; - } - - protected boolean consumerEnabled() { - return true; - } - - protected Function consumerRoute() { - return null; - } - - protected void configureProducer(RouteBuilder builder, RouteDefinition producerRoute) { - // Do nothing by default - } - - protected void configureConsumer(RouteDefinition consumerRoute) { - if (consumerRoute.hasCustomIdAssigned()) { - return; - } - consumerRoute.routeId("consumer-%s".formatted(getTestComponentName())); - } -} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponentResultFileBased.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponentResultFileBased.java deleted file mode 100644 index bc102b5e0..000000000 --- a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponentResultFileBased.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.karaf.camel.itests; - -import org.apache.camel.model.RouteDefinition; - -public abstract class AbstractCamelComponentResultFileBased extends AbstractCamelComponent { - - protected String getResultFileName() { - return getTestComponentName(); - } - - @Override - protected void configureConsumer(RouteDefinition consumerRoute) { - consumerRoute.toF("file:%s?fileName=%s", getBaseDir(), getResultFileName()); - } -} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafITest.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafITest.java deleted file mode 100644 index 8d4c6025d..000000000 --- a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafITest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.karaf.camel.itests; - - -import java.io.File; - -import org.apache.camel.CamelContext; -import org.apache.camel.ProducerTemplate; -import org.junit.After; -import org.junit.Before; -import org.osgi.framework.Bundle; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.util.stream.Stream; - -import org.apache.karaf.itests.KarafTestSupport; -import org.junit.Assert; -import org.ops4j.pax.exam.Configuration; - -import org.ops4j.pax.exam.CoreOptions; -import org.ops4j.pax.exam.Option; - -import org.ops4j.pax.exam.karaf.options.KarafDistributionOption; -import org.osgi.framework.BundleException; - -import static org.apache.karaf.camel.itests.Utils.toKebabCase; - -public abstract class AbstractCamelKarafITest extends KarafTestSupport { - - protected CamelContext context; - protected ProducerTemplate template; - - public String getVersion() { - return System.getProperty("project.version"); - } - - public String getBaseDir() { - return System.getProperty("project.target"); - } - - public File getUsersFile() { - // retrieve the users.properties file from the resources folder to avoid file duplication - return new File(System.getProperty("integration.test.project.resources"), "etc/users.properties"); - } - - public String getTestComponentName() { - return toKebabCase(this.getClass().getSimpleName()).replace("-itest", "-test"); - } - - @Override - public File getConfigFile(String path) { - if (path.equals("/etc/users.properties")) { - return getUsersFile(); - } - return super.getConfigFile(path); - } - - @Configuration - @Override - public Option[] config() { - // Workaround to fix the issue with the port already in use - String httpPort = Integer.toString(getAvailablePort(Integer.parseInt(MIN_HTTP_PORT), Integer.parseInt(MAX_HTTP_PORT))); - String rmiRegistryPort = Integer.toString(getAvailablePort(Integer.parseInt(MIN_RMI_REG_PORT), Integer.parseInt(MAX_RMI_REG_PORT))); - String rmiServerPort = Integer.toString(getAvailablePort(Integer.parseInt(MIN_RMI_SERVER_PORT), Integer.parseInt(MAX_RMI_SERVER_PORT))); - String sshPort = Integer.toString(getAvailablePort(Integer.parseInt(MIN_SSH_PORT), Integer.parseInt(MAX_SSH_PORT))); - - Option[] options = new Option[]{ - KarafDistributionOption.editConfigurationFilePut("etc/system.properties", "project.version", getVersion()), - KarafDistributionOption.editConfigurationFileExtend("etc/system.properties", "project.target", getBaseDir()), - KarafDistributionOption.features("mvn:org.apache.camel.karaf/apache-camel/"+ getVersion() + "/xml/features", "scr","camel-core"), - CoreOptions.mavenBundle().groupId("org.apache.camel.karaf").artifactId("camel-integration-test").versionAsInProject(), - KarafDistributionOption.editConfigurationFilePut("etc/org.ops4j.pax.web.cfg", "org.osgi.service.http.port", httpPort), - KarafDistributionOption.editConfigurationFilePut("etc/org.apache.karaf.management.cfg", "rmiRegistryPort", rmiRegistryPort), - KarafDistributionOption.editConfigurationFilePut("etc/org.apache.karaf.management.cfg", "rmiServerPort", rmiServerPort), - KarafDistributionOption.editConfigurationFilePut("etc/org.apache.karaf.shell.cfg", "sshPort", sshPort), - }; - return Stream.of(super.config(), options).flatMap(Stream::of).toArray(Option[]::new); - } - - @Before - public void init() throws Exception { - String testComponentName = getTestComponentName(); - installBundle("file://%s/%s-%s.jar".formatted(getBaseDir(), testComponentName, getVersion()),true); - assertBundleInstalled(testComponentName); - assertBundleInstalledAndRunning(testComponentName); - initCamelContext(); - initProducerTemplate(); - } - - private void initCamelContext() { - this.context = bundleContext.getService(bundleContext.getServiceReference(CamelContext.class)); - } - - private void initProducerTemplate() { - template = context.createProducerTemplate(); - template.start(); - } - - @After - public void destroy() { - destroyProducerTemplate(); - uninstallBundle(); - } - - private void destroyProducerTemplate() { - if (template != null) { - template.stop(); - } - } - - private void uninstallBundle() { - Bundle bundle = findBundleByName(getTestComponentName()); - if (bundle == null) { - return; - } - try { - bundle.uninstall(); - } catch (BundleException e) { - // Ignore me - } - } - - public static int getAvailablePort(int min, int max) { - for (int port = min; port <= max; port++) { - try (ServerSocket socket = new ServerSocket()) { - socket.setReuseAddress(true); - socket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), port), 1); - return socket.getLocalPort(); - } catch (Exception e) { - System.err.println("Port " + port + " not available, trying next one"); - } - } - throw new IllegalStateException("Can't find available network ports"); - } - - protected void assertBundleInstalledAndRunning(String name) { - Bundle bundle = findBundleByName(name); - Assert.assertNotNull("Bundle %s should be installed".formatted(name), bundle); - Assert.assertEquals(Bundle.ACTIVE, bundle.getState()); - //need to check with the command because the status may be Active while it's displayed as Waiting in the console - //because of an exception for instance - String bundles = executeCommand("bundle:list -s -t 0 | grep %s".formatted(name)); - Assert.assertTrue("bundle%s is in state %d /%s".formatted(bundle.getSymbolicName(), bundle.getState(), bundles), - bundles.contains("Active")); - } -} \ No newline at end of file diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafResultFileBasedITest.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafResultFileBasedITest.java deleted file mode 100644 index 3aa388a72..000000000 --- a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafResultFileBasedITest.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.karaf.camel.itests; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.TimeUnit; - -import org.awaitility.Awaitility; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public abstract class AbstractCamelKarafResultFileBasedITest extends AbstractCamelKarafITest { - - protected String getResultFileName() { - return getTestComponentName(); - } - - protected Charset getResultFileCharset() { - return StandardCharsets.UTF_8; - } - - protected int getTimeoutInSeconds() { - return 5; - } - - protected Path assertResultFileExists() { - Path filePath = Path.of(getBaseDir(), getResultFileName()); - Awaitility.await().atMost(getTimeoutInSeconds(), TimeUnit.SECONDS) - .until(() -> Files.exists(filePath)); - assertTrue(Files.exists(filePath)); - return filePath; - } - - protected void assertResultFileContains(String expectedFileContent) throws Exception { - assertEquals( - "The content of the result file is not correct", - expectedFileContent, Files.readString(assertResultFileExists(), getResultFileCharset()) - ); - } - - protected void assertResultFileIsSameAs(String expectedResultFileName) throws Exception { - assertResultFileContains( - Files.readString(createExpectedResultPath(expectedResultFileName)) - ); - } - - protected void assertResultFileIsSameAs(String expectedResultFileName, Charset encoding) throws Exception { - assertResultFileContains( - Files.readString(createExpectedResultPath(expectedResultFileName), encoding) - ); - } - - protected Path createExpectedResultPath(String expectedResultFileName) { - return Path.of(getBaseDir(), "test-classes", expectedResultFileName); - } -} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteITest.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteITest.java new file mode 100644 index 000000000..b821dd244 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteITest.java @@ -0,0 +1,466 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.itests; + + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.blueprint.BlueprintCamelContext; +import org.apache.camel.karaf.core.OsgiDefaultCamelContext; +import org.apache.karaf.itests.KarafTestSupport; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.CoreOptions; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.karaf.options.KarafDistributionConfigurationFilePutOption; +import org.ops4j.pax.exam.karaf.options.KarafDistributionOption; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.ops4j.pax.exam.OptionUtils.combine; + +public abstract class AbstractCamelRouteITest extends KarafTestSupport implements CamelContextProvider { + + public static final int CAMEL_KARAF_INTEGRATION_TEST_DEBUG_DEFAULT_PORT = 8889; + public static final String CAMEL_KARAF_INTEGRATION_TEST_DEBUG_PROPERTY = "camel.karaf.itest.debug"; + static final String CAMEL_KARAF_INTEGRATION_TEST_ROUTE_SUPPLIERS_PROPERTY = "camel.karaf.itest.route.suppliers"; + + private static final Logger LOG = LoggerFactory.getLogger(AbstractCamelRouteITest.class); + private final Map contexts = new ConcurrentHashMap<>(); + private final Map templates = new ConcurrentHashMap<>(); + private List requiredBundles; + + public String getCamelKarafVersion() { + String version = System.getProperty("camel.karaf.version"); + if (version == null) { + version = Utils.loadCamelKarafVersion(); + } + return version; + } + + public String getBaseDir() { + String location = System.getProperty("project.target"); + if (location == null) { + throw new IllegalStateException("The system property 'project.target' must be set to the target directory of" + + " the project or the method getBaseDir must be overridden to provide the base directory"); + } + return location; + } + + public File getUsersFile() { + // retrieve the users.properties file from the resources folder to avoid file duplication + String location = System.getProperty("users.file.location"); + if (location == null) { + location = Utils.loadUsersFileIfAbsent(getBaseDir()); + } + return new File(location); + } + + @Override + public File getConfigFile(String path) { + if (path.equals("/etc/users.properties")) { + return getUsersFile(); + } + return super.getConfigFile(path); + } + + @Configuration + @Override + public Option[] config() { + String camelKarafVersion = getCamelKarafVersion(); + if (camelKarafVersion == null) { + throw new IllegalArgumentException("The system property 'camel.karaf.version' must be set or the method " + + "getCamelKarafVersion must be overridden to provide the version of Camel Karaf to use"); + } + Option[] options = new Option[]{ + CoreOptions.systemProperty("project.target").value(getBaseDir()), + KarafDistributionOption.features("mvn:org.apache.camel.karaf/apache-camel/%s/xml/features".formatted(camelKarafVersion), "scr", getMode().getFeatureName()), + CoreOptions.mavenBundle().groupId("org.apache.camel.karaf").artifactId("camel-integration-test").version(camelKarafVersion) + }; + Option[] combine = combine(updatePorts(super.config()), options); + if (isDebugModeEnabled()) { + combine = combine(combine, KarafDistributionOption.debugConfiguration(Integer.toString(getDebugPort()), true)); + } + if (hasExternalResources()) { + combine = combine(combine, getExternalResourceOptions()); + } + if (hasCamelRouteSupplierFilter()) { + combine = combine(combine, getCamelRouteSupplierFilter()); + } + return combine(combine, getAdditionalOptions()); + } + + /** + * Indicates whether the debug mode is enabled or not. The debug mode is enabled when the system property + * {@link #CAMEL_KARAF_INTEGRATION_TEST_DEBUG_PROPERTY} is set. + * @return {@code true} if the debug mode is enabled, {@code false} otherwise + */ + private boolean isDebugModeEnabled() { + return System.getProperty(CAMEL_KARAF_INTEGRATION_TEST_DEBUG_PROPERTY) != null; + } + + /** + * Returns the debug port to use when the debug mode is enabled, corresponding to the value of the system property + * {@link #CAMEL_KARAF_INTEGRATION_TEST_DEBUG_PROPERTY}. The default value is {@link #CAMEL_KARAF_INTEGRATION_TEST_DEBUG_DEFAULT_PORT}. + * @return the debug port + */ + private int getDebugPort() { + return Integer.getInteger(CAMEL_KARAF_INTEGRATION_TEST_DEBUG_PROPERTY, CAMEL_KARAF_INTEGRATION_TEST_DEBUG_DEFAULT_PORT); + } + + /** + * Update the ports in the given options to work around the issue with the default method {@code getAvailablePort} + * that doesn't seem to work properly on macOS because the ports are not modified when a Karaf instance is already + * started locally. + */ + private static Option[] updatePorts(Option[] options) { + for (int i = 0; i < options.length; i++) { + Option option = options[i]; + if (option instanceof KarafDistributionConfigurationFilePutOption putOption) { + if (putOption.getConfigurationFilePath().equals("etc/org.ops4j.pax.web.cfg") && putOption.getKey().equals("org.osgi.service.http.port")) { + String httpPort = Integer.toString(Utils.getAvailablePort(Integer.parseInt(MIN_HTTP_PORT), Integer.parseInt(MAX_HTTP_PORT))); + options[i] = KarafDistributionOption.editConfigurationFilePut(putOption.getConfigurationFilePath(), putOption.getKey(), httpPort); + } else if (putOption.getConfigurationFilePath().equals("etc/org.apache.karaf.management.cfg")) { + if (putOption.getKey().equals("rmiRegistryPort")) { + String rmiRegistryPort = Integer.toString(Utils.getAvailablePort(Integer.parseInt(MIN_RMI_REG_PORT), Integer.parseInt(MAX_RMI_REG_PORT))); + options[i] = KarafDistributionOption.editConfigurationFilePut(putOption.getConfigurationFilePath(), putOption.getKey(), rmiRegistryPort); + } else if (putOption.getKey().equals("rmiServerPort")) { + String rmiServerPort = Integer.toString(Utils.getAvailablePort(Integer.parseInt(MIN_RMI_SERVER_PORT), Integer.parseInt(MAX_RMI_SERVER_PORT))); + options[i] = KarafDistributionOption.editConfigurationFilePut(putOption.getConfigurationFilePath(), putOption.getKey(), rmiServerPort); + } + } else if (putOption.getConfigurationFilePath().equals("etc/org.apache.karaf.shell.cfg") && putOption.getKey().equals("sshPort")) { + String sshPort = Integer.toString(Utils.getAvailablePort(Integer.parseInt(MIN_SSH_PORT), Integer.parseInt(MAX_SSH_PORT))); + options[i] = KarafDistributionOption.editConfigurationFilePut(putOption.getConfigurationFilePath(), putOption.getKey(), sshPort); + } + } + } + return options; + } + + @NotNull + private static Option[] getExternalResourceOptions() { + return PaxExamWithExternalResource.systemProperties().entrySet().stream() + .map(e -> CoreOptions.systemProperty(e.getKey()).value(e.getValue())) + .toArray(Option[]::new); + } + + private boolean hasExternalResources() { + CamelKarafTestHint hint = getClass().getAnnotation(CamelKarafTestHint.class); + return hint != null && hint.externalResourceProvider() != Object.class; + } + + + private Option getCamelRouteSupplierFilter() { + return CoreOptions.systemProperty(CAMEL_KARAF_INTEGRATION_TEST_ROUTE_SUPPLIERS_PROPERTY) + .value(String.join(",", getClass().getAnnotation(CamelKarafTestHint.class).camelRouteSuppliers())); + } + + private boolean hasCamelRouteSupplierFilter() { + CamelKarafTestHint hint = getClass().getAnnotation(CamelKarafTestHint.class); + return hint != null && hint.camelRouteSuppliers().length > 0; + } + + /** + * Returns the list of additional options to add to the configuration. + */ + protected Option[] getAdditionalOptions() { + return new Option[0]; + } + + @Before + public final void init() throws Exception { + installRequiredFeaturesRepositories(); + installRequiredFeatures(); + this.requiredBundles = installRequiredBundles(); + } + + /** + * Gives the list of features repositories' URI to install that are required for the test. + */ + protected List getRequiredFeaturesRepositories() { + return List.of(); + } + + private void installRequiredFeaturesRepositories() throws Exception { + for (String featuresRepository : getRequiredFeaturesRepositories()) { + addFeaturesRepository(featuresRepository); + } + } + + /** + * Gives the list of features to install that are required for the test. + */ + protected abstract List getRequiredFeatures(); + + /** + * Gives the list of all required features including the additional features specified in + * the {@link CamelKarafTestHint#additionalRequiredFeatures()}. + */ + private List getAllRequiredFeatures() { + CamelKarafTestHint hint = getClass().getAnnotation(CamelKarafTestHint.class); + if (hint == null || hint.additionalRequiredFeatures().length == 0) { + return getRequiredFeatures(); + } + List requiredFeatures = new ArrayList<>(getRequiredFeatures()); + requiredFeatures.addAll(List.of(hint.additionalRequiredFeatures())); + return requiredFeatures; + } + + private void installRequiredFeatures() throws Exception { + for (String featureName : getAllRequiredFeatures()) { + if (featureService.getFeature(featureName) == null) { + throw new IllegalArgumentException("Feature %s is not available".formatted(featureName)); + } + installAndAssertFeature(featureName); + } + } + + /** + * Installs the required bundles for the test. + * + * @return the list of the name of the installed bundles + * @throws Exception if an error occurs while installing a bundle + */ + protected List installRequiredBundles() throws Exception { + return List.of(); + } + + /** + * Indicates whether the test is a blueprint test or not. By default, it's not a blueprint test. + * @return {@code true} if the test is a blueprint test, {@code false} otherwise + */ + private static boolean isBlueprintTest(Class clazz) { + CamelKarafTestHint hint = clazz.getAnnotation(CamelKarafTestHint.class); + return hint != null && hint.isBlueprintTest(); + } + + private static Mode getMode(Class clazz) { + return getMode(isBlueprintTest(clazz)); + } + + private static Mode getMode(boolean blueprint) { + return blueprint ? Mode.BLUEPRINT : Mode.CORE; + } + + private Mode getMode() { + return getMode(getClass()); + } + + @After + public final void destroy() { + destroyProducerTemplates(); + uninstallRequiredBundles(); + uninstallRequiredFeatures(); + removeRequiredFeaturesRepositories(); + } + + private void uninstallRequiredBundles() { + if (requiredBundles == null) { + return; + } + for (String bundleName : requiredBundles) { + try { + uninstallBundle(bundleName); + } catch (Exception e) { + LOG.warn("Error while uninstalling bundle {}", bundleName, e); + } + } + } + + private void uninstallBundle(String bundleName) { + Bundle bundle = findBundleByName(bundleName); + if (bundle == null) { + return; + } + try { + bundle.uninstall(); + } catch (BundleException e) { + LOG.warn("Error while uninstalling bundle {}", bundleName, e); + } + } + + private void uninstallRequiredFeatures() { + for (String featureName : getAllRequiredFeatures()) { + try { + featureService.uninstallFeature(featureName); + } catch (Exception e) { + LOG.warn("Error while uninstalling feature {}", featureName, e); + } + } + } + + private void removeRequiredFeaturesRepositories() { + for (String featuresRepository : getRequiredFeaturesRepositories()) { + try { + featureService.removeRepository(new URI(featuresRepository)); + } catch (Exception e) { + LOG.warn("Error while removing features repository {}", featuresRepository, e); + } + } + } + + private void destroyProducerTemplates() { + templates.values().forEach(ProducerTemplate::stop); + templates.clear(); + } + + protected void assertBundleInstalledAndRunning(String name) { + Bundle bundle = findBundleByName(name); + Assert.assertNotNull("Bundle %s should be installed".formatted(name), bundle); + Assert.assertEquals(Bundle.ACTIVE, bundle.getState()); + //need to check with the command because the status may be Active while it's displayed as Waiting in the console + //because of an exception for instance + String bundles = executeCommand("bundle:list -s -t 0 | grep %s".formatted(name)); + Assert.assertTrue("bundle %s is in state %d /%s".formatted(bundle.getSymbolicName(), bundle.getState(), bundles), + bundles.contains("Active")); + } + + @Override + public CamelContext getContext(Class clazz) { + return contexts.computeIfAbsent(new CamelContextKey(clazz), key -> { + try { + return getMode(clazz).getCamelContextClass(bundleContext, key.getCamelContextName()); + } catch (InvalidSyntaxException e) { + throw new IllegalStateException("No CamelContext could be found matching the criteria", e); + } + }); + } + + @Override + public ProducerTemplate getTemplate(Class clazz) { + return templates.computeIfAbsent(new CamelContextKey(clazz), + key -> { + ProducerTemplate template = getContext(clazz).createProducerTemplate(); + template.start(); + return template; + }); + } + + @Override + public CamelContext getContext(String name, boolean isBlueprintTest) { + return contexts.computeIfAbsent(new CamelContextKey(name, isBlueprintTest), key -> { + try { + return getMode(isBlueprintTest).getCamelContextClass(bundleContext, key.getCamelContextName()); + } catch (InvalidSyntaxException e) { + throw new IllegalStateException("No CamelContext could be found matching the criteria", e); + } + }); + } + + @Override + public ProducerTemplate getTemplate(String name, boolean isBlueprintTest) { + return templates.computeIfAbsent(new CamelContextKey(name, isBlueprintTest), + key -> { + ProducerTemplate template = getContext(name, isBlueprintTest).createProducerTemplate(); + template.start(); + return template; + }); + } + + private enum Mode { + BLUEPRINT { + @Override + String getFeatureName() { + return "camel-blueprint"; + } + + @Override + Class getCamelContextClass() { + return BlueprintCamelContext.class; + } + }, + CORE { + @Override + String getFeatureName() { + return "camel-core"; + } + + @Override + Class getCamelContextClass() { + return OsgiDefaultCamelContext.class; + } + }; + + abstract String getFeatureName(); + + abstract Class getCamelContextClass(); + + CamelContext getCamelContextClass(BundleContext bundleContext, String name) throws InvalidSyntaxException { + ServiceReference[] references = bundleContext.getServiceReferences(CamelContext.class.getName(), null); + if (references == null) { + throw new IllegalStateException("No CamelContext available"); + } + for (ServiceReference reference : references) { + if (reference == null) { + continue; + } + CamelContext camelContext = (CamelContext) bundleContext.getService(reference); + if (camelContext.getClass().equals(getCamelContextClass()) + && (name == null || name.equals(camelContext.getName()))) { + return camelContext; + } + } + throw new IllegalStateException("No CamelContext could be found matching the criteria (mode = " + this + ", name = " + name + ")"); + } + } + + private static class CamelContextKey { + private final String name; + private final boolean blueprint; + + CamelContextKey(String name, boolean blueprint) { + this.name = name; + this.blueprint = blueprint; + } + + CamelContextKey(Class clazz) { + this(Utils.getCamelContextName(clazz), isBlueprintTest(clazz)); + } + + public String getCamelContextName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CamelContextKey that = (CamelContextKey) o; + return blueprint == that.blueprint && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name, blueprint); + } + } +} \ No newline at end of file diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteLauncher.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteLauncher.java new file mode 100644 index 000000000..f478a94cf --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteLauncher.java @@ -0,0 +1,65 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.itests; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.karaf.core.OsgiCamelContextPublisher; +import org.apache.camel.karaf.core.OsgiDefaultCamelContext; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Deactivate; + +/** + * The parent class of the Camel Karaf integration test for a specific Camel component/wrapper/feature. + */ +public abstract class AbstractCamelRouteLauncher { + + protected OsgiDefaultCamelContext camelContext; + protected ServiceRegistration serviceRegistration; + + @Activate + public void activate(ComponentContext componentContext) throws Exception { + BundleContext bundleContext = componentContext.getBundleContext(); + camelContext = new OsgiDefaultCamelContext(bundleContext); + String contextName = Utils.getCamelContextName(getClass()); + if (contextName == null) { + throw new IllegalStateException("Camel context name not set for " + getClass().getName() + " using @CamelKarafTestHint annotation"); + } + camelContext.getCamelContextExtension().setName(contextName); + serviceRegistration = new OsgiCamelContextPublisher(bundleContext).registerCamelContext(camelContext); + if (serviceRegistration == null) { + throw new IllegalStateException("Camel context registration failed for " + contextName + " most likely because the context name is already in use"); + } + camelContext.start(); + camelContext.addRoutes(createRouteBuilder()); + } + + @Deactivate + public void deactivate() { + if (camelContext == null) { + return; + } + camelContext.stop(); + if (serviceRegistration != null) { + serviceRegistration.unregister(); + } + } + + /** + * Create the RouteBuilder that will be used to create the Camel routes to test. + */ + protected abstract RouteBuilder createRouteBuilder(); +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteWithBundleITest.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteWithBundleITest.java new file mode 100644 index 000000000..96da477ce --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelRouteWithBundleITest.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.util.List; + +import org.ops4j.pax.exam.CoreOptions; +import org.ops4j.pax.exam.Option; + +import static org.ops4j.pax.exam.OptionUtils.combine; + +public abstract class AbstractCamelRouteWithBundleITest extends AbstractCamelRouteITest { + + + protected abstract String getTestBundleName(); + + public String getTestBundleVersion() { + return System.getProperty("project.version"); + } + + @Override + protected List installRequiredBundles() throws Exception { + String testBundleName = getTestBundleName(); + String testBundleVersion = getTestBundleVersion(); + if (testBundleVersion == null) { + throw new IllegalArgumentException("The system property project.version must be set to the version of the " + + "test bundle to install or the method getTestBundleVersion must be overridden to provide the version"); + } + installBundle("file://%s/%s-%s.jar".formatted(getBaseDir(), testBundleName, testBundleVersion), true); + assertBundleInstalledAndRunning(testBundleName); + return List.of(testBundleName); + } + + @Override + protected Option[] getAdditionalOptions() { + return combine( + super.getAdditionalOptions(), CoreOptions.systemProperty("project.version").value(getTestBundleVersion()) + ); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRoute.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRoute.java new file mode 100644 index 000000000..5be4e71c4 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRoute.java @@ -0,0 +1,54 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; + +public abstract class AbstractCamelSingleFeatureResultFileBasedRoute implements CamelSingleFeatureResultFileBasedRoute { + + private final CamelContextProvider provider; + private final String baseDir; + + protected AbstractCamelSingleFeatureResultFileBasedRoute(CamelContextProvider provider, String baseDir) { + this.provider = provider; + this.baseDir = baseDir; + } + + @Override + public String getBaseDir() { + return baseDir; + } + + @Override + public CamelContext getContext() { + return provider.getContext(); + } + + @Override + public ProducerTemplate getTemplate() { + return provider.getTemplate(); + } + + public void testRoutes() throws Exception { + triggerProducerRoute(); + executeTest(); + } + + protected abstract void executeTest() throws Exception; +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteITest.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteITest.java new file mode 100644 index 000000000..ff02b3134 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteITest.java @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.itests; + +import org.junit.Before; + +public abstract class AbstractCamelSingleFeatureResultFileBasedRouteITest extends AbstractCamelSingleFeatureRouteITest + implements CamelSingleFeatureResultFileBasedRoute { + + @Override + @Before + public void triggerProducerRoute() { + super.triggerProducerRoute(); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteSupplier.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteSupplier.java new file mode 100644 index 000000000..8a9ab9438 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultFileBasedRouteSupplier.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import org.apache.camel.model.RouteDefinition; + +public abstract class AbstractCamelSingleFeatureResultFileBasedRouteSupplier extends AbstractCamelSingleFeatureRouteSupplier { + + public String getBaseDir() { + return System.getProperty("project.target"); + } + + protected String getResultFileName() { + return getTestComponentName(); + } + + @Override + protected void configureConsumer(RouteDefinition consumerRoute) { + consumerRoute.toF("file:%s?fileName=%s", getBaseDir(), getResultFileName()); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRoute.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRoute.java new file mode 100644 index 000000000..2e6118b66 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRoute.java @@ -0,0 +1,54 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; + +public abstract class AbstractCamelSingleFeatureResultMockBasedRoute implements CamelSingleFeatureResultMockBasedRoute { + + private final CamelContextProvider provider; + + protected AbstractCamelSingleFeatureResultMockBasedRoute(CamelContextProvider provider) { + this.provider = provider; + } + + @Override + public CamelContext getContext() { + return provider.getContext(); + } + + @Override + public ProducerTemplate getTemplate() { + return provider.getTemplate(); + } + + public void testRoutes() throws Exception { + setupMock(); + try { + triggerProducerRoute(); + executeTest(); + } finally { + cleanMock(); + } + } + + protected void executeTest() throws Exception { + assertMockEndpointsSatisfied(); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafResultMockBasedITest.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRouteITest.java similarity index 61% rename from tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafResultMockBasedITest.java rename to tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRouteITest.java index 0c9e8149c..2b93ac55c 100644 --- a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelKarafResultMockBasedITest.java +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRouteITest.java @@ -16,29 +16,22 @@ */ package org.apache.karaf.camel.itests; -import org.apache.camel.component.mock.MockEndpoint; import org.junit.After; import org.junit.Before; -public abstract class AbstractCamelKarafResultMockBasedITest extends AbstractCamelKarafITest { +public abstract class AbstractCamelSingleFeatureResultMockBasedRouteITest extends AbstractCamelSingleFeatureRouteITest + implements CamelSingleFeatureResultMockBasedRoute { + @Override @Before public void setupMock() { - MockEndpoint endpoint = context.getEndpoint("mock:%s".formatted(getTestComponentName()), MockEndpoint.class); - endpoint.expectedMinimumMessageCount(1); - configureMock(endpoint); + CamelSingleFeatureResultMockBasedRoute.super.setupMock(); + triggerProducerRoute(); } + @Override @After public void cleanMock() { - MockEndpoint.resetMocks(context); - } - - protected void configureMock(MockEndpoint mock) { - // Do nothing by default - } - - protected void assertMockEndpointsSatisfied() throws InterruptedException { - MockEndpoint.assertIsSatisfied(context); + CamelSingleFeatureResultMockBasedRoute.super.cleanMock(); } } diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponentResultMockBased.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRouteSupplier.java similarity index 90% rename from tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponentResultMockBased.java rename to tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRouteSupplier.java index 4f3048614..21419fb25 100644 --- a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelComponentResultMockBased.java +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureResultMockBasedRouteSupplier.java @@ -14,12 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.karaf.camel.itests; import org.apache.camel.model.RouteDefinition; -public abstract class AbstractCamelComponentResultMockBased extends AbstractCamelComponent { - +public class AbstractCamelSingleFeatureResultMockBasedRouteSupplier extends AbstractCamelSingleFeatureRouteSupplier { protected String getResultMockName() { return getTestComponentName(); } diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteITest.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteITest.java new file mode 100644 index 000000000..522a5c16b --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteITest.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.util.List; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; + +public abstract class AbstractCamelSingleFeatureRouteITest extends AbstractCamelRouteWithBundleITest + implements CamelSingleFeatureRoute { + + @Override + public String getTestBundleName() { + return CamelSingleFeatureRoute.super.getTestBundleName(); + } + + /** + * Get the required features for this test. If more are needed, + * use {@link CamelKarafTestHint#externalResourceProvider()}. + */ + @Override + public final List getRequiredFeatures() { + return CamelSingleFeatureRoute.super.getRequiredFeatures(); + } + + @Override + public CamelContext getContext() { + return super.getContext(); + } + + @Override + public ProducerTemplate getTemplate() { + return super.getTemplate(); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteSupplier.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteSupplier.java new file mode 100644 index 000000000..cbd115517 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AbstractCamelSingleFeatureRouteSupplier.java @@ -0,0 +1,93 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.util.function.Function; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.RouteDefinition; + +import static org.apache.karaf.camel.itests.Utils.toKebabCase; + +/** + * The parent class of the camel route suppliers that test a specific Camel component/wrapper/feature. + *

+ * It creates and starts two simple Camel routes, the first one if {@link #producerEnabled()} returns {@code true} + * calls the producer of the tested Camel component and the second one if {@link #consumerEnabled()} returns {@code true} + * consumes what has been produced by the first route thanks to the consumer corresponding to the tested Camel component. + */ +public abstract class AbstractCamelSingleFeatureRouteSupplier implements CamelRouteSupplier { + + public String getTestComponentName() { + String name = toKebabCase(this.getClass().getSimpleName()); + if (!name.endsWith("-route-supplier")) { + throw new IllegalArgumentException("The route supplier class name doesn't match with the expected format: RouteSupplier"); + } + return name.replace("-route-supplier", "-test"); + } + + @Override + public void createRoutes(RouteBuilder builder) { + if (producerEnabled()) { + configureProducer( + builder, builder.fromF("direct:%s", getTestComponentName()).routeId("producer-%s".formatted(getTestComponentName())) + ); + } + if (consumerEnabled()) { + configureConsumer(consumerRoute().apply(builder)); + } + } + + /** + * Indicates whether the Camel component that is tested has a producer. Default is {@code true}. + */ + protected boolean producerEnabled() { + return true; + } + + /** + * Indicates whether the Camel component that is tested has a consumer. Default is {@code true}. + */ + protected boolean consumerEnabled() { + return true; + } + + /** + * Returns the function used to build the Camel route to test the consumer. Default is {@code null}. + */ + protected Function consumerRoute() { + return null; + } + + /** + * Executes the code needed to configure the Camel route to test the producer. + */ + protected void configureProducer(RouteBuilder builder, RouteDefinition producerRoute) { + // Do nothing by default + } + + /** + * Executes the code needed to configure the Camel route to test the consumer. + */ + protected void configureConsumer(RouteDefinition consumerRoute) { + if (consumerRoute.hasCustomIdAssigned()) { + return; + } + consumerRoute.routeId("consumer-%s".formatted(getTestComponentName())); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AvailablePortProvider.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AvailablePortProvider.java new file mode 100644 index 000000000..34763a940 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/AvailablePortProvider.java @@ -0,0 +1,68 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * A class that provides available ports for the test. + */ +public class AvailablePortProvider implements ExternalResource { + + private static final Set USED_PORTS = ConcurrentHashMap.newKeySet(); + + private final Map ports = new ConcurrentHashMap<>(); + + private final List neededPortPropertyNames; + + /** + * Create an AvailablePortProvider with the given name of property that will have the needed ports for the test as + * value. + * @param neededPortPropertyNames the name of the property that will have the needed ports as value + */ + public AvailablePortProvider(List neededPortPropertyNames) { + this.neededPortPropertyNames = neededPortPropertyNames; + } + + @Override + public void before() { + for (String name : neededPortPropertyNames) { + Utils.getNextAvailablePort(port -> { + if (USED_PORTS.add(port)) { + ports.put(name, port); + return true; + } + return false; + }); + } + } + + @Override + public void after() { + ports.values().forEach(USED_PORTS::remove); + } + + @Override + public Map properties() { + return ports.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString())); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelContextProvider.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelContextProvider.java new file mode 100644 index 000000000..eb6e62aa7 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelContextProvider.java @@ -0,0 +1,59 @@ +package org.apache.karaf.camel.itests; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; + +public interface CamelContextProvider { + + /** + * Returns the {@link CamelContext} associated with the given class according to the annotation + * {@link CamelKarafTestHint}. + */ + CamelContext getContext(Class clazz); + + /** + * Returns the {@link CamelContext} associated with the current class according to the annotation + * {@link CamelKarafTestHint}. + */ + default CamelContext getContext() { + return getContext(getClass()); + } + + /** + * Returns the {@link CamelContext} associated with the given name and type of test + */ + CamelContext getContext(String name, boolean isBlueprintTest); + + /** + * Returns the {@link CamelContext} associated with the given name for a blueprint test + */ + default CamelContext getContext(String name) { + return getContext(name, true); + } + + /** + * Returns the {@link ProducerTemplate} associated with the given class according to the annotation + * {@link CamelKarafTestHint}. + */ + ProducerTemplate getTemplate(Class clazz); + + /** + * Returns the {@link ProducerTemplate} associated with the current class according to the annotation + * {@link CamelKarafTestHint}. + */ + default ProducerTemplate getTemplate() { + return getTemplate(getClass()); + } + + /** + * Returns the {@link ProducerTemplate} associated with the given name and type of test + */ + ProducerTemplate getTemplate(String name, boolean isBlueprintTest); + + /** + * Returns the {@link ProducerTemplate} associated with the given name for a blueprint test + */ + default ProducerTemplate getTemplate(String name) { + return getTemplate(name, true); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelKarafTestHint.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelKarafTestHint.java new file mode 100644 index 000000000..8e9139e5b --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelKarafTestHint.java @@ -0,0 +1,50 @@ +package org.apache.karaf.camel.itests; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; + +/** + * The annotation uses to provide hints to the Camel Karaf test framework. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface CamelKarafTestHint { + + /** + * Specify the class that provides the methods to create all the external resources required by the test. + * In the provider class, each public static method that returns an instance of a subtype of {@link ExternalResource} + * with no parameters is considered as an {@link ExternalResource} supplier, so it will be invoked before executing + * the test and {@code PaxExamWithExternalResource} will take care of its lifecycle making sure that it is created and + * destroyed outside Karaf. + * + * @see PaxExamWithExternalResource + */ + Class externalResourceProvider() default Object.class; + + /** + * Indicates whether the test is a blueprint test or not. By default, it's not a blueprint test. + * @return {@code true} if the test is a blueprint test, {@code false} otherwise + */ + boolean isBlueprintTest() default false; + + /** + * Specify the list of additional features required by the test. + */ + String[] additionalRequiredFeatures() default {}; + + /** + * Specify the name of the Camel context to use in the test. + */ + String camelContextName() default ""; + + /** + * Specify the list of Camel route suppliers to use within the context of the test. By default, all detected + * Camel route suppliers are used. + */ + String[] camelRouteSuppliers() default {}; +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelRouteSupplier.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelRouteSupplier.java new file mode 100644 index 000000000..ec844954a --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelRouteSupplier.java @@ -0,0 +1,26 @@ +package org.apache.karaf.camel.itests; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; + +/** + * The interface that provides the ability to supply Camel routes to create and start in the Camel context. + */ +public interface CamelRouteSupplier { + + /** + * Configures the Camel context before creating the routes. + * + * @param camelContext the Camel context + */ + default void configure(CamelContext camelContext) { + // Do nothing by default + } + + /** + * Creates the Camel routes in the Camel context. + * + * @param builder the Camel route builder + */ + void createRoutes(RouteBuilder builder); +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultFileBasedRoute.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultFileBasedRoute.java new file mode 100644 index 000000000..a5a49faa3 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultFileBasedRoute.java @@ -0,0 +1,56 @@ +package org.apache.karaf.camel.itests; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import org.awaitility.Awaitility; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public interface CamelSingleFeatureResultFileBasedRoute extends CamelSingleFeatureRoute { + + default String getResultFileName() { + return getTestComponentName(); + } + + default Charset getResultFileCharset() { + return StandardCharsets.UTF_8; + } + + default Path assertResultFileExists() { + Path filePath = Path.of(getBaseDir(), getResultFileName()); + Awaitility.await().atMost(getTimeoutInSeconds(), TimeUnit.SECONDS) + .until(() -> Files.exists(filePath)); + assertTrue(Files.exists(filePath)); + return filePath; + } + + default void assertResultFileContains(String expectedFileContent) throws Exception { + assertEquals( + "The content of the result file is not correct", + expectedFileContent, Files.readString(assertResultFileExists(), getResultFileCharset()) + ); + } + + default void assertResultFileIsSameAs(String expectedResultFileName) throws Exception { + assertResultFileContains( + Files.readString(createExpectedResultPath(expectedResultFileName)) + ); + } + + default void assertResultFileIsSameAs(String expectedResultFileName, Charset encoding) throws Exception { + assertResultFileContains( + Files.readString(createExpectedResultPath(expectedResultFileName), encoding) + ); + } + + default Path createExpectedResultPath(String expectedResultFileName) { + return Path.of(getBaseDir(), "test-classes", expectedResultFileName); + } + + String getBaseDir(); +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultMockBasedRoute.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultMockBasedRoute.java new file mode 100644 index 000000000..c36e4b9b4 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureResultMockBasedRoute.java @@ -0,0 +1,28 @@ +package org.apache.karaf.camel.itests; + +import org.apache.camel.CamelContext; +import org.apache.camel.component.mock.MockEndpoint; + +public interface CamelSingleFeatureResultMockBasedRoute extends CamelSingleFeatureRoute { + + default void setupMock() { + MockEndpoint endpoint = getContext().getEndpoint("mock:%s".formatted(getTestComponentName()), MockEndpoint.class); + endpoint.setFailFast(false); + configureMock(endpoint); + } + + default void cleanMock() { + CamelContext context = getContext(); + if (context != null) { + MockEndpoint.resetMocks(context); + } + } + + default void configureMock(MockEndpoint mock) { + mock.expectedMinimumMessageCount(1); + } + + default void assertMockEndpointsSatisfied() throws InterruptedException { + MockEndpoint.assertIsSatisfied(getContext()); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureRoute.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureRoute.java new file mode 100644 index 000000000..5ac3758c1 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSingleFeatureRoute.java @@ -0,0 +1,62 @@ +package org.apache.karaf.camel.itests; + +import java.util.List; + +import org.apache.camel.CamelContext; +import org.apache.camel.Endpoint; +import org.apache.camel.Processor; +import org.apache.camel.ProducerTemplate; +import org.jetbrains.annotations.NotNull; + +import static org.apache.karaf.camel.itests.Utils.toKebabCase; + +public interface CamelSingleFeatureRoute { + + default int getTimeoutInSeconds() { + return 5; + } + + default String getTestComponentName() { + return getTestClassSimpleNameInKebabCase().replace("-itest", "-test"); + } + + default String getCamelFeatureName() { + return getTestClassSimpleNameInKebabCase().replace("-itest", ""); + } + + @NotNull + private String getTestClassSimpleNameInKebabCase() { + String name = toKebabCase(this.getClass().getSimpleName()); + if (!name.endsWith("-itest")) { + throw new IllegalArgumentException("The integration test class name doesn't match with the expected format: ITest"); + } + return name; + } + + default String getTestBundleName() { + return getTestComponentName(); + } + + default List getRequiredFeatures() { + return List.of(getCamelFeatureName()); + } + + default Processor getProcessorToCallOnSend() { + return exchange -> exchange.getMessage().setBody(getBodyToSend()); + } + + default String getBodyToSend() { + return getClass().getSimpleName(); + } + + CamelContext getContext(); + + ProducerTemplate getTemplate(); + + default void triggerProducerRoute() { + Endpoint endpoint = getContext().hasEndpoint("direct:%s".formatted(getTestComponentName())); + if (endpoint != null) { + getTemplate().send(endpoint, getProcessorToCallOnSend()); + } + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSuppliedRouteLauncher.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSuppliedRouteLauncher.java new file mode 100644 index 000000000..e2de01603 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/CamelSuppliedRouteLauncher.java @@ -0,0 +1,140 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.RouteDefinition; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.karaf.camel.itests.AbstractCamelRouteITest.CAMEL_KARAF_INTEGRATION_TEST_ROUTE_SUPPLIERS_PROPERTY; + +@CamelKarafTestHint(camelContextName = CamelSuppliedRouteLauncher.CAMEL_CONTEXT_NAME) +@Component( + name = "camel-supplied-route-launcher", + immediate = true +) +public class CamelSuppliedRouteLauncher extends AbstractCamelRouteLauncher implements ServiceListener { + + public static final String CAMEL_CONTEXT_NAME = "supplied-route-launcher"; + private static final Logger LOG = LoggerFactory.getLogger(CamelSuppliedRouteLauncher.class); + private final Map> routes = new ConcurrentHashMap<>(); + private final Set suppliers = ConcurrentHashMap.newKeySet(); + + @Override + public void activate(ComponentContext componentContext) throws Exception { + super.activate(componentContext); + camelContext.getBundleContext().addServiceListener(this); + loadSuppliers(); + } + + @Override + public void deactivate() { + suppliers.clear(); + super.deactivate(); + } + + private void loadSuppliers() { + String property = System.getProperty(CAMEL_KARAF_INTEGRATION_TEST_ROUTE_SUPPLIERS_PROPERTY); + if (property == null) { + return; + } + suppliers.addAll(List.of(property.split(","))); + } + + @SuppressWarnings("SuspiciousMethodCalls") + private boolean ignore(ServiceReference serviceReference) { + return !suppliers.isEmpty() && !suppliers.contains(serviceReference.getProperty("component.name")); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + // no routes to add here as they will be added by the listener + } + }; + } + + @Override + public void serviceChanged(ServiceEvent serviceEvent) { + ServiceReference serviceReference = serviceEvent.getServiceReference(); + if (ignore(serviceReference)) { + LOG.debug("Ignoring CamelRouteSupplier service: {}", serviceReference.getProperties()); + return; + } + if (serviceEvent.getType() == ServiceEvent.REGISTERED) { + if (camelContext.getBundleContext().getService(serviceReference) instanceof CamelRouteSupplier supplier) { + LOG.info("CamelRouteSupplier service registered: {} from the class {}", serviceReference.getProperties(), + supplier.getClass().getName()); + addRoutes(supplier); + } + } else if (serviceEvent.getType() == ServiceEvent.UNREGISTERING + && camelContext.getBundleContext().getService(serviceReference) instanceof CamelRouteSupplier supplier) { + LOG.info("CamelRouteSupplier service unregistered: {}", supplier.getClass().getName()); + removeRoutes(supplier); + } + } + + private void removeRoutes(CamelRouteSupplier supplier) { + try { + List routeDefinitions = routes.remove(supplier.getClass().getName()); + if (routeDefinitions == null) { + return; + } + camelContext.removeRouteDefinitions(routeDefinitions); + LOG.info("Route(s) removed from CamelRouteSupplier service: {}", supplier.getClass().getName()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void addRoutes(CamelRouteSupplier supplier) { + try { + List before = new ArrayList<>(camelContext.getRouteDefinitions()); + camelContext.addRoutes(new RouteBuilder() { + @Override + public void configure() { + supplier.configure(camelContext); + supplier.createRoutes(this); + } + }); + List added = new ArrayList<>(camelContext.getRouteDefinitions()); + added.removeAll(before); + if (routes.putIfAbsent(supplier.getClass().getName(), added) == null) { + LOG.info("Route(s) created from CamelRouteSupplier service: {}", supplier.getClass().getName()); + } else { + LOG.warn("Route(s) already created from CamelRouteSupplier service: {}", supplier.getClass().getName()); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/ExternalResource.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/ExternalResource.java new file mode 100644 index 000000000..22f6fe09b --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/ExternalResource.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.itests; + +import java.util.Map; + +/** + * An interface representing an external resource to set up before a test and guarantee to tear it down afterward. + * Compared to an {@link org.junit.rules.ExternalResource}, the methods {@link #before()} and {@link #after()} are + * executed outside Karaf container, so it can be used to set up external resources like a database, a message broker, + * etc. + * @see TemporaryFile + * @see GenericContainerResource + */ +public interface ExternalResource { + + /** + * Sets up the external resource. + */ + void before(); + + /** + * Tears down the external resource. + */ + void after(); + + /** + * Gives access to the properties of the external resource like a username, a password or a path, that will be + * provided to the Karaf instance as System properties. + */ + Map properties(); +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/GenericContainerResource.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/GenericContainerResource.java new file mode 100644 index 000000000..8a188ceed --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/GenericContainerResource.java @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; + +/** + * A JUnit ExternalResource that starts and stops a TestContainer. + * + * @param the type of the TestContainer + */ +public class GenericContainerResource> implements ExternalResource { + + private static final Logger LOG = LoggerFactory.getLogger(GenericContainerResource.class); + private final T container; + private final Map properties = new HashMap<>(); + private final List dependencies = new ArrayList<>(); + private final Consumer> onStarted; + + public GenericContainerResource(T container) { + this(container, t -> {}); + } + + /** + * Create a GenericContainerResource with the given TestContainer and a callback to be called when the container is + * started. + * @param container the TestContainer + * @param onStarted the callback to be called when the container is started + */ + public GenericContainerResource(T container, Consumer> onStarted) { + this.container = container; + this.onStarted = onStarted; + } + + @Override + public void before() { + container.start(); + onStarted.accept(this); + for (ExternalResource dependency : dependencies) { + dependency.properties().forEach(this::setProperty); + } + LOG.info("Container {} started", container.getDockerImageName()); + } + + @Override + public void after() { + container.stop(); + for (ExternalResource dependency : dependencies) { + try { + dependency.after(); + } catch (Exception e) { + LOG.warn("Error cleaning dependency: {}", dependency.getClass().getName(), e); + } + } + LOG.info("Container {} stopped", container.getDockerImageName()); + } + + @Override + public Map properties() { + return properties; + } + + public T getContainer() { + return container; + } + + public void setProperty(String key, String value) { + properties.put(key, value); + } + + public void addDependency(ExternalResource dependency) { + dependencies.add(dependency); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/PaxExamWithExternalResource.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/PaxExamWithExternalResource.java new file mode 100644 index 000000000..f8cdd5f3e --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/PaxExamWithExternalResource.java @@ -0,0 +1,139 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.manipulation.Sortable; +import org.junit.runner.manipulation.Sorter; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.ParentRunner; +import org.junit.runners.model.InitializationError; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.junit.impl.ProbeRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A fork of {@link PaxExam} that supports external resources which can be created and destroyed outside Karaf. + *

+ * This runner is intended to be used with {@link CamelKarafTestHint} annotation where + * {@link CamelKarafTestHint#externalResourceProvider()} is properly set in order to create the + * external resources that are needed by the test. Please note that due to the way PaxExam works, the class cannot be + * the same as the test class, but it can be a static inner class of it otherwise the class will need to be resolved + * within Karaf which is what we want to avoid. + * + * @see CamelKarafTestHint + */ +public class PaxExamWithExternalResource extends Runner implements Filterable, Sortable { + private static final Logger LOG = LoggerFactory.getLogger(PaxExamWithExternalResource.class); + private static final ThreadLocal current = new ThreadLocal<>(); + private final ParentRunner delegate; + private final List externalResources; + + public PaxExamWithExternalResource(Class testClass) throws InitializationError, InvocationTargetException, + IllegalAccessException { + this.externalResources = beforeAll(testClass); + try { + current.set(this); + this.delegate = new ProbeRunner(testClass); + } finally { + current.remove(); + } + } + + private List beforeAll(Class testClass) throws InvocationTargetException, IllegalAccessException { + CamelKarafTestHint annotation = testClass.getAnnotation(CamelKarafTestHint.class); + if (annotation != null && annotation.externalResourceProvider() != Object.class) { + List result = new ArrayList<>(); + for (Method m : annotation.externalResourceProvider().getMethods()) { + if (isExternalResourceSupplier(m)) { + ExternalResource externalResource = (ExternalResource) m.invoke(null); + externalResource.before(); + result.add(externalResource); + } + } + return result; + } + LOG.warn("Class {} is not annotated with @CamelKarafTestHint or externalResourceProvider is not set", testClass.getName()); + return List.of(); + } + + private boolean isExternalResourceSupplier(Method m) { + return ExternalResource.class.isAssignableFrom(m.getReturnType()) && m.getParameterTypes().length == 0 + && Modifier.isStatic(m.getModifiers()); + } + + @Override + public Description getDescription() { + return delegate.getDescription(); + } + + @Override + public void run(RunNotifier notifier) { + try { + delegate.run(notifier); + } finally { + afterAll(); + } + } + + private void afterAll() { + for (int i = externalResources.size() - 1; i >= 0; i--) { + try { + externalResources.get(i).after(); + } catch (Exception e) { + LOG.warn("Error while cleaning up external resource", e); + } + } + } + + @Override + public void filter(Filter filter) throws NoTestsRemainException { + delegate.filter(filter); + } + + @Override + public void sort(Sorter sorter) { + delegate.sort(sorter); + } + + static Map systemProperties() { + PaxExamWithExternalResource value = current.get(); + if (value == null) { + return Map.of(); + } + return value.externalResources.stream() + .map(ExternalResource::properties) + .map(Map::entrySet) + .flatMap(Set::stream) + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/TemporaryFile.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/TemporaryFile.java new file mode 100644 index 000000000..9e6094025 --- /dev/null +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/TemporaryFile.java @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.itests; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A temporary file that is deleted when the test is finished. + */ +public class TemporaryFile implements ExternalResource { + private static final Logger LOG = LoggerFactory.getLogger(TemporaryFile.class); + private final String key; + private final Path path; + + /** + * Create a temporary file with the given key. + * + * @param key the key to store the path of the temporary file + * @param prefix the prefix of the temporary file + * @param suffix the suffix of the temporary file + * @throws IOException if an I/O error occurs + */ + public TemporaryFile(String key, String prefix, String suffix) throws IOException { + this.key = key; + this.path = Files.createTempFile(prefix, suffix); + } + + @Override + public void before() { + // Do nothing + } + + @Override + public void after() { + try { + if (Files.deleteIfExists(path)) { + LOG.debug("Deleted temporary file: {}", path); + } + } catch (IOException e) { + LOG.warn("Failed to delete temporary file: {}", path, e); + } + } + + @Override + public Map properties() { + return Map.of(key, path.toString()); + } + + public Path getPath() { + return path; + } +} diff --git a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/Utils.java b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/Utils.java index 673905899..9d06ffe51 100644 --- a/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/Utils.java +++ b/tests/camel-integration-test/src/main/java/org/apache/karaf/camel/itests/Utils.java @@ -16,7 +16,24 @@ */ package org.apache.karaf.camel.itests; -final class Utils { +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.function.IntPredicate; + +import org.jetbrains.annotations.NotNull; +import org.ops4j.pax.exam.MavenUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class Utils { + + private static final Logger LOG = LoggerFactory.getLogger(Utils.class); private Utils() { } @@ -24,4 +41,82 @@ private Utils() { static String toKebabCase(String name) { return name.replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(); } + + public static int getAvailablePort(int min, int max) { + return getAvailablePort(min, max, null); + } + + public static int getAvailablePort(int min, int max, IntPredicate filter) { + for (int port = min; port <= max; port++) { + try (ServerSocket socket = new ServerSocket()) { + socket.setReuseAddress(true); + socket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), port), 1); + if (filter == null || filter.test(port)) { + return socket.getLocalPort(); + } + } catch (Exception e) { + LOG.debug("Port {} not available, trying next one", port); + } + } + throw new IllegalStateException("Can't find available network ports"); + } + + public static int getNextAvailablePort() { + return getNextAvailablePort(null); + } + + public static int getNextAvailablePort(IntPredicate filter) { + return getAvailablePort(30000, 40000, filter); + } + + /** + * Get the Camel context name from the given class. + * + * @param clazz the class from which the Camel context name should be extracted + * @return the Camel context name if it can be found, {@code null} otherwise. + */ + public static String getCamelContextName(Class clazz) { + CamelKarafTestHint hint = clazz.getAnnotation(CamelKarafTestHint.class); + if (hint == null || hint.camelContextName().isEmpty()) { + return null; + } + return hint.camelContextName(); + } + + static String loadCamelKarafVersion() { + try { + String version = MavenUtils.asInProject().getVersion("org.apache.camel.karaf", "camel-integration-test"); + LOG.info("Detected Camel Karaf version: {}", version); + return version; + } catch (Exception e) { + LOG.debug("Can't detect Camel Karaf version", e); + } + return null; + } + + static String loadUsersFileIfAbsent(String baseDir) { + Path location = Paths.get(baseDir, "camel-karaf-itest-resources", "users.properties"); + if (Files.exists(location)) { + LOG.debug("Detected users file at {}", location); + return location.toString(); + } + return loadUsersFile(location); + } + + @NotNull + private static String loadUsersFile(Path location) { + try (InputStream is = Utils.class.getResourceAsStream("/etc/users.properties")) { + if (is != null) { + Files.createDirectories(location.getParent()); + try (OutputStream os = Files.newOutputStream(location)) { + is.transferTo(os); + } + return location.toString(); + } + } catch (Exception e) { + LOG.debug("Can't load the users.properties file", e); + } + throw new IllegalStateException("Can't find the users.properties file, please provide it using the system " + + "property users.file.location"); + } } diff --git a/tests/components/camel-file/src/test/java/org/apache/karaf/camel/itests/CamelFileITest.java b/tests/components/camel-file/src/test/java/org/apache/karaf/camel/itests/CamelFileITest.java deleted file mode 100644 index a91208bb9..000000000 --- a/tests/components/camel-file/src/test/java/org/apache/karaf/camel/itests/CamelFileITest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.karaf.camel.itests; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.ops4j.pax.exam.junit.PaxExam; -import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; -import org.ops4j.pax.exam.spi.reactors.PerSuite; - - -@RunWith(PaxExam.class) -@ExamReactorStrategy(PerSuite.class) -public class CamelFileITest extends AbstractCamelKarafResultFileBasedITest { - - @Test - public void testResultFileContent() throws Exception { - assertResultFileContains("OK"); - } -} \ No newline at end of file diff --git a/tests/examples/blueprint-dsl-only/pom.xml b/tests/examples/blueprint-dsl-only/pom.xml new file mode 100644 index 000000000..85ea09186 --- /dev/null +++ b/tests/examples/blueprint-dsl-only/pom.xml @@ -0,0 +1,32 @@ + + + + 4.0.0 + + org.apache.camel.karaf + camel-karaf-examples-test + 4.6.0-SNAPSHOT + + + camel-karaf-examples-blueprint-dsl-only-test + Apache Camel :: Karaf :: Tests :: Samples :: Blueprint DSL Only + \ No newline at end of file diff --git a/tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route1.xml b/tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route1.xml new file mode 100644 index 000000000..711edafb8 --- /dev/null +++ b/tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route1.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route2.xml b/tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route2.xml new file mode 100644 index 000000000..3800cb794 --- /dev/null +++ b/tests/examples/blueprint-dsl-only/src/main/resources/OSGI-INF/blueprint/route2.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/examples/blueprint-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelBlueprintExampleWithBothTest.java b/tests/examples/blueprint-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelBlueprintExampleWithBothTest.java new file mode 100644 index 000000000..8609581bc --- /dev/null +++ b/tests/examples/blueprint-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelBlueprintExampleWithBothTest.java @@ -0,0 +1,65 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.examples.itest; + +import java.util.List; + +import org.apache.camel.Endpoint; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.karaf.camel.itests.AbstractCamelRouteWithBundleITest; +import org.apache.karaf.camel.itests.CamelKarafTestHint; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; + +import static org.junit.Assert.assertNotNull; + +@CamelKarafTestHint(isBlueprintTest = true) +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class CamelBlueprintExampleWithBothTest extends AbstractCamelRouteWithBundleITest { + + @Override + protected List getRequiredFeatures() { + return List.of(); + } + + @Override + protected String getTestBundleName() { + return "camel-karaf-examples-blueprint-dsl-only-test"; + } + + + @Test + public void testBlueprintDSL1() throws Exception { + verify("ctx1", 1); + } + + + @Test + public void testBlueprintDSL2() throws Exception { + verify("ctx2",2); + } + + private void verify(String contextName, int id) throws InterruptedException { + Endpoint endpoint = getContext(contextName).hasEndpoint("direct:example%d".formatted(id)); + assertNotNull(endpoint); + MockEndpoint mock = getContext(contextName).getEndpoint("mock:example%d".formatted(id), MockEndpoint.class); + mock.expectedBodiesReceived("Hello World"); + getTemplate(contextName).sendBody("direct:example%d".formatted(id), "Hello World"); + mock.assertIsSatisfied(); + } +} \ No newline at end of file diff --git a/tests/examples/java-dsl-only/pom.xml b/tests/examples/java-dsl-only/pom.xml new file mode 100644 index 000000000..e8addc900 --- /dev/null +++ b/tests/examples/java-dsl-only/pom.xml @@ -0,0 +1,32 @@ + + + + 4.0.0 + + org.apache.camel.karaf + camel-karaf-examples-test + 4.6.0-SNAPSHOT + + + camel-karaf-examples-java-dsl-only-test + Apache Camel :: Karaf :: Tests :: Samples :: Java DSL Only + \ No newline at end of file diff --git a/tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteFirstSupplier.java b/tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteFirstSupplier.java new file mode 100644 index 000000000..972480715 --- /dev/null +++ b/tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteFirstSupplier.java @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.examples.test; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.karaf.camel.itests.CamelRouteSupplier; +import org.osgi.service.component.annotations.Component; + +@Component( + name = "camel-karaf-examples-java-dsl-only-test-1", + immediate = true, + service = CamelRouteSupplier.class +) +public class CamelExampleRouteFirstSupplier implements CamelRouteSupplier { + @Override + public void createRoutes(RouteBuilder builder) { + builder.from("direct:example1") + .to("mock:example1"); + } +} diff --git a/tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSecondSupplier.java b/tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSecondSupplier.java new file mode 100644 index 000000000..768b81ce1 --- /dev/null +++ b/tests/examples/java-dsl-only/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSecondSupplier.java @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.examples.test; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.karaf.camel.itests.CamelRouteSupplier; +import org.osgi.service.component.annotations.Component; + +@Component( + name = "camel-karaf-examples-java-dsl-only-test-2", + immediate = true, + service = CamelRouteSupplier.class +) +public class CamelExampleRouteSecondSupplier implements CamelRouteSupplier { + @Override + public void createRoutes(RouteBuilder builder) { + builder.from("direct:example2") + .to("mock:example2"); + } +} diff --git a/tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleFirstOnlyTest.java b/tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleFirstOnlyTest.java new file mode 100644 index 000000000..69d4df296 --- /dev/null +++ b/tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleFirstOnlyTest.java @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.examples.itest; + +import java.util.List; + +import org.apache.camel.Endpoint; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.karaf.camel.itests.AbstractCamelRouteWithBundleITest; +import org.apache.karaf.camel.itests.CamelKarafTestHint; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + + +@CamelKarafTestHint(camelRouteSuppliers = "camel-karaf-examples-java-dsl-only-test-1") +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class CamelJavaExampleFirstOnlyTest extends AbstractCamelRouteWithBundleITest { + + @Override + protected List getRequiredFeatures() { + return List.of(); + } + + @Override + protected String getTestBundleName() { + return "camel-karaf-examples-java-dsl-only-test"; + } + + @Test + public void testJavaDSL() throws Exception { + Endpoint endpoint = getContext().hasEndpoint("direct:example1"); + assertNotNull(endpoint); + MockEndpoint mock = getContext().getEndpoint("mock:example1", MockEndpoint.class); + mock.expectedBodiesReceived("Hello World"); + getTemplate().sendBody("direct:example1", "Hello World"); + mock.assertIsSatisfied(); + assertNull("The second route supplier should be ignored", getContext().hasEndpoint("direct:example2")); + } +} \ No newline at end of file diff --git a/tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleWithBothTest.java b/tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleWithBothTest.java new file mode 100644 index 000000000..e7281ce92 --- /dev/null +++ b/tests/examples/java-dsl-only/src/test/java/org/apache/karaf/camel/examples/itest/CamelJavaExampleWithBothTest.java @@ -0,0 +1,63 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.examples.itest; + +import java.util.List; + +import org.apache.camel.Endpoint; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.karaf.camel.itests.AbstractCamelRouteWithBundleITest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; + +import static org.junit.Assert.assertNotNull; + + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class CamelJavaExampleWithBothTest extends AbstractCamelRouteWithBundleITest { + + @Override + protected List getRequiredFeatures() { + return List.of(); + } + + @Override + protected String getTestBundleName() { + return "camel-karaf-examples-java-dsl-only-test"; + } + + @Test + public void testJavaDSL1() throws Exception { + verify(1); + } + + + @Test + public void testJavaDSL2() throws Exception { + verify(2); + } + + private void verify(int id) throws InterruptedException { + Endpoint endpoint = getContext().hasEndpoint("direct:example%d".formatted(id)); + assertNotNull(endpoint); + MockEndpoint mock = getContext().getEndpoint("mock:example%d".formatted(id), MockEndpoint.class); + mock.expectedBodiesReceived("Hello World"); + getTemplate().sendBody("direct:example%d".formatted(id), "Hello World"); + mock.assertIsSatisfied(); + } +} \ No newline at end of file diff --git a/tests/examples/mixed/pom.xml b/tests/examples/mixed/pom.xml new file mode 100644 index 000000000..0edbcb5d8 --- /dev/null +++ b/tests/examples/mixed/pom.xml @@ -0,0 +1,32 @@ + + + + 4.0.0 + + org.apache.camel.karaf + camel-karaf-examples-test + 4.6.0-SNAPSHOT + + + camel-karaf-examples-mixed-test + Apache Camel :: Karaf :: Tests :: Samples :: Mixed + \ No newline at end of file diff --git a/tests/examples/mixed/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSupplier.java b/tests/examples/mixed/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSupplier.java new file mode 100644 index 000000000..3f36a95e4 --- /dev/null +++ b/tests/examples/mixed/src/main/java/org/apache/karaf/camel/examples/test/CamelExampleRouteSupplier.java @@ -0,0 +1,73 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.karaf.camel.examples.test; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.karaf.camel.itests.CamelRouteSupplier; +import org.osgi.service.component.annotations.Component; + +@Component( + name = "camel-karaf-examples-mixed-test", + immediate = true, + service = CamelRouteSupplier.class +) +public class CamelExampleRouteSupplier implements CamelRouteSupplier { + @Override + public void createRoutes(RouteBuilder builder) { + builder.fromF("jetty:http://0.0.0.0:%s/example", System.getProperty("java.dsl.jetty.port")) + .id("example-http-inbound") + .convertBodyTo(String.class) + .log("[EXAMPLE INBOUND] Received: ${body}") + .choice() + .when().simple("${headers.CamelHttpMethod} == 'POST'") + .setHeader("type").jsonpath("$.notification.type") + .choice() + .when().simple("${header.type} == 'email'") + .log("[EXAMPLE INBOUND] Received email notification") + .to("direct:email") + .setHeader("Exchange.HTTP_RESPONSE_CODE", builder.constant(200)) + .when().simple("${header.type} == 'http'") + .log("[EXAMPLE INBOUND] Received http notification") + .to("direct:http") + .setHeader("Exchange.HTTP_RESPONSE_CODE", builder.constant(200)) + .otherwise() + .log("[EXAMPLE INBOUND] Unknown notification") + .setBody(builder.constant("{ \"status\": \"reject\", \"type\": \"unknown\" }")) + .setHeader("Exchange.HTTP_RESPONSE_CODE", builder.constant(400)) + .otherwise() + .log("[EXAMPLE INBOUND] only POST is accepted (${headers.CamelHttpMethod})") + .setBody(builder.constant("{ \"error\": \"only POST is accepted\" }")) + .setHeader("Exchange.HTTP_RESPONSE_CODE", builder.constant(500)); + + builder.from("direct:email") + .id("example-email") + .log("[EXAMPLE EMAIL] Sending notification email") + .setHeader("to").jsonpath("$.notification.to") + .setHeader("subject", builder.constant("Notification")) + .setHeader("payload").jsonpath("$.notification.message") + //.to("smtp://localhost"); + .setBody(builder.simple("{ \"status\": \"email sent\", \"to\": \"${header.to}\", \"subject\": \"${header.subject}\" }")); + + builder.from("direct:http") + .id("example-http") + .log("[EXAMPLE HTTP] Sending http notification") + .setHeader("service").jsonpath("$.notification.service") + // send to HTTP service + .setBody(builder.simple("{ \"status\": \"http requested\", \"service\": \"${header.service}\" }")); + } +} diff --git a/tests/examples/mixed/src/main/resources/OSGI-INF/blueprint/route.xml b/tests/examples/mixed/src/main/resources/OSGI-INF/blueprint/route.xml new file mode 100644 index 000000000..60a0a1165 --- /dev/null +++ b/tests/examples/mixed/src/main/resources/OSGI-INF/blueprint/route.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + ${headers.CamelHttpMethod} == 'POST' + + $.notification.type + + + + ${header.type} == 'email' + + + 200 + + + ${header.type} == 'http' + + + 200 + + + + { "status": "reject", "type": "unknown" } + 400 + + + + + + { "error": "only POST is accepted" } + 500 + + + + + + + $.notification.to + Notification + $.notification.message + + { "status": "email sent", "to": "${header.to}", "subject": "${header.subject}" } + + + + + $.notification.service + + { "status": "http requested", "service": "${header.service}" } + + + \ No newline at end of file diff --git a/tests/examples/mixed/src/test/java/org/apache/karaf/camel/examples/itest/CamelExampleTest.java b/tests/examples/mixed/src/test/java/org/apache/karaf/camel/examples/itest/CamelExampleTest.java new file mode 100644 index 000000000..99dd98506 --- /dev/null +++ b/tests/examples/mixed/src/test/java/org/apache/karaf/camel/examples/itest/CamelExampleTest.java @@ -0,0 +1,114 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.examples.itest; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; + +import org.apache.karaf.camel.itests.AbstractCamelRouteWithBundleITest; +import org.apache.karaf.camel.itests.AvailablePortProvider; +import org.apache.karaf.camel.itests.CamelKarafTestHint; +import org.apache.karaf.camel.itests.PaxExamWithExternalResource; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; + +import static org.junit.Assert.assertEquals; + + +@CamelKarafTestHint(isBlueprintTest = true, externalResourceProvider = CamelExampleTest.ExternalResourceProviders.class) +@RunWith(PaxExamWithExternalResource.class) +@ExamReactorStrategy(PerClass.class) +public class CamelExampleTest extends AbstractCamelRouteWithBundleITest { + + public static final String BLUEPRINT_DSL_JETTY_PORT = "blueprint.dsl.jetty.port"; + public static final String JAVA_DSL_JETTY_PORT = "java.dsl.jetty.port"; + + @Override + protected List getRequiredFeatures() { + return List.of("camel-jetty", "camel-jsonpath", "camel-mail"); + } + + @Override + protected String getTestBundleName() { + return "camel-karaf-examples-mixed-test"; + } + + @Test(timeout = 60000) + public void testBlueprintDSL() throws Exception { + verify(System.getProperty(BLUEPRINT_DSL_JETTY_PORT)); + } + + @Test(timeout = 60000) + public void testJavaDSL() throws Exception { + verify(System.getProperty(JAVA_DSL_JETTY_PORT)); + } + + private static void verify(String port) throws IOException { + URL url = new URL("http://localhost:%s/example".formatted(port)); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + connection.setDoInput(true); + + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()))) { + writer.println("{ \"notification\": { \"type\": \"email\", \"to\": \"foo@bar.com\", \"message\": \"this is a test\" }}"); + writer.flush(); + } + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + StringBuilder buffer = new StringBuilder(); + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + assertEquals("{ \"status\": \"email sent\", \"to\": \"foo@bar.com\", \"subject\": \"Notification\" }", buffer.toString()); + } + + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + connection.setDoInput(true); + + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()))) { + writer.println("{ \"notification\": { \"type\": \"http\", \"service\": \"http://foo\" }}"); + writer.flush(); + } + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + StringBuilder buffer = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + assertEquals("{ \"status\": \"http requested\", \"service\": \"http://foo\" }", buffer.toString()); + } + } + + public static final class ExternalResourceProviders { + + public static AvailablePortProvider createAvailablePortProvider() { + return new AvailablePortProvider(List.of(BLUEPRINT_DSL_JETTY_PORT, JAVA_DSL_JETTY_PORT)); + } + } +} \ No newline at end of file diff --git a/tests/components/pom.xml b/tests/examples/pom.xml similarity index 92% rename from tests/components/pom.xml rename to tests/examples/pom.xml index ac03e8fd8..24071442e 100644 --- a/tests/components/pom.xml +++ b/tests/examples/pom.xml @@ -28,17 +28,14 @@ ../pom.xml - camel-karaf-components-test + camel-karaf-examples-test pom - Apache Camel :: Karaf :: Tests :: Components - - - ${project.basedir}/../../camel-integration-test/src/main/resources - + Apache Camel :: Karaf :: Tests :: Examples - camel-file - camel-seda + mixed + java-dsl-only + blueprint-dsl-only @@ -160,7 +157,7 @@ - + org.apache.servicemix.tooling depends-maven-plugin @@ -181,7 +178,7 @@ - org.apache.karaf.camel.test;version=${camel.version} + org.apache.karaf.camel.examples.test;version=${project.version} org.apache.camel*;${camel.osgi.import.camel.version}, @@ -231,8 +228,7 @@ ${project.version} ${project.build.directory} - ${integration.test.project.resources} - ERROR + WARN 5 diff --git a/tests/features/camel-amqp/pom.xml b/tests/features/camel-amqp/pom.xml new file mode 100644 index 000000000..9fcbc5b15 --- /dev/null +++ b/tests/features/camel-amqp/pom.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + + org.apache.camel.karaf + camel-karaf-features-test + 4.6.0-SNAPSHOT + + + camel-amqp-test + Apache Camel :: Karaf :: Tests :: Features :: AMQP + + + + org.testcontainers + rabbitmq + ${testcontainers-version} + test + + + \ No newline at end of file diff --git a/tests/features/camel-amqp/src/main/resources/OSGI-INF/blueprint/route.xml b/tests/features/camel-amqp/src/main/resources/OSGI-INF/blueprint/route.xml new file mode 100644 index 000000000..8f98b9a65 --- /dev/null +++ b/tests/features/camel-amqp/src/main/resources/OSGI-INF/blueprint/route.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + This is header + + + Hello Camel + + + + + + + + + + + ${header.theHeader} != "This is header" + + + + + + + + + + ${body} != "Hello Camel" + + + + + + + + + OK + + + + + \ No newline at end of file diff --git a/tests/features/camel-amqp/src/test/java/org/apache/karaf/camel/itest/CamelAmqpITest.java b/tests/features/camel-amqp/src/test/java/org/apache/karaf/camel/itest/CamelAmqpITest.java new file mode 100644 index 000000000..bf190cb44 --- /dev/null +++ b/tests/features/camel-amqp/src/test/java/org/apache/karaf/camel/itest/CamelAmqpITest.java @@ -0,0 +1,67 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.itest; + +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultMockBasedRouteITest; +import org.apache.karaf.camel.itests.CamelKarafTestHint; +import org.apache.karaf.camel.itests.GenericContainerResource; +import org.apache.karaf.camel.itests.PaxExamWithExternalResource; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; +import org.testcontainers.containers.RabbitMQContainer; + + +@CamelKarafTestHint( + externalResourceProvider = CamelAmqpITest.ExternalResourceProviders.class, + isBlueprintTest = true) +@RunWith(PaxExamWithExternalResource.class) +@ExamReactorStrategy(PerClass.class) +public class CamelAmqpITest extends AbstractCamelSingleFeatureResultMockBasedRouteITest { + + @Override + public void configureMock(MockEndpoint mock) { + mock.expectedBodiesReceived("OK"); + } + + @Test + public void testResultMock() throws Exception { + assertMockEndpointsSatisfied(); + } + + public static final class ExternalResourceProviders { + private static final String PASSWORD = "s3cret"; + + public static GenericContainerResource createRabbitMQContainer() { + final RabbitMQContainer rabbitMQContainer = + new RabbitMQContainer("rabbitmq:3.13.1") + .withAdminPassword(PASSWORD); + return new GenericContainerResource<>(rabbitMQContainer, + resource -> { + try { + rabbitMQContainer.execInContainer("rabbitmq-plugins", "enable", "rabbitmq_amqp1_0"); + } catch (Exception e) { + throw new RuntimeException(e); + } + resource.setProperty("amqp.host", rabbitMQContainer.getHost()); + resource.setProperty("amqp.port", Integer.toString(rabbitMQContainer.getAmqpPort())); + resource.setProperty("amqp.username", rabbitMQContainer.getAdminUsername()); + resource.setProperty("amqp.password", rabbitMQContainer.getAdminPassword()); + } + ); + } + } +} \ No newline at end of file diff --git a/tests/components/camel-file/pom.xml b/tests/features/camel-core/pom.xml similarity index 87% rename from tests/components/camel-file/pom.xml rename to tests/features/camel-core/pom.xml index 00ed971be..00959e4da 100644 --- a/tests/components/camel-file/pom.xml +++ b/tests/features/camel-core/pom.xml @@ -23,14 +23,12 @@ 4.0.0 org.apache.camel.karaf - camel-karaf-components-test + camel-karaf-features-test 4.6.0-SNAPSHOT ../pom.xml - camel-file-test - Apache Camel :: Karaf :: Tests :: Components :: File - - + camel-core-test + Apache Camel :: Karaf :: Tests :: Features :: Core \ No newline at end of file diff --git a/tests/components/camel-file/src/main/java/org/apache/karaf/camel/test/CamelFileComponent.java b/tests/features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelFileRouteSupplier.java similarity index 84% rename from tests/components/camel-file/src/main/java/org/apache/karaf/camel/test/CamelFileComponent.java rename to tests/features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelFileRouteSupplier.java index 2a7b0f5b9..93b0d832b 100644 --- a/tests/components/camel-file/src/main/java/org/apache/karaf/camel/test/CamelFileComponent.java +++ b/tests/features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelFileRouteSupplier.java @@ -19,13 +19,15 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.RouteDefinition; -import org.apache.karaf.camel.itests.AbstractCamelComponentResultFileBased; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultFileBasedRouteSupplier; +import org.apache.karaf.camel.itests.CamelRouteSupplier; import org.osgi.service.component.annotations.Component; @Component( name = "karaf-camel-file-test", - immediate = true + immediate = true, + service = CamelRouteSupplier.class ) -public class CamelFileComponent extends AbstractCamelComponentResultFileBased { +public class CamelFileRouteSupplier extends AbstractCamelSingleFeatureResultFileBasedRouteSupplier { @Override protected Function consumerRoute() { @@ -39,6 +41,4 @@ protected void configureProducer(RouteBuilder builder, RouteDefinition producerR .setBody(builder.constant("OK")) // Set the body of the message to "OK" .toF("file:%s?fileName=testResult.txt", getBaseDir()); } - - } \ No newline at end of file diff --git a/tests/components/camel-seda/src/main/java/org/apache/karaf/camel/test/CamelSedaComponent.java b/tests/features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelSedaRouteSupplier.java similarity index 55% rename from tests/components/camel-seda/src/main/java/org/apache/karaf/camel/test/CamelSedaComponent.java rename to tests/features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelSedaRouteSupplier.java index b5626f12c..527a21c48 100644 --- a/tests/components/camel-seda/src/main/java/org/apache/karaf/camel/test/CamelSedaComponent.java +++ b/tests/features/camel-core/src/main/java/org/apache/karaf/camel/test/CamelSedaRouteSupplier.java @@ -1,32 +1,36 @@ -/* +/** * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with + * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.apache.karaf.camel.test; import java.util.function.Function; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.RouteDefinition; -import org.apache.karaf.camel.itests.AbstractCamelComponentResultMockBased; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultMockBasedRouteSupplier; +import org.apache.karaf.camel.itests.CamelRouteSupplier; import org.osgi.service.component.annotations.Component; @Component( name = "karaf-camel-seda-test", - immediate = true + immediate = true, + service = CamelRouteSupplier.class ) -public class CamelSedaComponent extends AbstractCamelComponentResultMockBased { +public class CamelSedaRouteSupplier extends AbstractCamelSingleFeatureResultMockBasedRouteSupplier { @Override protected Function consumerRoute() { @@ -40,4 +44,4 @@ protected void configureProducer(RouteBuilder builder, RouteDefinition producerR // send it to the seda queue that is async .to("seda:next"); } -} \ No newline at end of file +} diff --git a/tests/features/camel-core/src/test/java/org/apache/karaf/camel/itest/CamelCoreITest.java b/tests/features/camel-core/src/test/java/org/apache/karaf/camel/itest/CamelCoreITest.java new file mode 100644 index 000000000..5a2648ab5 --- /dev/null +++ b/tests/features/camel-core/src/test/java/org/apache/karaf/camel/itest/CamelCoreITest.java @@ -0,0 +1,77 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.itest; + +import java.util.List; + +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.karaf.camel.itests.AbstractCamelRouteWithBundleITest; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultFileBasedRoute; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultMockBasedRoute; +import org.apache.karaf.camel.itests.CamelContextProvider; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; + + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class CamelCoreITest extends AbstractCamelRouteWithBundleITest { + + @Override + protected String getTestBundleName() { + return "camel-core-test"; + } + + @Override + protected List getRequiredFeatures() { + return List.of(); + } + + @Test + public void testCamelFile() throws Exception { + new CamelFileITest(this, getBaseDir()).testRoutes(); + } + + @Test + public void testCamelSeda() throws Exception { + new CamelSedaITest(this).testRoutes(); + } + + public static class CamelFileITest extends AbstractCamelSingleFeatureResultFileBasedRoute { + + public CamelFileITest(CamelContextProvider provider, String baseDir) { + super(provider, baseDir); + } + + @Override + protected void executeTest() throws Exception { + assertResultFileContains("OK"); + } + } + + public static class CamelSedaITest extends AbstractCamelSingleFeatureResultMockBasedRoute { + + public CamelSedaITest(CamelContextProvider provider) { + super(provider); + } + + @Override + public void configureMock(MockEndpoint mock) { + mock.expectedBodiesReceived("OK"); + } + } +} \ No newline at end of file diff --git a/tests/features/camel-elasticsearch/pom.xml b/tests/features/camel-elasticsearch/pom.xml new file mode 100644 index 000000000..c356faf4f --- /dev/null +++ b/tests/features/camel-elasticsearch/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + org.apache.camel.karaf + camel-karaf-features-test + 4.6.0-SNAPSHOT + ../pom.xml + + + camel-elasticsearch-test + Apache Camel :: Karaf :: Tests :: Features :: ElasticSearch Java API Client + + + + org.apache.camel + camel-elasticsearch + ${camel.version} + + + org.testcontainers + elasticsearch + ${testcontainers-version} + test + + + + + \ No newline at end of file diff --git a/tests/features/camel-elasticsearch/src/main/java/org/apache/karaf/camel/test/CamelElasticsearchRouteSupplier.java b/tests/features/camel-elasticsearch/src/main/java/org/apache/karaf/camel/test/CamelElasticsearchRouteSupplier.java new file mode 100644 index 000000000..cb340416d --- /dev/null +++ b/tests/features/camel-elasticsearch/src/main/java/org/apache/karaf/camel/test/CamelElasticsearchRouteSupplier.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.test; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.es.ElasticsearchComponent; +import org.apache.camel.model.RouteDefinition; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultMockBasedRouteSupplier; +import org.apache.karaf.camel.itests.CamelRouteSupplier; +import org.osgi.service.component.annotations.Component; + +@Component(name = "karaf-camel-elasticsearch-test", immediate = true, service = CamelRouteSupplier.class) +public class CamelElasticsearchRouteSupplier extends AbstractCamelSingleFeatureResultMockBasedRouteSupplier { + + private static final String INDEX_NAME = "testindex"; + + @Override + public void configure(CamelContext camelContext) { + final ElasticsearchComponent elasticsearchComponent = new ElasticsearchComponent(); + elasticsearchComponent.setEnableSSL(true); + elasticsearchComponent.setHostAddresses( + "%s:%s".formatted(System.getProperty("elasticsearch.host"), System.getProperty("elasticsearch.port")) + ); + elasticsearchComponent.setUser(System.getProperty("elasticsearch.username")); + elasticsearchComponent.setPassword(System.getProperty("elasticsearch.password")); + elasticsearchComponent.setCertificatePath("file:%s".formatted(System.getProperty("elasticsearch.cafile"))); + + camelContext.addComponent("elasticsearch",elasticsearchComponent); + } + + @Override + protected void configureProducer(RouteBuilder builder, RouteDefinition producerRoute) { + //to add the mock endpoint at the end of the route, call configureConsumer + configureConsumer( + producerRoute.toF("elasticsearch://elasticsearch?operation=Exists&indexName=%s", INDEX_NAME) + .log("Index exist: ${body}") + .setBody(builder.simple(""" + {"date": "${header.CamelTimerFiredTime}", "someKey": "someValue"} + """)) + .toF("elasticsearch://elasticsearch?operation=Index&indexName=%s", INDEX_NAME) + .log("Index doc : ${body}") + .setHeader("_ID", builder.simple("${body}")) + .toF("elasticsearch://elasticsearch?operation=GetById&indexName=%s", INDEX_NAME) + .log("Get doc: ${body}") + .setHeader("indexId", builder.simple("${header._ID}")) + .setBody(builder.constant(""" + {"doc": {"someKey": "someValue2"}} + """)) + .toF("elasticsearch://elasticsearch?operation=Update&indexName=%s", INDEX_NAME) + .log("Update doc: ${body} ") + .setBody(builder.simple("${header._ID}")) + .toF("elasticsearch://elasticsearch?operation=GetById&indexName=%s", INDEX_NAME) + .log("Get doc: ${body}") + .setBody(builder.simple("${header._ID}")) + .toF("elasticsearch://elasticsearch?operation=Delete&indexName=%s", INDEX_NAME) + .log("Delete doc: ${body}") + .setBody(builder.constant("OK")) + ); + + } + + @Override + protected boolean consumerEnabled() { + return false; + } +} \ No newline at end of file diff --git a/tests/features/camel-elasticsearch/src/test/java/org/apache/karaf/camel/itest/CamelElasticsearchITest.java b/tests/features/camel-elasticsearch/src/test/java/org/apache/karaf/camel/itest/CamelElasticsearchITest.java new file mode 100644 index 000000000..6a45f89c1 --- /dev/null +++ b/tests/features/camel-elasticsearch/src/test/java/org/apache/karaf/camel/itest/CamelElasticsearchITest.java @@ -0,0 +1,82 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.itest; + +import java.io.IOException; +import java.nio.file.Files; +import java.time.Duration; + +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultMockBasedRouteITest; +import org.apache.karaf.camel.itests.CamelKarafTestHint; +import org.apache.karaf.camel.itests.GenericContainerResource; +import org.apache.karaf.camel.itests.PaxExamWithExternalResource; +import org.apache.karaf.camel.itests.TemporaryFile; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.elasticsearch.ElasticsearchContainer; + +@CamelKarafTestHint(externalResourceProvider = CamelElasticsearchITest.ExternalResourceProviders.class) +@RunWith(PaxExamWithExternalResource.class) +@ExamReactorStrategy(PerClass.class) +public class CamelElasticsearchITest extends AbstractCamelSingleFeatureResultMockBasedRouteITest { + + @Override + public void configureMock(MockEndpoint mock) { + mock.expectedBodiesReceived("OK"); + } + + @Test + public void testResultMock() throws Exception { + assertMockEndpointsSatisfied(); + } + + public static final class ExternalResourceProviders { + + private static final String USER_NAME = "elastic"; + private static final String PASSWORD = "s3cret"; + private static final int ELASTIC_SEARCH_PORT = 9200; + + public static GenericContainerResource createElasticsearchContainer() { + final ElasticsearchContainer elasticsearchContainer = + new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.11.1").withPassword(PASSWORD); + // Increase the timeout from 60 seconds to 90 seconds to ensure that it will be long enough + // on the build pipeline + elasticsearchContainer.setWaitStrategy( + new LogMessageWaitStrategy() + .withRegEx(".*(\"message\":\\s?\"started[\\s?|\"].*|] started\n$)") + .withStartupTimeout(Duration.ofSeconds(90))); + return new GenericContainerResource<>(elasticsearchContainer, + resource -> { + resource.getContainer().caCertAsBytes().ifPresent(content -> { + try { + TemporaryFile tempFile = new TemporaryFile("elasticsearch.cafile", "http_ca", ".crt"); + Files.write(tempFile.getPath(), content); + resource.addDependency(tempFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + resource.setProperty("elasticsearch.host", elasticsearchContainer.getHost()); + resource.setProperty("elasticsearch.port", Integer.toString(elasticsearchContainer.getMappedPort(ELASTIC_SEARCH_PORT))); + resource.setProperty("elasticsearch.username", USER_NAME); + resource.setProperty("elasticsearch.password", PASSWORD); + } + ); + } + } +} \ No newline at end of file diff --git a/tests/components/camel-seda/pom.xml b/tests/features/camel-jetty/pom.xml similarity index 87% rename from tests/components/camel-seda/pom.xml rename to tests/features/camel-jetty/pom.xml index 152053086..63a399b89 100644 --- a/tests/components/camel-seda/pom.xml +++ b/tests/features/camel-jetty/pom.xml @@ -23,12 +23,12 @@ 4.0.0 org.apache.camel.karaf - camel-karaf-components-test + camel-karaf-features-test 4.6.0-SNAPSHOT - camel-seda-test - Apache Camel :: Karaf :: Tests :: Components :: Seda + camel-jetty-test + Apache Camel :: Karaf :: Tests :: Features :: Jetty \ No newline at end of file diff --git a/tests/features/camel-jetty/src/main/java/org/apache/karaf/camel/test/CamelJettyRouteSupplier.java b/tests/features/camel-jetty/src/main/java/org/apache/karaf/camel/test/CamelJettyRouteSupplier.java new file mode 100644 index 000000000..7e9442bfc --- /dev/null +++ b/tests/features/camel-jetty/src/main/java/org/apache/karaf/camel/test/CamelJettyRouteSupplier.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.karaf.camel.test; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.function.Function; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.RouteDefinition; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultMockBasedRouteSupplier; +import org.apache.karaf.camel.itests.CamelRouteSupplier; +import org.apache.karaf.camel.itests.Utils; +import org.osgi.service.component.annotations.Component; + +@Component( + name = "karaf-camel-jetty-test", + immediate = true, + service = CamelRouteSupplier.class +) +public class CamelJettyRouteSupplier extends AbstractCamelSingleFeatureResultMockBasedRouteSupplier { + + private final int port = Utils.getNextAvailablePort(); + + @Override + protected Function consumerRoute() { + return builder -> builder.from("jetty://http://localhost:%s/jettyTest".formatted(port)).transform(builder.constant("OK")); + } + + @Override + protected void configureProducer(RouteBuilder builder, RouteDefinition producerRoute) { + producerRoute.log("calling http endpoint") + .process(new HttpClientProcessor()); + } + + class HttpClientProcessor implements Processor { + + @Override + public void process(Exchange exchange) throws Exception { + + HttpClient client = HttpClient.newHttpClient(); + + // Create a URI for the request + URI uri = URI.create("http://localhost:%s/jettyTest".formatted(port)); + + // Create a HttpRequest + HttpRequest request = HttpRequest.newBuilder() + .uri(uri) + .build(); + + client.send(request, HttpResponse.BodyHandlers.ofString()); + } + } +} + diff --git a/tests/components/camel-seda/src/test/java/org/apache/karaf/camel/itests/CamelSedaITest.java b/tests/features/camel-jetty/src/test/java/org/apache/karaf/camel/itest/CamelJettyITest.java similarity index 73% rename from tests/components/camel-seda/src/test/java/org/apache/karaf/camel/itests/CamelSedaITest.java rename to tests/features/camel-jetty/src/test/java/org/apache/karaf/camel/itest/CamelJettyITest.java index de76109a6..d5e743dd1 100644 --- a/tests/components/camel-seda/src/test/java/org/apache/karaf/camel/itests/CamelSedaITest.java +++ b/tests/features/camel-jetty/src/test/java/org/apache/karaf/camel/itest/CamelJettyITest.java @@ -11,22 +11,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.karaf.camel.itests; +package org.apache.karaf.camel.itest; import org.apache.camel.component.mock.MockEndpoint; +import org.apache.karaf.camel.itests.AbstractCamelSingleFeatureResultMockBasedRouteITest; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.junit.PaxExam; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; -import org.ops4j.pax.exam.spi.reactors.PerSuite; +import org.ops4j.pax.exam.spi.reactors.PerClass; @RunWith(PaxExam.class) -@ExamReactorStrategy(PerSuite.class) -public class CamelSedaITest extends AbstractCamelKarafResultMockBasedITest { +@ExamReactorStrategy(PerClass.class) +public class CamelJettyITest extends AbstractCamelSingleFeatureResultMockBasedRouteITest { @Override - protected void configureMock(MockEndpoint mock) { + public void configureMock(MockEndpoint mock) { mock.expectedBodiesReceived("OK"); } diff --git a/tests/features/pom.xml b/tests/features/pom.xml new file mode 100644 index 000000000..451b6745c --- /dev/null +++ b/tests/features/pom.xml @@ -0,0 +1,248 @@ + + + + + 4.0.0 + + + org.apache.camel.karaf + camel-karaf-tests + 4.6.0-SNAPSHOT + ../pom.xml + + + camel-karaf-features-test + pom + Apache Camel :: Karaf :: Tests :: Features + + + ${project.basedir}/../../camel-integration-test/src/main/resources/etc/users.properties + + + + camel-amqp + camel-core + camel-elasticsearch + camel-jetty + + + + + + org.apache.karaf + karaf-bom + ${karaf.version} + pom + import + + + org.apache.karaf.itests + common + ${karaf.version} + test + + + + + + + org.apache.camel.karaf + camel-integration-test + ${project.version} + compile + + + org.apache.camel.karaf + camel-core-osgi + ${project.version} + provided + + + org.apache.camel + camel-core-engine + ${camel.version} + provided + + + org.apache.camel + camel-mock + ${camel.version} + provided + + + + org.apache.camel.karaf + camel-api + ${project.version} + provided + + + org.osgi + osgi.core + provided + + + org.osgi + org.osgi.service.component + provided + + + org.osgi + org.osgi.service.component.annotations + provided + + + + org.apache.karaf.itests + common + test + + + + org.apache.karaf + apache-karaf + test + tar.gz + + + org.apache.karaf + org.apache.karaf.client + + + + + + org.apache.karaf.shell + org.apache.karaf.shell.core + test + + + + org.ops4j.pax.exam + pax-exam-container-karaf + test + + + + org.ops4j.pax.exam + pax-exam-junit4 + + + org.apache.geronimo.specs + geronimo-atinject_1.0_spec + test + + + org.awaitility + awaitility + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.hamcrest + runtime + + + + + + + + org.apache.servicemix.tooling + depends-maven-plugin + + + generate-depends-file + + generate-depends-file + + + + + + org.apache.felix + maven-bundle-plugin + true + true + + + + org.apache.karaf.camel.test;version=${project.version} + + + org.apache.camel*;${camel.osgi.import.camel.version}, + * + + + + + + cleanVersions + generate-sources + + cleanVersions + + + + ${project.version} + + + + + bundle + package + + bundle + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + + test + + integration-test + + + none + + + **/*Test.java + + + ${project.version} + ${project.version} + ${project.build.directory} + ${users.file.location} + WARN + + 5 + + + + + + + + \ No newline at end of file diff --git a/tests/pom.xml b/tests/pom.xml index 3cc280261..b9097d1c1 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -35,8 +35,8 @@ camel-test-scr camel-integration-test - components - + features + examples \ No newline at end of file