1+ package redis .clients .jedis .mcf ;
2+
3+ import eu .rekawek .toxiproxy .Proxy ;
4+ import eu .rekawek .toxiproxy .ToxiproxyClient ;
5+ import eu .rekawek .toxiproxy .model .ToxicDirection ;
6+ import org .junit .jupiter .api .AfterAll ;
7+ import org .junit .jupiter .api .BeforeAll ;
8+ import org .junit .jupiter .api .BeforeEach ;
9+ import org .junit .jupiter .api .Test ;
10+ import org .junit .jupiter .api .Tag ;
11+ import redis .clients .jedis .DefaultJedisClientConfig ;
12+ import redis .clients .jedis .EndpointConfig ;
13+ import redis .clients .jedis .HostAndPort ;
14+ import redis .clients .jedis .HostAndPorts ;
15+ import redis .clients .jedis .JedisClientConfig ;
16+ import redis .clients .jedis .UnifiedJedis ;
17+
18+ import java .io .IOException ;
19+
20+ import static org .junit .jupiter .api .Assertions .*;
21+
22+ @ Tag ("failover" )
23+ public class EchoStrategyIntegrationTest {
24+
25+ private static final EndpointConfig endpoint = HostAndPorts .getRedisEndpoint ("redis-failover-1" );
26+ private static final HostAndPort proxyHostAndPort = endpoint .getHostAndPort ();
27+ private static final ToxiproxyClient tp = new ToxiproxyClient ("localhost" , 8474 );
28+ private static Proxy redisProxy ;
29+
30+ @ BeforeAll
31+ public static void setupProxy () throws IOException {
32+ if (tp .getProxyOrNull ("redis-health-test" ) != null ) {
33+ tp .getProxy ("redis-health-test" ).delete ();
34+ }
35+ redisProxy = tp .createProxy ("redis-health-test" , "0.0.0.0:29379" , "redis-failover-1:9379" );
36+ }
37+
38+ @ AfterAll
39+ public static void cleanupProxy () throws IOException {
40+ if (redisProxy != null ) {
41+ redisProxy .delete ();
42+ }
43+ }
44+
45+ @ BeforeEach
46+ public void resetProxy () throws IOException {
47+ redisProxy .enable ();
48+ redisProxy .toxics ().getAll ().forEach (toxic -> {
49+ try {
50+ toxic .remove ();
51+ } catch (IOException e ) {
52+ throw new RuntimeException (e );
53+ }
54+ });
55+ }
56+
57+ @ Test
58+ public void testEchoStrategyRecoversAfterDisconnect () throws Exception {
59+ JedisClientConfig config = DefaultJedisClientConfig .builder ().socketTimeoutMillis (1000 )
60+ .connectionTimeoutMillis (1000 ).build ();
61+
62+ UnifiedJedis jedis = new UnifiedJedis (proxyHostAndPort , config );
63+ jedis .ping ();
64+ EchoStrategy strategy = new EchoStrategy (proxyHostAndPort , config , 1000 , 500 );
65+
66+ // Initial health check should work
67+ HealthStatus initialStatus = strategy .doHealthCheck (proxyHostAndPort );
68+ assertEquals (HealthStatus .HEALTHY , initialStatus );
69+
70+ // Disable the proxy to simulate network failure
71+ redisProxy .disable ();
72+
73+ // Health check should now fail - this will expose the bug
74+ HealthStatus statusAfterDisable = strategy .doHealthCheck (proxyHostAndPort );
75+ assertEquals (HealthStatus .UNHEALTHY , statusAfterDisable );
76+
77+ // Re-enable proxy
78+ redisProxy .enable ();
79+ // Health check should recover
80+ HealthStatus statusAfterEnable = strategy .doHealthCheck (proxyHostAndPort );
81+ assertEquals (HealthStatus .HEALTHY , statusAfterEnable );
82+ }
83+
84+ @ Test
85+ public void testEchoStrategyWithConnectionTimeout () throws Exception {
86+ JedisClientConfig config = DefaultJedisClientConfig .builder ().socketTimeoutMillis (100 )
87+ .connectionTimeoutMillis (100 ).build ();
88+
89+ EchoStrategy strategy = new EchoStrategy (proxyHostAndPort , config , 1000 , 300 );
90+
91+ // Initial health check should work
92+ assertEquals (HealthStatus .HEALTHY , strategy .doHealthCheck (proxyHostAndPort ));
93+
94+ // Add latency toxic to simulate slow network
95+ redisProxy .toxics ().latency ("slow-connection" , ToxicDirection .DOWNSTREAM , 1000 );
96+
97+ // Health check should timeout and return unhealthy
98+ HealthStatus slowStatus = strategy .doHealthCheck (proxyHostAndPort );
99+ assertEquals (HealthStatus .UNHEALTHY , slowStatus , "Health check should fail with high latency" );
100+
101+ // Remove toxic
102+ redisProxy .toxics ().get ("slow-connection" ).remove ();
103+
104+ // Health check should recover
105+ HealthStatus recoveredStatus = strategy .doHealthCheck (proxyHostAndPort );
106+ assertEquals (HealthStatus .HEALTHY , recoveredStatus , "Health check should recover from high latency" );
107+ }
108+
109+ @ Test
110+ public void testConnectionDropDuringHealthCheck () throws Exception {
111+ JedisClientConfig config = DefaultJedisClientConfig .builder ().socketTimeoutMillis (2000 ).build ();
112+
113+ EchoStrategy strategy = new EchoStrategy (proxyHostAndPort , config , 1000 , 1500 );
114+
115+ // Initial health check
116+ assertEquals (HealthStatus .HEALTHY , strategy .doHealthCheck (proxyHostAndPort ));
117+
118+ // Simulate connection drop by limiting data transfer
119+ redisProxy .toxics ().limitData ("connection-drop" , ToxicDirection .UPSTREAM , 10 );
120+
121+ // This should fail due to connection issues
122+ HealthStatus droppedStatus = strategy .doHealthCheck (proxyHostAndPort );
123+ assertEquals (HealthStatus .UNHEALTHY , droppedStatus );
124+
125+ // Remove toxic
126+ redisProxy .toxics ().get ("connection-drop" ).remove ();
127+
128+ // Health check should recover
129+ HealthStatus afterRecovery = strategy .doHealthCheck (proxyHostAndPort );
130+ assertEquals (HealthStatus .HEALTHY , afterRecovery );
131+
132+ }
133+ }
0 commit comments