package de.idealo.logback.appender.jedisclient; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Optional; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.OngoingStubbing; import de.idealo.logback.appender.jedisclient.JedisClient; import de.idealo.logback.appender.jedisclient.JedisClientProvider; import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; import redis.clients.jedis.exceptions.JedisException; public class JedisClientTest { private static final int MAX_INIT_RETRIES = 3; private static final long INIT_RETRIES_INTERVAL_MILLIS = 100L; @Mock private JedisClientProvider clientProvider; @Mock private Jedis jedis; @Mock private Pipeline pipeline; @Before public void init() { MockitoAnnotations.initMocks(this); when(clientProvider.getJedisClient()).thenReturn(Optional.of(jedis)); when(jedis.pipelined()).thenReturn(pipeline); } @Test(expected = IllegalArgumentException.class) public void exception_on_invalid_max_tries_value() { try (JedisClient jedisClient = new JedisClient(clientProvider, 0, INIT_RETRIES_INTERVAL_MILLIS)) { } } @Test public void valid_client_on_last_retry() throws InterruptedException { withClientOnTryNumber(MAX_INIT_RETRIES); try (JedisClient jedisClient = new JedisClient(clientProvider, MAX_INIT_RETRIES, INIT_RETRIES_INTERVAL_MILLIS)) { TimeUnit.MILLISECONDS.sleep(INIT_RETRIES_INTERVAL_MILLIS * (MAX_INIT_RETRIES + 1)); verify(clientProvider, times(MAX_INIT_RETRIES)).getJedisClient(); assertEquals(pipeline, jedisClient.getPipeline().orElse(null)); } } @Test public void no_client_on_exceeded_retries() throws InterruptedException { int clientOnTry = MAX_INIT_RETRIES + 1; withClientOnTryNumber(clientOnTry); try (JedisClient jedisClient = new JedisClient(clientProvider, MAX_INIT_RETRIES, INIT_RETRIES_INTERVAL_MILLIS)) { TimeUnit.MILLISECONDS.sleep(INIT_RETRIES_INTERVAL_MILLIS * (MAX_INIT_RETRIES + 1)); verify(clientProvider, times(MAX_INIT_RETRIES)).getJedisClient(); assertEquals(null, jedisClient.getPipeline().orElse(null)); } } @Test public void reconnect_succeeds_after_init_failed() throws InterruptedException { int clientOnTry = MAX_INIT_RETRIES + 1; withClientOnTryNumber(clientOnTry); try (JedisClient jedisClient = new JedisClient(clientProvider, MAX_INIT_RETRIES, INIT_RETRIES_INTERVAL_MILLIS)) { TimeUnit.MILLISECONDS.sleep(INIT_RETRIES_INTERVAL_MILLIS * (MAX_INIT_RETRIES + 1)); verify(clientProvider, times(MAX_INIT_RETRIES)).getJedisClient(); assertEquals(null, jedisClient.getPipeline().orElse(null)); jedisClient.reconnect(); verify(clientProvider, times(MAX_INIT_RETRIES + 1)).getJedisClient(); assertEquals(pipeline, jedisClient.getPipeline().orElse(null)); } } @Test public void no_pipeline_on_failed_reconnect() throws InterruptedException { when(clientProvider.getJedisClient()).thenReturn(Optional.of(jedis)).thenReturn(Optional.empty()); doThrow(new JedisException("")).when(jedis).close(); try (JedisClient jedisClient = new JedisClient(clientProvider, MAX_INIT_RETRIES, INIT_RETRIES_INTERVAL_MILLIS)) { verify(clientProvider, times(1)).getJedisClient(); assertEquals(pipeline, jedisClient.getPipeline().orElse(null)); jedisClient.reconnect(); verify(clientProvider, times(2)).getJedisClient(); assertEquals(null, jedisClient.getPipeline().orElse(null)); } } @Test public void new_pipeline_on_successful_reconnect() throws InterruptedException { Jedis newJedis = mock(Jedis.class); Pipeline newPipeline = mock(Pipeline.class); when(newJedis.pipelined()).thenReturn(newPipeline); when(clientProvider.getJedisClient()).thenReturn(Optional.of(jedis)).thenReturn(Optional.of(newJedis)); try (JedisClient jedisClient = new JedisClient(clientProvider, MAX_INIT_RETRIES, INIT_RETRIES_INTERVAL_MILLIS)) { verify(clientProvider, times(1)).getJedisClient(); assertEquals(pipeline, jedisClient.getPipeline().orElse(null)); jedisClient.reconnect(); verify(clientProvider, times(2)).getJedisClient(); assertEquals(newPipeline, jedisClient.getPipeline().orElse(null)); } } @Test public void no_reconnect_during_initialization() throws InterruptedException { final Jedis firstInitTryResult = mock(Jedis.class); final Jedis reconnectResult = mock(Jedis.class); final Pipeline reconnectPipeline = mock(Pipeline.class); when(reconnectResult.pipelined()).thenReturn(reconnectPipeline); when(clientProvider.getJedisClient()).thenReturn(Optional.empty()) .thenReturn(Optional.of(firstInitTryResult)) .thenReturn(Optional.of(reconnectResult)); try (JedisClient jedisClient = new JedisClient(clientProvider, MAX_INIT_RETRIES, INIT_RETRIES_INTERVAL_MILLIS)) { jedisClient.reconnect(); // won't work, client is initializing assertEquals(null, jedisClient.getPipeline().orElse(null)); TimeUnit.MILLISECONDS.sleep(INIT_RETRIES_INTERVAL_MILLIS * 2); // initialization resulted in firstInitTryResult jedisClient.reconnect(); verify(firstInitTryResult).close(); assertEquals(reconnectPipeline, jedisClient.getPipeline().orElse(null)); } } @Test public void stop_initialization_on_shutdown() throws InterruptedException { final int initRetryMillis = 50; when(clientProvider.getJedisClient()).thenReturn(Optional.empty()); try (JedisClient jedisClient = new JedisClient(clientProvider, Integer.MAX_VALUE, initRetryMillis)) { assertTrue(jedisClient.isInitializing()); TimeUnit.MILLISECONDS.sleep(3 * initRetryMillis); assertTrue(jedisClient.isInitializing()); jedisClient.close(); TimeUnit.MILLISECONDS.sleep(initRetryMillis); assertFalse(jedisClient.isInitializing()); } } private void withClientOnTryNumber(int tries) { OngoingStubbing<Optional<Jedis>> stub = when(clientProvider.getJedisClient()); for (int i = 1; i < tries; i++) { stub = stub.thenReturn(Optional.empty()); } stub.thenReturn(Optional.of(jedis)); } }