public void Throw_SocketException_when_connection_is_closed() {
            
            // Arrange
            var fakesocket = new FakeSocket { Connected = false, ReceiveCallback = (buffer, offset, length) => { throw new SocketException(); } };
            var callbacks = new PoolSocketCallbacks();
            var poolsocket = new PoolSocket(fakesocket, callbacks.Reclaim);

            // Act
            try {
                poolsocket.Receive(new byte[0], 0, 0);
                Assert.Fail("didn't throw");
            }
            catch (SocketException ex) {

                // Assert
                Assert.AreEqual((int)SocketError.NotConnected, ex.ErrorCode);
            }            
        }
        public void Can_read_single_response_with_data_from_single_receive() {

            // Arrange
            var data = "blahblahblah";
            var fakeSocket = new FakeSocket {
                ReceiveCallback = Responses(
                    string.Format("OK FOO {0}\r\n{1}\r\n", data.Length, data)
                )
            };

            // Act
            var receiver = new ResponseReceiver(fakeSocket);
            receiver.Reset(new Request("OK").ExpectData("OK"));
            var response = receiver.GetResponse();
            Assert.AreEqual("OK", response.Status);

            // Assert
            Assert.AreEqual(new[] { "FOO", data.Length.ToString() }, response.Arguments);
            Assert.AreEqual(data, Encoding.ASCII.GetString(response.Data));
        }
        public void On_failure_PoolSocket_and_underlying_socket_are_disposed_and_reclaimed() {

            // Arrange
            var fakesocket = new FakeSocket { ReceiveCallback = (buffer, offset, length) => { throw new SocketException(); } };
            var callbacks = new PoolSocketCallbacks();
            var poolsocket = new PoolSocket(fakesocket, callbacks.Reclaim);

            // Act
            try {
                poolsocket.Receive(new byte[0], 0, 0);
                Assert.Fail("didn't throw");
            } catch(SocketException) { }

            // Assert
            Assert.AreEqual(1, fakesocket.ReceiveCalled, "send called wrong number of times");
            Assert.IsTrue(fakesocket.IsDisposed, "underlying socket wasn't disposed");
            Assert.IsTrue(poolsocket.IsDisposed, "pool socket wasn't disposed");
            Assert.AreEqual(1, callbacks.ReclaimCalled, "reclaim was called wrong number of times");
            Assert.AreSame(fakesocket, callbacks.ReclaimedSocket, "underlying socket wasn't the one reclaimed");
        }
        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 Can_read_single_response_with_many_data_chunks() {

            // Arrange
            var dataChunks = Enumerable.Repeat("a123456789", 15).ToArray();
            var data = string.Concat(dataChunks);
            dataChunks[dataChunks.Length - 1] = dataChunks[dataChunks.Length - 1] + "\r\n";
            var fakeSocket = new FakeSocket {
                ReceiveCallback = Responses(
                    new[] {string.Format("OK FOO {0}\r\n", data.Length)}
                        .Concat(dataChunks)
                        .ToArray()
                )
            };

            // Act
            var receiver = new ResponseReceiver(fakeSocket);
            receiver.Reset(new Request("OK").ExpectData("OK"));
            var response = receiver.GetResponse();
            Assert.AreEqual("OK", response.Status);

            // Assert
            Assert.AreEqual(new[] { "FOO", data.Length.ToString() }, response.Arguments);
            Assert.AreEqual(data, Encoding.ASCII.GetString(response.Data));
        }
        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);
        }