Skip to content

Commit 7ff47de

Browse files
Merge pull request quarkusio#47968 from holly-cummins/redis-devmode-tests
Add tests for redis dev services behaviour in devmode
2 parents a5a1ba5 + 1bdf2ef commit 7ff47de

File tree

4 files changed

+305
-0
lines changed

4 files changed

+305
-0
lines changed

integration-tests/redis-devservices/pom.xml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
<groupId>io.quarkus</groupId>
1616
<artifactId>quarkus-redis-client</artifactId>
1717
</dependency>
18+
<dependency>
19+
<groupId>io.quarkus</groupId>
20+
<artifactId>quarkus-rest-jackson</artifactId>
21+
</dependency>
1822
<dependency>
1923
<groupId>io.quarkus</groupId>
2024
<artifactId>quarkus-arc</artifactId>
@@ -24,6 +28,21 @@
2428
<artifactId>quarkus-junit5</artifactId>
2529
<scope>test</scope>
2630
</dependency>
31+
<dependency>
32+
<groupId>io.quarkus</groupId>
33+
<artifactId>quarkus-devservices-common</artifactId>
34+
<scope>test</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>io.quarkus</groupId>
38+
<artifactId>quarkus-junit5-internal</artifactId>
39+
<scope>test</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>io.rest-assured</groupId>
43+
<artifactId>rest-assured</artifactId>
44+
<scope>test</scope>
45+
</dependency>
2746
<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
2847
<dependency>
2948
<groupId>io.quarkus</groupId>
@@ -51,6 +70,19 @@
5170
</exclusion>
5271
</exclusions>
5372
</dependency>
73+
<dependency>
74+
<groupId>io.quarkus</groupId>
75+
<artifactId>quarkus-rest-jackson-deployment</artifactId>
76+
<version>${project.version}</version>
77+
<type>pom</type>
78+
<scope>test</scope>
79+
<exclusions>
80+
<exclusion>
81+
<groupId>*</groupId>
82+
<artifactId>*</artifactId>
83+
</exclusion>
84+
</exclusions>
85+
</dependency>
5486
</dependencies>
5587

