1919
2020import com .github .sonus21 .rqueue .annotation .RqueueListener ;
2121import com .github .sonus21 .rqueue .core .RqueueMessageEnqueuer ;
22+ import java .net .ServerSocket ;
2223import java .util .ArrayList ;
2324import java .util .Collections ;
2425import java .util .List ;
2526import java .util .concurrent .CountDownLatch ;
2627import java .util .concurrent .TimeUnit ;
28+ import org .junit .jupiter .api .AfterAll ;
29+ import org .junit .jupiter .api .BeforeAll ;
2730import org .junit .jupiter .api .Tag ;
2831import org .junit .jupiter .api .Test ;
2932import org .springframework .beans .factory .annotation .Autowired ;
3033import org .springframework .boot .autoconfigure .SpringBootApplication ;
31- import org .springframework .boot .data .redis .autoconfigure .DataRedisAutoConfiguration ;
32- import org .springframework .boot .data .redis .autoconfigure .DataRedisReactiveAutoConfiguration ;
3334import org .springframework .boot .test .context .SpringBootTest ;
3435import org .springframework .stereotype .Component ;
3536import org .springframework .test .context .DynamicPropertyRegistry ;
3940import org .testcontainers .junit .jupiter .Container ;
4041import org .testcontainers .junit .jupiter .Testcontainers ;
4142import org .testcontainers .utility .DockerImageName ;
43+ import redis .embedded .RedisServer ;
4244
4345/**
4446 * End-to-end integration test wiring a Spring Boot application against a Testcontainers-managed
5052 * -> BrokerMessagePoller.pop -> @RqueueListener invocation -> broker.ack
5153 * </pre>
5254 *
53- * <h2>Why Redis auto-config is excluded </h2>
55+ * <h2>Why an embedded Redis is started </h2>
5456 *
55- * The starter declares {@code spring-boot-starter-data-redis} as an {@code api} dependency, so
56- * Spring Boot would try to auto-configure a {@code LettuceConnectionFactory} at startup and fail
57- * the context because no Redis instance is available in this test. Excluding the Redis
58- * auto-config classes on {@link TestApp} (rather than via property) keeps the exclusion local to
59- * this test and visible to readers.
60- *
61- * <h2>Producer path</h2>
62- *
63- * The sync producer flows through {@code BaseMessageSender#enqueue} which now delegates to
64- * {@code MessageBroker.enqueue(QueueDetail, RqueueMessage)} when the active broker advertises
65- * {@code !usesPrimaryHandlerDispatch()}, and {@code storeMessageMetadata} short-circuits on the
66- * same flag so no Redis HASH write is attempted. Together with the broker-driven poller this
67- * exercises the full produce-and-consume loop without touching Redis.
57+ * Several rqueue beans (notably {@code RqueueConfig}, {@code RqueueQStatsDaoImpl}, and the
58+ * dashboard controller chain) currently still require a {@code RedisConnectionFactory} as a hard
59+ * Spring dependency, even when {@code rqueue.backend=nats}. Pure NATS-only deployments without
60+ * Redis on the classpath will need those beans to become conditional — tracked as a v1.x
61+ * follow-up. Until then this test starts an embedded Redis purely to satisfy the bean graph;
62+ * the actual message flow runs entirely through JetStream and never hits Redis.
6863 */
6964@ SpringBootTest (
7065 classes = NatsBackendEndToEndIT .TestApp .class ,
7368@ Tag ("nats" )
7469class NatsBackendEndToEndIT {
7570
71+ private static RedisServer REDIS ;
72+ private static int REDIS_PORT ;
73+
7674 @ Container
77- static final GenericContainer <?> NATS = new GenericContainer <>(
78- DockerImageName .parse ("nats:2.10-alpine" ))
79- .withCommand ("-js" )
80- .withExposedPorts (4222 )
81- .waitingFor (Wait .forLogMessage (".*Server is ready.*\\ n" , 1 ));
75+ static final GenericContainer <?> NATS =
76+ new GenericContainer <>(DockerImageName .parse ("nats:2.10-alpine" ))
77+ .withCommand ("-js" )
78+ .withExposedPorts (4222 )
79+ .waitingFor (Wait .forLogMessage (".*Server is ready.*\\ n" , 1 ));
80+
81+ @ BeforeAll
82+ static void startRedis () throws Exception {
83+ try (ServerSocket s = new ServerSocket (0 )) {
84+ REDIS_PORT = s .getLocalPort ();
85+ }
86+ REDIS = new RedisServer (REDIS_PORT );
87+ REDIS .start ();
88+ }
89+
90+ @ AfterAll
91+ static void stopRedis () throws Exception {
92+ if (REDIS != null ) {
93+ REDIS .stop ();
94+ }
95+ }
8296
8397 @ DynamicPropertySource
84- static void natsProps (DynamicPropertyRegistry r ) {
98+ static void registerProps (DynamicPropertyRegistry r ) {
8599 r .add (
86100 "rqueue.nats.connection.url" ,
87101 () -> "nats://" + NATS .getHost () + ":" + NATS .getMappedPort (4222 ));
102+ r .add ("spring.data.redis.host" , () -> "localhost" );
103+ r .add ("spring.data.redis.port" , () -> REDIS_PORT );
88104 }
89105
90- @ Autowired
91- RqueueMessageEnqueuer enqueuer ;
92-
93- @ Autowired
94- TestListener listener ;
106+ @ Autowired RqueueMessageEnqueuer enqueuer ;
107+ @ Autowired TestListener listener ;
95108
96109 @ Test
97110 void enqueueIsReceivedByListener () throws Exception {
@@ -103,8 +116,7 @@ void enqueueIsReceivedByListener() throws Exception {
103116 .containsExactlyInAnyOrder ("payload-0" , "payload-1" , "payload-2" , "payload-3" , "payload-4" );
104117 }
105118
106- @ SpringBootApplication (
107- exclude = {DataRedisAutoConfiguration .class , DataRedisReactiveAutoConfiguration .class })
119+ @ SpringBootApplication
108120 static class TestApp {}
109121
110122 @ Component
0 commit comments