TrackingConnectionPoolInitTest.java

package redis.clients.jedis.mcf;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import java.io.IOException;
import java.util.List;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;

import redis.clients.jedis.Connection;
import redis.clients.jedis.ConnectionTestHelper;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.PushConsumer;
import redis.clients.jedis.PushConsumerChainImpl;
import redis.clients.jedis.util.server.TcpMockServer;

public class TrackingConnectionPoolInitTest {

  private TcpMockServer mockServer;

  @BeforeEach
  public void setUp() throws IOException {
    mockServer = new TcpMockServer();
    mockServer.start();
  }

  @AfterEach
  public void tearDown() throws IOException {
    if (mockServer != null) {
      mockServer.stop();
    }
  }

  /**
   * Regression test for the double-initialization bug in {@link TrackingConnectionPool}.
   * <p>
   * Before the fix, a {@link Connection} obtained through a {@code TrackingConnectionPool} was
   * initialized twice.
   * </p>
   */
  @Test
  public void pooledConnectionRegistersPubSubConsumerExactlyOnce() {
    DefaultJedisClientConfig config = DefaultJedisClientConfig.builder().resp3().build();
    HostAndPort hostAndPort = new HostAndPort("localhost", mockServer.getPort());

    try (
        TrackingConnectionPool pool = TrackingConnectionPool.builder().hostAndPort(hostAndPort)
            .clientConfig(config).build();
        Connection conn = pool.getResource()) {

      List<PushConsumer> consumers = ConnectionTestHelper.getPushConsumers(conn);
      // contains(...) is an exact-length matcher: two copies of PUBSUB_CONSUMER would fail.
      assertThat(consumers, contains(is(PushConsumerChainImpl.PUBSUB_CONSUMER)));
    }
  }

  /**
   * Verifies that a {@link Connection} borrowed from a {@code TrackingConnectionPool} is
   * initialized exactly once.
   */
  @Test
  public void pooledConnectionInitializedExactlyOnce() {
    DefaultJedisClientConfig config = DefaultJedisClientConfig.builder().resp3().build();
    HostAndPort hostAndPort = new HostAndPort("localhost", mockServer.getPort());

    // Mock the constructor so the borrow runs without opening a socket.
    try (MockedConstruction<Connection> mocked = mockConstruction(Connection.class)) {
      try (TrackingConnectionPool pool = TrackingConnectionPool.builder().hostAndPort(hostAndPort)
          .clientConfig(config).build()) {

        pool.getResource();

        assertEquals(1, mocked.constructed().size());
        Connection pooledConnection = mocked.constructed().get(0);
        verify(pooledConnection, times(1)).initializeFromClientConfig();
      }
    }
  }
}