Skip to content

Commit

Permalink
Using TestContainers for Arquillian Remote
Browse files Browse the repository at this point in the history
  • Loading branch information
kifj committed Mar 17, 2024
1 parent 65c9814 commit 598e055
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 53 deletions.
22 changes: 2 additions & 20 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,8 @@ node {
}

stage('Run IT test') {
docker
.image('registry.x1/j7beck/x1-wildfly-stomp-test-it:1.8')
.withRun('-e MANAGEMENT=public -e HTTP=public --name stomp-test-it') {
c ->
waitFor("http://${hostIp(c)}:9990/health/ready", 20, 3)
withMaven(maven: 'Maven-3.9', mavenSettingsConfig: mavenSetting) {
sh "mvn -Parq-remote verify -Djboss.managementAddress=${hostIp(c)}"
}
withMaven(maven: 'Maven-3.9', mavenSettingsConfig: mavenSetting) {
sh "mvn -Parq-remote verify"
}
}

Expand Down Expand Up @@ -65,15 +59,3 @@ node {
}
}
}

def hostIp(container) {
sh "docker inspect -f {{.NetworkSettings.IPAddress}} ${container.id} > hostIp"
readFile('hostIp').trim()
}

def waitFor(target, sleepInSec, retries) {
retry (retries) {
sleep sleepInSec
httpRequest url: target, validResponseCodes: '200'
}
}
17 changes: 17 additions & 0 deletions src/main/docker/integration-tests/scripts/create-stomp-test.cli
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
embed-server --admin-only --server-config=profile.xml --std-out=echo

if (outcome != success) of /subsystem=datasources/data-source=stocksDS:read-resource
/subsystem=datasources/data-source=stocksDS:add( \
jndi-name=java:jboss/datasources/stocksDS, \
connection-url=jdbc:postgresql://${env.DB_SERVER:postgresql}:${env.DB_PORT:5432}/stocks,\
user-name=${env.DB_USER:stocks}, \
password=${env.DB_PASSWORD:stocks}, \
statistics-enabled=true,\
min-pool-size=2,\
max-pool-size=5,\
driver-name=postgresql,\
driver-class=org.postgresql.Driver,\
validate-on-match=true,\
check-valid-connection-sql="select current_timestamp",\
statistics-enabled=true\
)
end-if

/subsystem=messaging-activemq/server=default/jms-queue=stocksQueue:add(durable=true, entries=[java:/jms/queue/stocks])
/subsystem=messaging-activemq/server=default/jms-topic=quotesTopic:add(entries=[java:/jms/topic/quotes])
/subsystem=logging/logger=x1.service.registry:write-attribute(name=level,value=DEBUG)
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/x1/arquillian/ArquillianTestContainers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package x1.arquillian;

import java.util.List;

import org.jboss.arquillian.container.spi.ContainerRegistry;
import org.testcontainers.containers.GenericContainer;

/**
* implementation must have a no-arg constructor and must be annotated
* with @ContainerDefinition
*/
public interface ArquillianTestContainers {
List<GenericContainer<?>> instances();

default void configureAfterStart(ContainerRegistry registry) {
};

default boolean followLog(GenericContainer<?> container) {
return true;
}

default boolean simpleLog(GenericContainer<?> container) {
return false;
}
}
14 changes: 14 additions & 0 deletions src/test/java/x1/arquillian/ContainerDefinition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package x1.arquillian;

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;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface ContainerDefinition {

}
69 changes: 69 additions & 0 deletions src/test/java/x1/arquillian/Containers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package x1.arquillian;

import java.util.Arrays;
import java.util.List;

import org.jboss.arquillian.config.descriptor.api.ContainerDef;
import org.jboss.arquillian.container.spi.Container;
import org.jboss.arquillian.container.spi.ContainerRegistry;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import jakarta.ws.rs.core.Response.Status;

