diff --git a/README.md b/README.md
index 75a0b5006..792d9e22f 100644
--- a/README.md
+++ b/README.md
@@ -1114,6 +1114,16 @@ It covers different usages:
3. from a blocking endpoint
4. from a reactive endpoint
+### `cache/infinispan`
+Verifies the `quarkus-infinispan-cache` extension using `@CacheResult`, `@CacheInvalidate`, `@CacheInvalidateAll` and `@CacheKey`.
+It covers different usages:
+1. from an application scoped service
+2. from a request scoped service
+3. from a blocking endpoint
+4. from a reactive endpoint
+
+Also test POJOs as cache value and cache expiration.
+
### `cache/redis`
Verifies the `quarkus-redis-cache` extension using `@CacheResult`, `@CacheInvalidate`, `@CacheInvalidateAll` and `@CacheKey`.
diff --git a/cache/infinispan/pom.xml b/cache/infinispan/pom.xml
new file mode 100644
index 000000000..e94f3830f
--- /dev/null
+++ b/cache/infinispan/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+
+ io.quarkus.ts.qe
+ parent
+ 1.0.0-SNAPSHOT
+ ../..
+
+ cache-infinispan
+ jar
+ Quarkus QE TS: Cache: Infinispan
+
+
+ io.quarkus
+ quarkus-infinispan-cache
+
+
+ io.quarkus
+ quarkus-rest
+
+
+ io.quarkus.qe
+ quarkus-test-service-infinispan
+ test
+
+
+
diff --git a/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ApplicationScopeService.java b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ApplicationScopeService.java
new file mode 100644
index 000000000..88aef9fe0
--- /dev/null
+++ b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ApplicationScopeService.java
@@ -0,0 +1,7 @@
+package io.quarkus.ts.cache.infinispan;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class ApplicationScopeService extends BaseServiceWithCache {
+}
diff --git a/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/BaseServiceWithCache.java b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/BaseServiceWithCache.java
new file mode 100644
index 000000000..2484dd9a0
--- /dev/null
+++ b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/BaseServiceWithCache.java
@@ -0,0 +1,52 @@
+package io.quarkus.ts.cache.infinispan;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.infinispan.protostream.GeneratedSchema;
+import org.infinispan.protostream.annotations.Proto;
+import org.infinispan.protostream.annotations.ProtoSchema;
+
+import io.quarkus.cache.CacheInvalidate;
+import io.quarkus.cache.CacheInvalidateAll;
+import io.quarkus.cache.CacheKey;
+import io.quarkus.cache.CacheResult;
+
+public abstract class BaseServiceWithCache {
+
+ private static final String CACHE_NAME = "service-cache";
+
+ private static final AtomicInteger counter = new AtomicInteger(0);
+
+ @CacheResult(cacheName = CACHE_NAME)
+ public String getValue() {
+ return "Value: " + counter.getAndIncrement();
+ }
+
+ @CacheInvalidate(cacheName = CACHE_NAME)
+ public void invalidate() {
+ // do nothing
+ }
+
+ @CacheResult(cacheName = CACHE_NAME)
+ public ExpensiveResponse getValueWithPrefix(@CacheKey String prefix) {
+ return new ExpensiveResponse(prefix + ": " + counter.getAndIncrement());
+ }
+
+ @CacheInvalidate(cacheName = CACHE_NAME)
+ public void invalidateWithPrefix(@CacheKey String prefix) {
+ // do nothing
+ }
+
+ @CacheInvalidateAll(cacheName = CACHE_NAME)
+ public void invalidateAll() {
+ // do nothing
+ }
+
+ @Proto
+ public record ExpensiveResponse(String result) {
+ }
+
+ @ProtoSchema(includeClasses = { ExpensiveResponse.class })
+ interface Schema extends GeneratedSchema {
+ }
+}
diff --git a/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/BlockingWithCacheResource.java b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/BlockingWithCacheResource.java
new file mode 100644
index 000000000..0283b75af
--- /dev/null
+++ b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/BlockingWithCacheResource.java
@@ -0,0 +1,57 @@
+package io.quarkus.ts.cache.infinispan;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+
+import io.quarkus.cache.CacheInvalidate;
+import io.quarkus.cache.CacheInvalidateAll;
+import io.quarkus.cache.CacheKey;
+import io.quarkus.cache.CacheResult;
+import io.smallrye.common.annotation.Blocking;
+
+@Blocking
+@Path("/api/blocking")
+public class BlockingWithCacheResource {
+
+ private static final String CACHE_NAME = "api-blocking-cache";
+
+ private final AtomicInteger counter = new AtomicInteger(0);
+
+ @GET
+ @CacheResult(cacheName = CACHE_NAME)
+ public String getValue() {
+ return "Value: " + counter.getAndIncrement();
+ }
+
+ @POST
+ @Path("/invalidate-cache")
+ @CacheInvalidate(cacheName = CACHE_NAME)
+ public void invalidate() {
+ // do nothing
+ }
+
+ @GET
+ @Path("/using-prefix/{prefix}")
+ @CacheResult(cacheName = CACHE_NAME)
+ public String getValueWithPrefix(@PathParam("prefix") @CacheKey String prefix) {
+ return prefix + ": " + counter.getAndIncrement();
+ }
+
+ @POST
+ @Path("/using-prefix/{prefix}/invalidate-cache")
+ @CacheInvalidate(cacheName = CACHE_NAME)
+ public void invalidateWithPrefix(@PathParam("prefix") @CacheKey String prefix) {
+ // do nothing
+ }
+
+ @POST
+ @Path("/invalidate-cache-all")
+ @CacheInvalidateAll(cacheName = CACHE_NAME)
+ public void invalidateAll() {
+ // do nothing
+ }
+}
diff --git a/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/CacheExpirationResource.java b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/CacheExpirationResource.java
new file mode 100644
index 000000000..98823bd57
--- /dev/null
+++ b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/CacheExpirationResource.java
@@ -0,0 +1,21 @@
+package io.quarkus.ts.cache.infinispan;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+import io.quarkus.cache.CacheKey;
+import io.quarkus.cache.CacheResult;
+
+@Path("/cache")
+public class CacheExpirationResource {
+
+ @GET
+ @Path("/{key}")
+ @Produces(MediaType.TEXT_PLAIN)
+ @CacheResult(cacheName = "expiring-cache")
+ public String getCachedValue(@CacheKey String key) {
+ return "Value for key " + key + " at " + System.currentTimeMillis();
+ }
+}
diff --git a/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ReactiveWithCacheResource.java b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ReactiveWithCacheResource.java
new file mode 100644
index 000000000..85989af67
--- /dev/null
+++ b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ReactiveWithCacheResource.java
@@ -0,0 +1,58 @@
+package io.quarkus.ts.cache.infinispan;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+
+import io.quarkus.cache.CacheInvalidate;
+import io.quarkus.cache.CacheInvalidateAll;
+import io.quarkus.cache.CacheKey;
+import io.quarkus.cache.CacheResult;
+import io.smallrye.common.annotation.NonBlocking;
+import io.smallrye.mutiny.Uni;
+
+@NonBlocking
+@Path("/api/reactive")
+public class ReactiveWithCacheResource {
+
+ private static final String CACHE_NAME = "api-reactive-cache";
+
+ private final AtomicInteger counter = new AtomicInteger(0);
+
+ @GET
+ @CacheResult(cacheName = CACHE_NAME)
+ public Uni getValue() {
+ return Uni.createFrom().item("Value: " + counter.getAndIncrement());
+ }
+
+ @POST
+ @Path("/invalidate-cache")
+ @CacheInvalidate(cacheName = CACHE_NAME)
+ public Uni invalidate() {
+ return Uni.createFrom().nullItem();
+ }
+
+ @GET
+ @Path("/using-prefix/{prefix}")
+ @CacheResult(cacheName = CACHE_NAME)
+ public Uni getValueWithPrefix(@PathParam("prefix") @CacheKey String prefix) {
+ return Uni.createFrom().item(prefix + ": " + counter.getAndIncrement());
+ }
+
+ @POST
+ @Path("/using-prefix/{prefix}/invalidate-cache")
+ @CacheInvalidate(cacheName = CACHE_NAME)
+ public Uni invalidateWithPrefix(@PathParam("prefix") @CacheKey String prefix) {
+ return Uni.createFrom().nullItem();
+ }
+
+ @POST
+ @Path("/invalidate-cache-all")
+ @CacheInvalidateAll(cacheName = CACHE_NAME)
+ public Uni invalidateAll() {
+ return Uni.createFrom().nullItem();
+ }
+}
diff --git a/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/RequestScopeService.java b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/RequestScopeService.java
new file mode 100644
index 000000000..8828dad0c
--- /dev/null
+++ b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/RequestScopeService.java
@@ -0,0 +1,7 @@
+package io.quarkus.ts.cache.infinispan;
+
+import jakarta.enterprise.context.RequestScoped;
+
+@RequestScoped
+public class RequestScopeService extends BaseServiceWithCache {
+}
diff --git a/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ServiceWithCacheResource.java b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ServiceWithCacheResource.java
new file mode 100644
index 000000000..3ad935326
--- /dev/null
+++ b/cache/infinispan/src/main/java/io/quarkus/ts/cache/infinispan/ServiceWithCacheResource.java
@@ -0,0 +1,66 @@
+package io.quarkus.ts.cache.infinispan;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+@Path("/services")
+public class ServiceWithCacheResource {
+
+ public static final String APPLICATION_SCOPE_SERVICE_PATH = "application-scope";
+ public static final String REQUEST_SCOPE_SERVICE_PATH = "request-scope";
+
+ @Inject
+ ApplicationScopeService applicationScopeService;
+
+ @Inject
+ RequestScopeService requestScopeService;
+
+ @GET
+ @Path("/{service}")
+ @Produces(MediaType.TEXT_PLAIN)
+ public String getValueFromService(@PathParam("service") String service) {
+ return lookupServiceByPathParam(service).getValue();
+ }
+
+ @POST
+ @Path("/{service}/invalidate-cache")
+ public void invalidateCacheFromService(@PathParam("service") String service) {
+ lookupServiceByPathParam(service).invalidate();
+ }
+
+ @POST
+ @Path("/{service}/invalidate-cache-all")
+ public void invalidateCacheAllFromService(@PathParam("service") String service) {
+ lookupServiceByPathParam(service).invalidateAll();
+ }
+
+ @GET
+ @Path("/{service}/using-prefix/{prefix}")
+ @Produces(MediaType.TEXT_PLAIN)
+ public BaseServiceWithCache.ExpensiveResponse getValueUsingPrefixFromService(@PathParam("service") String service,
+ @PathParam("prefix") String prefix) {
+ return lookupServiceByPathParam(service).getValueWithPrefix(prefix);
+ }
+
+ @POST
+ @Path("/{service}/using-prefix/{prefix}/invalidate-cache")
+ public void invalidateCacheUsingPrefixFromService(@PathParam("service") String service,
+ @PathParam("prefix") String prefix) {
+ lookupServiceByPathParam(service).invalidateWithPrefix(prefix);
+ }
+
+ private BaseServiceWithCache lookupServiceByPathParam(String service) {
+ if (APPLICATION_SCOPE_SERVICE_PATH.equals(service)) {
+ return applicationScopeService;
+ } else if (REQUEST_SCOPE_SERVICE_PATH.equals(service)) {
+ return requestScopeService;
+ }
+
+ throw new IllegalArgumentException("Service " + service + " is not recognised");
+ }
+}
diff --git a/cache/infinispan/src/main/resources/application.properties b/cache/infinispan/src/main/resources/application.properties
new file mode 100644
index 000000000..a3c0fad0f
--- /dev/null
+++ b/cache/infinispan/src/main/resources/application.properties
@@ -0,0 +1,3 @@
+quarkus.infinispan-client.hosts=localhost:11222
+quarkus.cache.infinispan.expiring-cache.lifespan=3s
+quarkus.cache.infinispan.expiring-cache.max-idle=5s
diff --git a/cache/infinispan/src/test/java/io/quarkus/ts/cache/infinispan/InfinispanCacheIT.java b/cache/infinispan/src/test/java/io/quarkus/ts/cache/infinispan/InfinispanCacheIT.java
new file mode 100644
index 000000000..82c1ffc0a
--- /dev/null
+++ b/cache/infinispan/src/test/java/io/quarkus/ts/cache/infinispan/InfinispanCacheIT.java
@@ -0,0 +1,215 @@
+package io.quarkus.ts.cache.infinispan;
+
+import static io.quarkus.ts.cache.infinispan.ServiceWithCacheResource.APPLICATION_SCOPE_SERVICE_PATH;
+import static io.quarkus.ts.cache.infinispan.ServiceWithCacheResource.REQUEST_SCOPE_SERVICE_PATH;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import org.apache.http.HttpStatus;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import io.quarkus.test.bootstrap.InfinispanService;
+import io.quarkus.test.bootstrap.RestService;
+import io.quarkus.test.scenarios.QuarkusScenario;
+import io.quarkus.test.services.Container;
+import io.quarkus.test.services.QuarkusApplication;
+
+@QuarkusScenario
+public class InfinispanCacheIT {
+
+ private static final String SERVICE_APPLICATION_SCOPE_PATH = "/services/" + APPLICATION_SCOPE_SERVICE_PATH;
+ private static final String SERVICE_REQUEST_SCOPE_PATH = "/services/" + REQUEST_SCOPE_SERVICE_PATH;
+ private static final String RESOURCE_BLOCKING_API_PATH = "/api/blocking";
+ private static final String RESOURCE_REACTIVE_API_PATH = "/api/reactive";
+
+ private static final String PREFIX_ONE = "prefix1";
+ private static final String PREFIX_TWO = "prefix2";
+
+ private static final int INFINISPAN_PORT = 11222;
+
+ @Container(image = "${infinispan.image}", expectedLog = "${infinispan.expected-log}", port = INFINISPAN_PORT)
+ static InfinispanService infinispan = new InfinispanService()
+ .withUsername("admin")
+ .withPassword("password");
+
+ @QuarkusApplication()
+ static RestService app = new RestService()
+ .withProperty("quarkus.infinispan-client.hosts", infinispan::getInfinispanServerAddress)
+ .withProperties("test.properties");
+
+ /**
+ * Check whether the `@CacheResult` annotation works when used in a service.
+ */
+ @ParameterizedTest
+ @ValueSource(strings = { SERVICE_APPLICATION_SCOPE_PATH, SERVICE_REQUEST_SCOPE_PATH, RESOURCE_BLOCKING_API_PATH,
+ RESOURCE_REACTIVE_API_PATH })
+ public void shouldGetTheSameValueAlwaysWhenGettingValueFromPath(String path) {
+ // We call the service endpoint
+ String value = getFromPath(path);
+
+ // At this point, the cache is populated and we should get the same value from the cache
+ assertEquals(value, getFromPath(path), "Value was different which means cache is not working");
+ }
+
+ /**
+ * Check whether the `@CacheInvalidate` annotation invalidates the cache when used in a service.
+ */
+ @ParameterizedTest
+ @ValueSource(strings = { SERVICE_APPLICATION_SCOPE_PATH, SERVICE_REQUEST_SCOPE_PATH, RESOURCE_BLOCKING_API_PATH,
+ RESOURCE_REACTIVE_API_PATH })
+ public void shouldGetDifferentValueWhenInvalidateCacheFromPath(String path) {
+ // We call the service endpoint
+ String value = getFromPath(path);
+
+ // invalidate the cache
+ invalidateCacheFromPath(path);
+
+ // Then the value should be different as we have invalidated the cache.
+ assertNotEquals(value, getFromPath(path), "Value was equal which means cache invalidate didn't work");
+ }
+
+ /**
+ * Check whether the `@CacheResult` annotation works when used in a service.
+ */
+ @ParameterizedTest
+ @ValueSource(strings = { SERVICE_APPLICATION_SCOPE_PATH, SERVICE_REQUEST_SCOPE_PATH, RESOURCE_BLOCKING_API_PATH,
+ RESOURCE_REACTIVE_API_PATH })
+ public void shouldGetTheSameValueForSamePrefixesWhenGettingValueFromPath(String path) {
+ // We call the service endpoint
+ String value = getValueFromPathUsingPrefix(path, PREFIX_ONE);
+
+ // At this point, the cache is populated and we should get the same value from the cache
+ assertEquals(value, getValueFromPathUsingPrefix(path, PREFIX_ONE),
+ "Value was different which means cache is not working");
+ // But different value using another prefix
+ assertNotEquals(value, getValueFromPathUsingPrefix(path, PREFIX_TWO),
+ "Value was equal which means @CacheKey didn't work");
+ }
+
+ /**
+ * Check whether the `@CacheInvalidate` annotation does not invalidate all the caches
+ */
+ @ParameterizedTest
+ @ValueSource(strings = { SERVICE_APPLICATION_SCOPE_PATH, SERVICE_REQUEST_SCOPE_PATH, RESOURCE_BLOCKING_API_PATH,
+ RESOURCE_REACTIVE_API_PATH })
+ public void shouldGetTheSameValuesEvenAfterCallingToCacheInvalidateFromPath(String path) {
+ // We call the service endpoints
+ String valueOfPrefix1 = getValueFromPathUsingPrefix(path, PREFIX_ONE);
+ String valueOfPrefix2 = getValueFromPathUsingPrefix(path, PREFIX_TWO);
+
+ // invalidate the cache: this should not invalidate all the keys
+ invalidateCacheFromPath(path);
+
+ // At this point, the cache is populated and we should get the same value for both prefixes
+ assertEquals(valueOfPrefix1, getValueFromPathUsingPrefix(path, PREFIX_ONE));
+ assertEquals(valueOfPrefix2, getValueFromPathUsingPrefix(path, PREFIX_TWO));
+ }
+
+ /**
+ * Check whether the `@CacheInvalidate` and `@CacheKey` annotations work as expected.
+ */
+ @ParameterizedTest
+ @ValueSource(strings = { SERVICE_APPLICATION_SCOPE_PATH, SERVICE_REQUEST_SCOPE_PATH, RESOURCE_BLOCKING_API_PATH,
+ RESOURCE_REACTIVE_API_PATH })
+ public void shouldGetDifferentValueWhenInvalidateCacheOnlyForOnePrefixFromPath(String path) {
+ // We call the service endpoints
+ String valueOfPrefix1 = getValueFromPathUsingPrefix(path, PREFIX_ONE);
+ String valueOfPrefix2 = getValueFromPathUsingPrefix(path, PREFIX_TWO);
+
+ // invalidate the cache: this should not invalidate all the keys
+ invalidateCacheWithPrefixFromPath(path, PREFIX_ONE);
+
+ // The cache was invalidated only for prefix1, so the value should be different
+ assertNotEquals(valueOfPrefix1, getValueFromPathUsingPrefix(path, PREFIX_ONE));
+ // The cache was not invalidated for prefix2, so the value should be the same
+ assertEquals(valueOfPrefix2, getValueFromPathUsingPrefix(path, PREFIX_TWO));
+ }
+
+ /**
+ * Check whether the `@CacheInvalidateAll` annotation works as expected.
+ */
+ @ParameterizedTest
+ @ValueSource(strings = { SERVICE_APPLICATION_SCOPE_PATH, SERVICE_REQUEST_SCOPE_PATH, RESOURCE_BLOCKING_API_PATH,
+ RESOURCE_REACTIVE_API_PATH })
+ public void shouldGetDifferentValueWhenInvalidateAllTheCacheFromPath(String path) {
+ // We call the service endpoints
+ String value = getFromPath(path);
+ String valueOfPrefix1 = getValueFromPathUsingPrefix(path, PREFIX_ONE);
+ String valueOfPrefix2 = getValueFromPathUsingPrefix(path, PREFIX_TWO);
+
+ // invalidate all the cache
+ invalidateCacheAllFromPath(path);
+
+ // Then, all the values should be different:
+ assertNotEquals(value, getFromPath(path));
+ assertNotEquals(valueOfPrefix1, getValueFromPathUsingPrefix(path, PREFIX_ONE));
+ assertNotEquals(valueOfPrefix2, getValueFromPathUsingPrefix(path, PREFIX_TWO));
+ }
+
+ @Test
+ public void testCacheLifespan() throws InterruptedException {
+ // First request, value is cached
+ String firstResponse = getFromPath("/cache/testLifespan");
+
+ // Wait for 3 seconds and make another request, cache should return a different result
+ Thread.sleep(3000);
+
+ String secondResponse = getFromPath("/cache/testLifespan");
+
+ assertNotEquals(firstResponse, secondResponse, "Cache should return a new value after expiration");
+ }
+
+ @Test
+ public void testCacheMaxIdle() throws InterruptedException {
+ // First request
+ String firstResponse = getFromPath("/cache/testIdle");
+
+ // Wait for 2 seconds and make another request, cache should return the same result
+ Thread.sleep(2000);
+
+ String secondResponse = getFromPath("/cache/testIdle");
+
+ assertEquals(firstResponse, secondResponse, "Cache should hold the same value within the lifespan and max-idle");
+
+ // Wait for 5 more seconds and make another request, cache should return a different result
+ Thread.sleep(5000);
+
+ String thirdResponse = getFromPath("/cache/testIdle");
+
+ assertNotEquals(firstResponse, thirdResponse, "Cache should return a new value after expiration");
+ }
+
+ private void invalidateCacheAllFromPath(String path) {
+ postFromPath(path + "/invalidate-cache-all");
+ }
+
+ private void invalidateCacheWithPrefixFromPath(String path, String prefix) {
+ postFromPath(path + "/using-prefix/" + prefix + "/invalidate-cache");
+ }
+
+ private void invalidateCacheFromPath(String path) {
+ postFromPath(path + "/invalidate-cache");
+ }
+
+ private String getValueFromPathUsingPrefix(String path, String prefix) {
+ return getFromPath(path + "/using-prefix/" + prefix);
+ }
+
+ private String getFromPath(String path) {
+ return app.given()
+ .when().get(path)
+ .then()
+ .statusCode(HttpStatus.SC_OK)
+ .extract().asString();
+ }
+
+ private void postFromPath(String path) {
+ app.given()
+ .when().post(path)
+ .then()
+ .statusCode(HttpStatus.SC_NO_CONTENT);
+ }
+
+}
diff --git a/cache/infinispan/src/test/java/io/quarkus/ts/cache/infinispan/OpenShiftInfinispanCacheIT.java b/cache/infinispan/src/test/java/io/quarkus/ts/cache/infinispan/OpenShiftInfinispanCacheIT.java
new file mode 100644
index 000000000..49e4e4a14
--- /dev/null
+++ b/cache/infinispan/src/test/java/io/quarkus/ts/cache/infinispan/OpenShiftInfinispanCacheIT.java
@@ -0,0 +1,7 @@
+package io.quarkus.ts.cache.infinispan;
+
+import io.quarkus.test.scenarios.OpenShiftScenario;
+
+@OpenShiftScenario
+public class OpenShiftInfinispanCacheIT extends InfinispanCacheIT {
+}
diff --git a/cache/infinispan/src/test/resources/test.properties b/cache/infinispan/src/test/resources/test.properties
new file mode 100644
index 000000000..4d3b274e8
--- /dev/null
+++ b/cache/infinispan/src/test/resources/test.properties
@@ -0,0 +1,3 @@
+ts.infinispan.openshift.use-internal-service-as-url=true
+quarkus.infinispan-client.username=admin
+quarkus.infinispan-client.password=password
diff --git a/pom.xml b/pom.xml
index b19afadce..3e279d67e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -456,6 +456,7 @@
env-info
cache/caffeine
cache/redis
+ cache/infinispan
infinispan-client