Inheritance: IConnectionPool, IDisposable
        public void Creating_client_does_not_get_socket_from_pool() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);

            // Act
            var client = new ClacksClient(pool);

            // Assert
            Assert.AreEqual(0, _factory.Sockets.Count);
        }
        public void Getting_sockets_in_parallel_will_create_a_new_socket() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);

            // Act
            var s1 = pool.GetSocket();
            var s2 = pool.GetSocket();

            // Assert
            Assert.AreEqual(2, _factory.Sockets.Count);
        }
        public void Getting_sockets_sequentially_will_not_create_a_new_socket() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);

            // Act
            var s1 = pool.GetSocket();
            s1.Dispose();
            var s2 = pool.GetSocket();

            // Assert
            Assert.AreEqual(1, _factory.Sockets.Count);
        }
        public void Disposing_client_returns_socket_to_pool() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);

            // Act
            var client = new ClacksClient(pool);
            client.Dispose();
            var s = pool.GetSocket();

            // Assert
            Assert.AreEqual(1, _factory.Sockets.Count);
        }
        public void Disposing_pool_socket_does_not_dispose_underlying_socket() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);

            // Act
            var s1 = pool.GetSocket();
            s1.Dispose();

            // Assert
            Assert.AreEqual(1, _factory.Sockets.Count);
            Assert.AreNotSame(_factory.Sockets[0], s1);
            Assert.AreEqual(0, _factory.Sockets[0].DisposeCalled);
        }
        public void Making_client_request_gets_socket_from_pool() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);
            var socket = new FakeSocket {
                ReceiveCallback = (buffer, size, offset) => {
                    var bytes = Encoding.ASCII.GetBytes("OK\r\n");
                    Array.Copy(bytes, buffer, bytes.Length);
                    return bytes.Length;
                }
            };
            _factory.Builder = () => socket;

            // Act
            var client = new ClacksClient(pool);
            client.Exec(Request.Create("HI"));

            // Assert
            Assert.AreEqual(1, _factory.Sockets.Count);
        }
        public void Getting_sockets_quickly_in_sequence_when_the_first_has_closed_will_return_closed_socket() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);

            // Act
            var s1 = pool.GetSocket();
            s1.Dispose();
            _factory.Sockets[0].Connected = false;
            var s2 = pool.GetSocket();

            // Assert
            Assert.AreEqual(1, _factory.Sockets.Count);
            Assert.IsFalse(s2.Connected);
        }
        public void Getting_sockets_slowly_in_sequence_when_the_first_has_closed_will_return_new_socket() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);
            pool.CheckInterval = TimeSpan.FromSeconds(1);

            // Act
            var s1 = pool.GetSocket();
            s1.Dispose();
            _factory.Sockets[0].Connected = false;
            Thread.Sleep(3000);
            var s2 = pool.GetSocket();

            // Assert
            Assert.AreEqual(2, _factory.Sockets.Count);
        }
        public void Reconnecting_client_retries_request_on_failure() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);
            var failSocket = new FakeSocket();
            failSocket.ReceiveCallback = (buffer, size, offset) => {
                failSocket.Connected = false;
                throw new SocketException(42);
            };
            var successSocket = new FakeSocket {
                ReceiveCallback = (buffer, size, offset) => {
                    var bytes = Encoding.ASCII.GetBytes("OK\r\n");
                    Array.Copy(bytes, buffer, bytes.Length);
                    return bytes.Length;
                }
            };
            _factory.Builder = () => _factory.Sockets.Any() ? successSocket : failSocket;

            // Act
            var client = new ClacksClient(pool);
            var response = client.Exec(Request.Create("HI"));

            // Assert
            Assert.AreEqual(2, _factory.Sockets.Count);
            Assert.AreEqual(1, failSocket.ReceiveCalled);
            Assert.AreEqual(1, successSocket.ReceiveCalled);
            Assert.AreEqual("OK", response.Status);
        }
        public void Client_recovers_if_connection_is_closed_by_remote_host() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);
            var failSocket = new FakeSocket();
            failSocket.ReceiveCallback = (buffer, size, offset) => {

                // This happens when the other side hangs up the phone while we are still reading data
                failSocket.Connected = true;
                throw new SocketException((int)SocketError.ConnectionReset);
            };
            var successSocket = new FakeSocket {
                ReceiveCallback = (buffer, size, offset) => {
                    var bytes = Encoding.ASCII.GetBytes("OK\r\n");
                    Array.Copy(bytes, buffer, bytes.Length);
                    return bytes.Length;
                }
            };
            _factory.Builder = () => _factory.Sockets.Any() ? successSocket : failSocket;

            // Act
            var client = new ClacksClient(pool);
            var response = client.Exec(Request.Create("HI"));

            // Assert
            Assert.AreEqual(2, _factory.Sockets.Count);
            Assert.AreEqual(1, failSocket.ReceiveCalled);
            Assert.AreEqual(1, successSocket.ReceiveCalled);
            Assert.AreEqual("OK", response.Status);
        }
        public void Non_reconnecting_client_only_issues_request_once_if_not_failing() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);
            var successSocket = new FakeSocket {
                ReceiveCallback = (buffer, size, offset) => {
                    var bytes = Encoding.ASCII.GetBytes("OK\r\n");
                    Array.Copy(bytes, buffer, bytes.Length);
                    return bytes.Length;
                }
            };
            _factory.Builder = () => successSocket;

            // Act
            var client = new ClacksClient(pool, attemptReconnect: false);
            var response = client.Exec(Request.Create("HI"));

            // Assert
            Assert.AreEqual(1, _factory.Sockets.Count);
            Assert.AreEqual(1, successSocket.ReceiveCalled);
            Assert.AreEqual("OK", response.Status);
        }
        public void Non_reconnecting_client_does_not_retry_failed_request() {

            // Arrange
            var pool = new ConnectionPool(_factory.Create);
            var failSocket = new FakeSocket();
            failSocket.ReceiveCallback = (buffer, size, offset) => {
                failSocket.Connected = false;
                throw new SocketException(42);
            };
            var successSocket = new FakeSocket {
                ReceiveCallback = (buffer, size, offset) => {
                    var bytes = Encoding.ASCII.GetBytes("OK\r\n");
                    Array.Copy(bytes, buffer, bytes.Length);
                    return bytes.Length;
                }
            };
            _factory.Builder = () => _factory.Sockets.Any() ? successSocket : failSocket;

            // Act
            var client = new ClacksClient(pool, attemptReconnect: false);
            try {
                client.Exec(Request.Create("HI"));
                Assert.Fail("call should not have succeeded");
            } catch { }

            // Assert
            Assert.AreEqual(1, _factory.Sockets.Count);
            Assert.AreEqual(1, failSocket.ReceiveCalled);
            Assert.AreEqual(0, successSocket.ReceiveCalled);
        }