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");
                }
        }
        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 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 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");
            }
        }