public void AuthenticationFailed()
        {
            var sentMutex = new ManualResetEventSlim(false);

            using (var serverStub = new ServerMock())
            {
                IMemcacheRequest authenticationRequest = null;
                // a token that fails
                var authenticatorTokenFailing = new Moq.Mock<IAuthenticationToken>();
                authenticatorTokenFailing
                    .Setup(t => t.StepAuthenticate(Moq.It.IsAny<TimeSpan>(), out authenticationRequest))
                    .Returns(Status.TemporaryFailure);
                // a token that works
                var authenticatorTokenOk = new Moq.Mock<IAuthenticationToken>();
                authenticatorTokenOk
                    .Setup(t => t.StepAuthenticate(Moq.It.IsAny<TimeSpan>(), out authenticationRequest))
                    .Returns(Status.NoError);
                // an authenticator that returns one failing token followed by working tokens
                bool alreadyFailed = false;
                var authenticator = new Moq.Mock<IMemcacheAuthenticator>();
                authenticator
                    .Setup(auth => auth.CreateToken())
                    .Returns(() =>
                        {
                            if (alreadyFailed)
                                return authenticatorTokenOk.Object;
                            alreadyFailed = true;
                            return authenticatorTokenFailing.Object;
                        });

                // setup the request to send
                bool requestFailed = false;
                bool requestAchieved = false;
                var request = new Moq.Mock<IMemcacheRequest>();
                request
                    .Setup(r => r.Fail())
                    .Callback(() =>
                        {
                            requestFailed = true;
                            sentMutex.Set();
                        });
                request
                    .Setup(r => r.HandleResponse(
                        Moq.It.Is<Headers.MemcacheResponseHeader>(h => h.Status == Status.NoError),
                        Moq.It.IsAny<byte[]>(),
                        Moq.It.IsAny<byte[]>(),
                        Moq.It.IsAny<byte[]>()))
                    .Callback(() =>
                        {
                            requestAchieved = true;
                            sentMutex.Set();
                        });
                var queryBuffer = new byte[MemcacheRequestHeader.Size];
                new MemcacheRequestHeader().ToData(queryBuffer);
                request
                    .Setup(r => r.GetQueryBuffer())
                    .Returns(queryBuffer);

                IMemcacheTransport transportToWork = null;
                var transportToFail = new MemcacheTransport(
                    serverStub.ListenEndPoint,
                    new MemcacheClientConfiguration
                    {
                        SocketTimeout = TimeSpan.Zero,
                        Authenticator = authenticator.Object,
                    },
                    _ => { },
                    t =>
                    {
                        Interlocked.Exchange(ref transportToWork, t);
                    },
                    false,
                    () => false);
                new MemcacheResponseHeader
                    {
                        Status = Status.NoError,
                        Opcode = Opcode.Get,
                    }.ToData(serverStub.ResponseHeader);

                Exception raised = null;
                transportToFail.TransportError += e =>
                        // when the transport fails collect the exception
                        Interlocked.Exchange(ref raised, e);
                var sent = transportToFail.TrySend(request.Object);

                Assert.IsFalse(sent, "The request send should fail");
                Assert.IsNotNull(raised, "The authentication should have failed");

                // wait for reconnection to happen (should be done in a instant timer)
                Assert.That(ref transportToWork, (!Is.Null).After(1000, 10), "The working transport should have been set");

                sent = transportToWork.TrySend(request.Object);
                Assert.IsTrue(sent, "The request should have been sent");
                var received = sentMutex.Wait(TimeSpan.FromMinutes(5));
                Assert.IsTrue(received, "The response should have been received");
                Assert.IsFalse(requestFailed, "The request should not have failed");
                Assert.IsTrue(requestAchieved, "The request should have achieved");
            }
        }
        public void MemcacheSocketTest([Values(0, 2, 10)]int maxByteSentByServer)
        {
            using (var serverMock = new ServerMock())
            {
                var endPoint = serverMock.ListenEndPoint;
                serverMock.MaxSent = maxByteSentByServer;

                // random header
                var requestHeader = new MemcacheRequestHeader
                {
                    Cas = 1,
                    DataType = 2,
                    ExtraLength = 5,
                    KeyLength = 3,
                    Opaque = 42,
                    Opcode = Opcode.Increment,
                    TotalBodyLength = 12,
                };
                // body with the size defined in the header
                var requestBody = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

                // build the request buffer
                var requestBuffer = new byte[MemcacheRequestHeader.Size + requestHeader.TotalBodyLength];
                requestHeader.ToData(requestBuffer);
                Array.Copy(requestBody, 0, requestBuffer, MemcacheRequestHeader.Size, requestHeader.TotalBodyLength);

                // build the response header
                var responseHeader = new MemcacheResponseHeader
                {
                    Cas = 8,
                    DataType = 12,
                    ExtraLength = 3,
                    KeyLength = 0,
                    // must be the same or it will crash : TODO add a test to ensure we detect this fail
                    Opaque = requestHeader.Opaque,
                    Status = Status.UnknownCommand,
                    Opcode = Opcode.Prepend,
                    TotalBodyLength = 15,
                };
                // body with the size defined in the header
                var responseExtra = new byte[] { 1, 2, 3 };
                var responseMessage = new byte[] { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

                // build the request buffer
                var responseBody = new byte[responseHeader.TotalBodyLength];
                Array.Copy(responseExtra, 0, responseBody, 0, responseHeader.ExtraLength);
                Array.Copy(responseMessage, 0, responseBody, responseHeader.ExtraLength, responseHeader.TotalBodyLength - responseHeader.ExtraLength);

                // set the things to answer to the server
                serverMock.ResponseBody = responseBody;
                responseHeader.ToData(serverMock.ResponseHeader);

                var request = new RequestMock
                {
                    QueryBuffer = requestBuffer,
                    RequestId = requestHeader.Opaque
                    /*ResponseHeader = responseHeader,
                    Message = responseMessage,
                    Extra = responseExtra,*/
                };

                using (var transport = new MemcacheTransport(endPoint, new MemcacheClientConfiguration(), _ => { }, _ => { }, false, null))
                {
                    Assert.IsTrue(transport.TrySend(request));

                    Assert.IsTrue(request.Mutex.Wait(TimeSpan.FromSeconds(10)), "The request has not been completed on less than 10 sec");

                    // and now, assert that we sent what we had to send and we received what the server sent
                    Assert.AreEqual(requestHeader, serverMock.LastReceivedHeader, "Sent header differ from header received by the server");
                    CollectionAssert.AreEqual(requestBody, serverMock.LastReceivedBody, "Sent body is different than received by the server");

                    Assert.AreEqual(responseHeader, request.ResponseHeader, "Received header differ from header sent by the server");
                    CollectionAssert.AreEqual(responseExtra, request.Extra, "Received extra is different than sent by the server");
                    CollectionAssert.AreEqual(responseMessage, request.Message, "Received message is different than sent by the server");
                }
            }
        }
        public void QueueFullTest()
        {
            var config = new Configuration.MemcacheClientConfiguration
            {
                QueueLength = 1,
            };
            int transportAvailablized = 0;

            using (var serverMock = new ServerMock())
            using (var transportToTest = new MemcacheTransport(serverMock.ListenEndPoint, config, t => { }, t => Interlocked.Increment(ref transportAvailablized), false, null))
            {
                var requestHeader = new MemcacheResponseHeader
                {
                    Cas = 1,
                    DataType = 2,
                    ExtraLength = 4,
                    KeyLength = 0,
                    Opaque = 42,
                    Opcode = Opcode.Get,
                    Status = Headers.Status.KeyNotFound,
                    TotalBodyLength = 4,
                };
                requestHeader.ToData(serverMock.ResponseHeader);
                serverMock.ResponseBody = new byte[4];

                serverMock.ReceiveMutex = new ManualResetEventSlim();
                var clientMutex = new ManualResetEventSlim();
                var request1 = new GetRequest
                {
                    RequestId = (uint)42,
                    Key = "Hello, world".Select(c => (byte)c).ToArray(),
                    RequestOpcode = Opcode.Get,
                    CallBack = (r, v) => clientMutex.Set(),
                };
                var request2 = new GetRequest
                {
                    RequestId = (uint)42,
                    Key = "Hello, world".Select(c => (byte)c).ToArray(),
                    RequestOpcode = Opcode.Get,
                    CallBack = (r, v) => { },
                };

                // we sent a first request and let the server wait before respond
                Assert.IsTrue(transportToTest.TrySend(request1), "The first request failed to be sent");
                Assert.That(() => transportAvailablized, Is.EqualTo(2).After(1000, 50));
                // we check that the queue is full, and the transport fail to send a new request
                Assert.IsFalse(transportToTest.TrySend(request2), "The second request should not have been sent");
                // unblocks both server response and callback from client
                serverMock.ReceiveMutex.Set();
                Assert.IsTrue(clientMutex.Wait(1000), "The response callback has not been triggered for the first request");
                // make sure that we triggered the transport available after the queue is not full anymore
                Assert.That(() => transportAvailablized, Is.EqualTo(3).After(1000, 50));
                // checks if we can send a new request since the queue is not full anymore
                Assert.IsTrue(transportToTest.TrySend(request2), "The third request failed to be sent");
                Assert.That(() => transportAvailablized, Is.EqualTo(4).After(1000, 50));
            }
        }
        public void AuthenticationTest()
        {
            var config = new Configuration.MemcacheClientConfiguration
            {
                Authenticator = Configuration.MemcacheClientConfiguration.SaslPlainAuthenticatorFactory(string.Empty, "NoLogin", "NoPass"),
            };

            using (var serverMock = new ServerMock())
            using (var transport = new MemcacheTransport(serverMock.ListenEndPoint, config,
                t => { },
                t => { }, false, null))
            {
                var mutex = new ManualResetEventSlim();
                new MemcacheResponseHeader
                {
                    Opaque = 0,
                    Opcode = Opcode.StartAuth,
                    Status = Status.NoError,
                }.ToData(serverMock.ResponseHeader);

                Status responseStatus = Status.InternalError;
                Assert.IsTrue(transport.TrySend(new NoOpRequest { RequestId = 0, Callback = h => { mutex.Set(); responseStatus = h.Status; } })
                    , "Unable to send a request on authenticated transport");
                Assert.IsTrue(mutex.Wait(1000), "No response retrived on authenticated transport");
                Assert.AreEqual(Status.NoError, responseStatus, "The response returned on error");
            }
        }