@ContainerDefinition
public final class Containers implements ArquillianTestContainers {
private Network network = Network.newNetwork();

private GenericContainer<?> database = new GenericContainer<>(
DockerImageName.parse("registry.x1/j7beck/x1-postgres-stomp-test:1.8")).withNetwork(network)
.withNetworkAliases("db");

private GenericContainer<?> etcd = new GenericContainer<>(DockerImageName.parse("quay.io/coreos/etcd:v3.5.11"))
.withEnv("ETCD_ENABLE_V2", "true").withNetwork(network).withNetworkAliases("etcd").withCommand("etcd",
"--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://etcd:2379");

private GenericContainer<?> wildfly = new GenericContainer<>(
DockerImageName.parse("registry.x1/j7beck/x1-wildfly-stomp-test-it:1.8")).dependsOn(database).dependsOn(etcd)
.withNetwork(network).withEnv("DB_SERVER", "db").withEnv("DB_PORT", "5432").withEnv("DB_USER", "stocks")
.withEnv("DB_PASSWORD", "stocks").withEnv("ETCD_SERVER", "etcd").withEnv("ETCD_PORT", "2379")
.withEnv("X1_SERVICE_REGISTRY_STAGE", "docker").withExposedPorts(8080, 9990)
.waitingFor(Wait.forHttp("/health/ready").forStatusCode(Status.OK.getStatusCode()));

private final List<GenericContainer<?>> instances = Arrays.asList(etcd, database, wildfly);

@Override
public List<GenericContainer<?>> instances() {
return instances;
}

@Override
public void configureAfterStart(ContainerRegistry registry) {
Container arquillianContainer = registry.getContainers().iterator().next();
ContainerDef containerConfiguration = arquillianContainer.getContainerConfiguration();
containerConfiguration.property("managementPort", Integer.toString(wildfly.getMappedPort(9990)));

// if we would run the test as client, we would need to access the servlet from the host
// ProtocolDef protocolConfiguration = arquillianContainer.getProtocolConfiguration(new ProtocolDescription(ServletProtocolDefinition.NAME));
// protocolConfiguration.property("port", Integer.toString(wildfly.getMappedPort(8080)));
}

@Override
public boolean followLog(GenericContainer<?> container) {
if (container == etcd) {
return false;
}
return true;
}

@Override
public boolean simpleLog(GenericContainer<?> container) {
if (container == wildfly) {
return true;
}
return false;
}

}
20 changes: 20 additions & 0 deletions src/test/java/x1/arquillian/SimpleLogConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package x1.arquillian;

import org.testcontainers.containers.output.BaseConsumer;
import org.testcontainers.containers.output.OutputFrame;

public class SimpleLogConsumer extends BaseConsumer<SimpleLogConsumer> {

@Override
public void accept(OutputFrame outputFrame) {
var outputType = outputFrame.getType();
var utf8String = outputFrame.getUtf8StringWithoutLineEnding();

switch (outputType) {
case END -> {}
case STDOUT -> System.out.println(utf8String);
case STDERR -> System.err.println(utf8String);
default -> throw new IllegalArgumentException("Unexpected outputType " + outputType);
}
}
}
78 changes: 78 additions & 0 deletions src/test/java/x1/arquillian/TestContainersExtension.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package x1.arquillian;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.jboss.arquillian.container.spi.ContainerRegistry;
import org.jboss.arquillian.container.spi.event.container.AfterStop;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.core.spi.LoadableExtension;
import org.jboss.arquillian.core.spi.ServiceLoader;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.output.Slf4jLogConsumer;