5688
<build>
@@ -103,6 +135,30 @@
103135
<configuration>
104136
<skip>false</skip>
105137
</configuration>
138+
<executions>
139+
<execution>
140+
<id>default-test</id>
141+
<goals>
142+
<goal>test</goal>
143+
</goals>
144+
<configuration>
145+
<excludes>
146+
<exclude>**/continuoustesting/**/*.java</exclude>
147+
</excludes>
148+
</configuration>
149+
</execution>
150+
<execution>
151+
<id>devmode-test</id>
152+
<goals>
153+
<goal>test</goal>
154+
</goals>
155+
<configuration>
156+
<includes>
157+
<include>**/continuoustesting/**/*.java</include>
158+
</includes>
159+
</configuration>
160+
</execution>
161+
</executions>
106162
</plugin>
107163
<plugin>
108164
<artifactId>maven-failsafe-plugin</artifactId>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.quarkus.test.devservices.redis;
2+
3+
import jakarta.inject.Inject;
4+
import jakarta.ws.rs.GET;
5+
import jakarta.ws.rs.Path;
6+
import jakarta.ws.rs.PathParam;
7+
8+
import io.quarkus.redis.datasource.RedisDataSource;
9+
10+
@Path("/")
11+
public class TestResource {
12+
13+
@Inject
14+
RedisDataSource redis;
15+
16+
@GET
17+
@Path("/ping")
18+
public String ping() {
19+
return redis.execute("ping").toString();
20+
}
21+
22+
@GET
23+
@Path("/set/{key}/{value}")
24+
public String set(@PathParam("key") String key, @PathParam("value") String value) {
25+
redis.value(String.class).set(key, value);
26+
return "OK";
27+
}
28+
29+
@GET
30+
@Path("/get/{key}")
31+
public String get(@PathParam("key") String key) {
32+
return redis.value(String.class).get(key);
33+
}
34+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package io.quarkus.redis.devservices.continuoustesting.it;
2+
3+
import static io.restassured.RestAssured.when;
4+
import static org.hamcrest.Matchers.is;
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
import java.util.ArrayList;
10+
import java.util.Arrays;
11+
import java.util.Collection;
12+
import java.util.List;
13+
14+
import org.jboss.shrinkwrap.api.ShrinkWrap;
15+
import org.jboss.shrinkwrap.api.asset.StringAsset;
16+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
17+
import org.junit.jupiter.api.Disabled;
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.extension.RegisterExtension;
20+
import org.testcontainers.DockerClientFactory;
21+
22+
import com.github.dockerjava.api.model.Container;
23+
import com.github.dockerjava.api.model.ContainerPort;
24+
25+
import io.quarkus.redis.devservices.it.PlainQuarkusTest;
26+
import io.quarkus.test.ContinuousTestingTestUtils;
27+
import io.quarkus.test.QuarkusDevModeTest;
28+
import io.quarkus.test.devservices.redis.TestResource;
29+
30+
public class DevServicesRedisContinuousTestingTest {
31+
32+
static final String DEVSERVICES_DISABLED_PROPERTIES = ContinuousTestingTestUtils.appProperties(
33+
"quarkus.devservices.enabled=false");
34+
35+
static final String FIXED_PORT_PROPERTIES = ContinuousTestingTestUtils.appProperties(
36+
"quarkus.redis.devservices.port=6377");
37+
38+
static final String UPDATED_FIXED_PORT_PROPERTIES = ContinuousTestingTestUtils.appProperties(
39+
"quarkus.redis.devservices.port=6342");
40+
41+
@RegisterExtension
42+
public static QuarkusDevModeTest test = new QuarkusDevModeTest()
43+
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
44+
.addClasses(TestResource.class)
45+
.addAsResource(new StringAsset(ContinuousTestingTestUtils.appProperties("")),
46+
"application.properties"))
47+
.setTestArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClass(PlainQuarkusTest.class));
48+
49+
@Disabled("Not currently working")
50+
@Test
51+
public void testContinuousTestingDisablesDevServicesWhenPropertiesChange() {
52+
ContinuousTestingTestUtils utils = new ContinuousTestingTestUtils();
53+
var result = utils.waitForNextCompletion();
54+
assertEquals(1, result.getTotalTestsPassed());
55+
assertEquals(0, result.getTotalTestsFailed());
56+
57+
// Now let's disable dev services globally ... BOOOOOM! Splat!
58+
test.modifyResourceFile("application.properties", s -> DEVSERVICES_DISABLED_PROPERTIES);
59+
result = utils.waitForNextCompletion();
60+
assertEquals(0, result.getTotalTestsPassed());
61+
assertEquals(1, result.getTotalTestsFailed());
62+
63+
// We could check the container goes away, but we'd have to check slowly, because ryuk can be slow
64+
}
65+
66+
// This tests behaviour in dev mode proper (rather than continuous testing)
67+
@Test
68+
public void testDevModeServiceConfigRefresh() {
69+
List<Container> started = getRedisContainers();
70+
// Interacting with the app will force a refresh
71+
ping();
72+
73+
assertFalse(started.isEmpty());
74+
Container container = started.get(0);
75+
assertTrue(Arrays.stream(container.getPorts()).noneMatch(p -> p.getPublicPort() == 6377),
76+
"Expected random port, but got: " + Arrays.toString(container.getPorts()));
77+
78+
test.modifyResourceFile("application.properties",
79+
s -> ContinuousTestingTestUtils.appProperties(FIXED_PORT_PROPERTIES));
80+
81+
// Force another refresh
82+
ping();
83+
List<Container> newContainers = getRedisContainersExcludingExisting(started);
84+
assertEquals(1, newContainers.size()); // this can be wrong
85+
Container newContainer = newContainers.get(0);
86+
assertTrue(Arrays.stream(newContainer.getPorts()).anyMatch(p -> p.getPublicPort() == 6377),
87+
"Expected port 6377, but got: " + Arrays.toString(newContainer.getPorts()));
88+
}
89+
90+
void ping() {
91+
when().get("/ping").then()
92+
.statusCode(200)
93+
.body(is("PONG"));
94+
}
95+
96+
@Test
97+
public void testContinuousTestingReusesInstanceWhenPropertiesAreNotChanged() {
98+
99+
ContinuousTestingTestUtils utils = new ContinuousTestingTestUtils();
100+
var result = utils.waitForNextCompletion();
101+
assertEquals(1, result.getTotalTestsPassed());
102+
assertEquals(0, result.getTotalTestsFailed());
103+
List<Container> redisContainers = getRedisContainers();
104+
105+
// Make a change that shouldn't affect dev services
106+
test.modifyTestSourceFile(PlainQuarkusTest.class, s -> s.replaceAll("redisClient", "updatedRedisClient"));
107+
108+
result = utils.waitForNextCompletion();
109+
assertEquals(1, result.getTestsPassed());
110+
assertEquals(0, result.getTestsFailed());
111+
112+
// Some containers could have disappeared, because ryuk cleaned them up, but no new containers should have appeared
113+
List<Container> newContainers = getRedisContainersExcludingExisting(redisContainers);
114+
assertEquals(0, newContainers.size(),
115+
"New containers: " + newContainers + "\n Old containers: " + redisContainers + "\n All containers: "
116+
+ getAllContainers());
117+
}
118+
119+
@Test
120+
public void testContinuousTestingCreatesANewInstanceWhenPropertiesAreChanged() {
121+
122+
ContinuousTestingTestUtils utils = new ContinuousTestingTestUtils();
123+
var result = utils.waitForNextCompletion();
124+
assertEquals(1, result.getTotalTestsPassed());
125+
assertEquals(0, result.getTotalTestsFailed());
126+
List<Container> existingContainers = new ArrayList<>();
127+
existingContainers.addAll(getRedisContainers());
128+
129+
test.modifyResourceFile("application.properties", s -> FIXED_PORT_PROPERTIES);
130+
131+
result = utils.waitForNextCompletion();
132+
assertEquals(1, result.getTestsPassed());
133+
assertEquals(0, result.getTestsFailed());
134+
135+
// A new container should have appeared
136+
{
137+
List<Container> newContainers = getRedisContainersExcludingExisting(existingContainers);
138+
existingContainers.addAll(newContainers);
139+
assertEquals(1, newContainers.size(),
140+
"New containers: " + newContainers + "\n Old containers: " + existingContainers + "\n All containers: "
141+
+ getAllContainers());
142+
143+
// The new container should be on the new port
144+
List<Integer> ports = Arrays.stream(newContainers.get(0).getPorts())
145+
.map(ContainerPort::getPublicPort)
146+
.toList();
147+
148+
// Oh good, it's one port, so it should be the expected one
149+
assertTrue(ports.contains(6377), "Container ports: " + ports);
150+
}
151+
test.modifyResourceFile("application.properties", s -> UPDATED_FIXED_PORT_PROPERTIES);
152+
153+
result = utils.waitForNextCompletion();
154+
assertEquals(1, result.getTestsPassed());
155+
assertEquals(0, result.getTestsFailed());
156+
157+
// Another new container should have appeared
158+
159+
{
160+
List<Container> newContainers = getRedisContainersExcludingExisting(existingContainers);
161+
assertEquals(1, newContainers.size(),
162+
"New containers: " + newContainers + "\n Old containers: " + existingContainers + "\n All containers: "
163+
+ getAllContainers());
164+
165+
// The new container should be on the new port
166+
List<Integer> ports = Arrays.stream(newContainers.get(0).getPorts())
167+
.map(ContainerPort::getPublicPort)
168+
.toList();
169+
assertTrue(ports.contains(6342), "Container ports: " + ports);
170+
171+
}
172+
}
173+
174+
private static List<Container> getAllContainers() {
175+
return DockerClientFactory.lazyClient().listContainersCmd().exec().stream()
176+
.filter(container -> isRedisContainer(container)).toList();
177+
}
178+
179+
private static List<Container> getRedisContainers() {
180+
return getAllContainers();
181+
}
182+
183+
private static List<Container> getRedisContainersExcludingExisting(Collection<Container> existingContainers) {
184+
return getRedisContainers().stream().filter(
185+
container -> existingContainers.stream().noneMatch(existing -> existing.getId().equals(container.getId())))
186+
.toList();
187+
}
188+
189+
private static boolean isRedisContainer(Container container) {
190+
// The output of getCommand() seems to vary by host OS (it's different on CI and mac), but the image name should be reliable
191+
return container.getImage().contains("redis");
192+
}
193+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.quarkus.redis.devservices.it;
2+
3+
import static org.junit.jupiter.api.Assertions.assertNotNull;
4+
5+
import jakarta.inject.Inject;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
import io.quarkus.redis.datasource.RedisDataSource;
10+
import io.quarkus.test.junit.QuarkusTest;
11+
12+
@QuarkusTest
13+
public class PlainQuarkusTest {
14+
15+
@Inject
16+
RedisDataSource redisClient;
17+
18+
@Test
19+
public void shouldStartRedisContainer() {
20+
assertNotNull(redisClient);
21+
}
22+
}

0 commit comments

Comments
 (0)