public class TestContainersExtension implements LoadableExtension {
private static final Logger LOGGER = LoggerFactory.getLogger(TestContainersExtension.class);
private static final String PACKAGE_NAME = "x1.arquillian";

public static boolean isRemoteArquillian() {
return System.getProperty("arquillian.launch").equals("remote");
}

@Override
public void register(ExtensionBuilder builder) {
if (isRemoteArquillian()) {
findArquillianTestContainers().ifPresent(containerDefinition -> {
LoadContainerConfiguration.containerDefinition = containerDefinition;
builder.observer(LoadContainerConfiguration.class);
});
}
}

public static final class LoadContainerConfiguration {
private static ArquillianTestContainers containerDefinition;

public void registerInstance(@Observes ContainerRegistry registry, ServiceLoader serviceLoader) {
containerDefinition.instances().forEach(container -> {
container.start();
if (containerDefinition.followLog(container)) {
var logConsumer = containerDefinition.simpleLog(container) ? new SimpleLogConsumer()
: new Slf4jLogConsumer(LOGGER).withSeparateOutputStreams();
container.followOutput(logConsumer);
}
});
LOGGER.info("Started {}", getImageNames());
containerDefinition.configureAfterStart(registry);
}

public void stopInstance(@Observes AfterStop event) {
containerDefinition.instances().forEach(container -> container.stop());
LOGGER.info("Stopped {}", getImageNames());
}

private List<String> getImageNames() {
return containerDefinition.instances().stream().map(instance -> instance.getDockerImageName())
.collect(Collectors.toList());
}
}

private Optional<ArquillianTestContainers> findArquillianTestContainers() {
var classes = new Reflections(PACKAGE_NAME).getTypesAnnotatedWith(ContainerDefinition.class);
if (classes.isEmpty()) {
return Optional.empty();
} else if (classes.size() > 1) {
throw new IllegalArgumentException(
"Found more than one ContainerDefinition under " + PACKAGE_NAME + ": " + classes);
}
try {
LOGGER.debug("Found ContainerDefinition in {}", classes);
return Optional.of((ArquillianTestContainers) classes.iterator().next().getDeclaredConstructor().newInstance());
} catch (Exception e) {
LOGGER.warn("Could not create ContainerDefinition", e);
return Optional.empty();
}
}
}
2 changes: 1 addition & 1 deletion src/test/java/x1/service/test/ResolverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public static Archive<?> createTestArchive() {
.withTransitivity().asFile();

return ShrinkWrap.create(WebArchive.class, VersionData.APP_NAME_MAJOR_MINOR + ".war").addPackages(true, "x1.stomp")
.addAsResource("test-persistence.xml", "META-INF/persistence.xml")
.addAsResource("managed-persistence.xml", "META-INF/persistence.xml")
.addAsResource("microprofile-config.properties", "META-INF/microprofile-config.properties")
.addAsResource("service-registry.properties").addAsWebInfResource("beans.xml")
.addAsWebInfResource("test-ds.xml").addAsWebInfResource("jboss-deployment-structure.xml")
Expand Down
72 changes: 41 additions & 31 deletions src/test/java/x1/stomp/test/AbstractIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,56 @@
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.extension.ExtendWith;

import x1.arquillian.TestContainersExtension;
import x1.stomp.boundary.JacksonConfig;
import x1.stomp.version.VersionData;

@ExtendWith(ArquillianExtension.class)
@Tag("Arquillian")
public abstract class AbstractIT {

protected Client client;

@ArquillianResource
protected URL url;

@Deployment
public static Archive<?> createTestArchive() {
var libraries = Maven.resolver().loadPomFromFile("pom.xml")
.resolve("org.assertj:assertj-core", "org.hamcrest:hamcrest-core").withTransitivity().asFile();

return ShrinkWrap.create(WebArchive.class, VersionData.APP_NAME_MAJOR_MINOR + ".war").addPackages(true, "x1.stomp")
.addAsResource("test-persistence.xml", "META-INF/persistence.xml")

protected Client client;

@ArquillianResource
protected URL url;

@Deployment
public static Archive<?> createTestArchive() {
var libraries = Maven.resolver().loadPomFromFile("pom.xml")
.resolve("org.assertj:assertj-core", "org.hamcrest:hamcrest-core").withTransitivity().asFile();

if (TestContainersExtension.isRemoteArquillian()) {
return ShrinkWrap.create(WebArchive.class, VersionData.APP_NAME_MAJOR_MINOR + ".war")
.addPackages(true, "x1.stomp").addAsResource("remote-persistence.xml", "META-INF/persistence.xml")
.addAsResource("microprofile-config.properties", "META-INF/microprofile-config.properties")
.addAsResource("quickquoteresult.xml").addAsWebInfResource("beans.xml").addAsWebInfResource("test-ds.xml")
.addAsResource("quickquoteresult.xml").addAsWebInfResource("beans.xml")
.addAsWebInfResource("jboss-deployment-structure.xml").addAsLibraries(libraries);
} else {
return ShrinkWrap.create(WebArchive.class, VersionData.APP_NAME_MAJOR_MINOR + ".war")
.addPackages(true, "x1.stomp").addAsResource("managed-persistence.xml", "META-INF/persistence.xml")
.addAsWebInfResource("test-ds.xml")
.addAsResource("microprofile-config.properties", "META-INF/microprofile-config.properties")
.addAsResource("quickquoteresult.xml").addAsWebInfResource("beans.xml")
.addAsWebInfResource("jboss-deployment-structure.xml").addAsLibraries(libraries);
}

@BeforeEach
public void setup() {
client = ClientBuilder.newClient().register(JacksonConfig.class);
}
}

@AfterEach
public void tearDown() {
client.close();
}

public Integer getPortOffset() {
return Integer.valueOf(System.getProperty("jboss.socket.binding.port-offset", "0"));
}
@BeforeEach
public void setup() {
client = ClientBuilder.newClient().register(JacksonConfig.class);
}

@AfterEach
public void tearDown() {
client.close();
}

public Integer getPortOffset() {
return Integer.valueOf(System.getProperty("jboss.socket.binding.port-offset", "0"));
}

public String getHost() {
return System.getProperty("jboss.bind.address", "127.0.0.1");
}

public String getHost() {
return System.getProperty("jboss.bind.address", "127.0.0.1");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x1.arquillian.TestContainersExtension
File renamed without changes.
15 changes: 15 additions & 0 deletions src/test/resources/remote-persistence.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
<persistence-unit name="stomp-test">
<jta-data-source>java:jboss/datasources/stocksDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.default_schema" value="stocks" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
</properties>
</persistence-unit>
</persistence>
3 changes: 2 additions & 1 deletion src/test/resources/simplelogger.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.slf4j.simpleLogger.defaultLogLevel=info
org.slf4j.simpleLogger.log.x1=debug
org.slf4j.simpleLogger.log.org.hibernate=warn
org.slf4j.simpleLogger.log.org.hibernate=warn
org.slf4j.simpleLogger.log.x1.arquillian=info

0 comments on commit 598e055

Please sign in to comment.