describe("Memcache", function () {
    
    it("require", function () {
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        expect(Memcache).toBeTruthy();
    });


    it("get - simple value in 1 packet", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            e = result;
            expect(result.length).toBe(4);
            expect(result.toString()).toBe('test');
        });
        
        runs(function () {
            socketMock.cb(new Buffer('VALUE testKey 0 4\r\ntest\r\nEND\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(1);
        });
    });
    
    
    it("get - simple value in 2 packets", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            e = result;
            expect(result.length).toBe(4);
            expect(result.toString()).toBe('test');
        });
        
        runs(function () {
            socketMock.cb(new Buffer('VALUE testKey 0 4\r\n'));
            socketMock.cb(new Buffer('test\r\nEND\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(1);
        });
    });
    
    
    it("get - simple value in 3 packets", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            e = result;
            expect(result.length).toBe(4);
            expect(result.toString()).toBe('test');
        });
        
        runs(function () {
            socketMock.cb(new Buffer('VALUE testKey 0 4\r\n'));
            socketMock.cb(new Buffer('test'));
            socketMock.cb(new Buffer('\r\nEND\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(1);
        });
        
        
    });
    
    it("get - simple value in 3 packets", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            e = result;
            expect(result.length).toBe(4);
            expect(result.toString()).toBe('test');
        });
        
        runs(function () {
            socketMock.cb(new Buffer('VALUE testKey 0 4\r\n'));
            socketMock.cb(new Buffer('test\r\nE'));
            socketMock.cb(new Buffer('ND\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(1);
        });
        
        
    });
    
    
    
    it("get - cache miss", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            expect(result).toBe(null);
            e = true;
        });
        
        runs(function () {
            socketMock.cb(new Buffer('END\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(2);
        });
        
        
    });
    
    
    // "CLIENT_ERROR <error>\r\n"
    // means some sort of client error in the input line, i.e. the input
    // doesn't conform to the protocol in some way. <error> is a
    // human-readable error string.
    it("get - client error", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        var spyConsole = spyOn(console, 'warn').andCallThrough();
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            expect(result).toBe(null);
            e = true;
        });
        
        runs(function () {
            socketMock.cb(new Buffer('CLIENT_ERROR <error>\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(2);
            expect(spyConsole.callCount).toEqual(1);
            expect(spyConsole).toHaveBeenCalledWith(
                'MEMCACHE/getChunk ERROR:',
                'get testKey\r\n',
                'CLIENT_ERROR <error>\r\n'
            );
        });
        
        
    });
    
    
    
    // "SERVER_ERROR <error>\r\n"
    // means some sort of server error prevents the server from carrying
    // out the command. <error> is a human-readable error string. In cases
    // of severe server errors, which make it impossible to continue
    // serving the client (this shouldn't normally happen), the server will
    // close the connection after sending the error line. This is the only
    // case in which the server closes a connection to a client.
    it("get - server error", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        var spyConsole = spyOn(console, 'warn').andCallThrough();
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            expect(result).toBe(null);
            e = true;
        });
        
        runs(function () {
            socketMock.cb(new Buffer('SERVER_ERROR <error>\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(2);
            expect(spyConsole.callCount).toEqual(1);
            expect(spyConsole).toHaveBeenCalledWith(
                'MEMCACHE/getChunk ERROR:',
                'get testKey\r\n',
                'SERVER_ERROR <error>\r\n'
            );
        });
    });
    
    
    
    // means the client sent a nonexistent command name.
    it("get - error", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        var spyConsole = spyOn(console, 'warn').andCallThrough();
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            expect(result).toBe(null);
            e = true;
        });
        
        runs(function () {
            socketMock.cb(new Buffer('ERROR\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(2);
            expect(spyConsole.callCount).toEqual(1);
            expect(spyConsole).toHaveBeenCalledWith(
                'MEMCACHE/getChunk ERROR:',
                'get testKey\r\n',
                'ERROR\r\n'
            );
        });
    });
    
    
    
    
    // means the client sent a nonexistent command name.
    it("get - unsupported response", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(13);
                expect(dataBuff.toString()).toEqual('get testKey\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        var spyConsole = spyOn(console, 'warn').andCallThrough();
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.get('testKey', function (result) {
            expect(result).toBe(null);
            e = true;
        });
        
        runs(function () {
            socketMock.cb(new Buffer('some unsupported response'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(2);
            expect(spyConsole.callCount).toEqual(1);
            expect(spyConsole).toHaveBeenCalledWith(
                'MEMCACHE/getChunk ERROR:',
                'get testKey\r\n',
                'some unsupported response'
            );
        });
    });
    
    
    
    
    it("add - simple value in 1 packet -> stored", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(26);
                expect(dataBuff.toString()).toEqual('add testKey 0 30 4\r\ntest\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.add('testKey', 'test', function (result) {
            e = result;
            expect(result.length).toBe(Memcache.STORED.length);
            expect(result.toString()).toBe(Memcache.STORED);
        }, 30, true);
        
        runs(function () {
            socketMock.cb(new Buffer('STORED\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(1);
        });
    });
    
    
    it("add - simple value in 1 packet -> not stored", function () {
        
        // socket mock
        var socketMock = {
            cb: null,
            _socket: {
                removeAllListeners: function () {}
            },
            on: function (event, cb) {
                expect(event).toEqual('data');
                this.cb = cb;
            },
            write: function (dataBuff) {
                expect(Buffer.isBuffer(dataBuff)).toBeTruthy();
                expect(dataBuff.toString().length).toEqual(26);
                expect(dataBuff.toString()).toEqual('add testKey 0 30 4\r\ntest\r\n');
            }
        };
        var spyRemoveAllListeners = spyOn(socketMock._socket, 'removeAllListeners').andCallThrough();
        var spySocketMockOn = spyOn(socketMock, 'on').andCallThrough();
        
        
        // connectionpool mock
        var connectionPoolMock = {
            getSocket: function (cb) {
                cb(socketMock);
            },
            givebackSocket: function (socket) {}
        };
        var spyGetSocket = spyOn(connectionPoolMock, 'getSocket').andCallThrough();
        var spyGivebackSocket = spyOn(connectionPoolMock, 'givebackSocket').andCallThrough();
        
        
        
        // test
        var Memcache = require('../../lib/cache/Memcache.js').Memcache;
        var mc = new Memcache(connectionPoolMock);
        var e = null;
        
        mc.add('testKey', 'test', function (result) {
            e = result;
            expect(result.length).toBe(Memcache.NOT_STORED.length);
            expect(result.toString()).toBe(Memcache.NOT_STORED);
        }, 30, true);
        
        runs(function () {
            socketMock.cb(new Buffer('NOT_STORED\r\n'));
        });
        
        waitsFor(function () {return e;}, "memcache result should arrive", 100);
        
        runs(function () {
            expect(spySocketMockOn.callCount).toEqual(1);
            expect(spyGetSocket.callCount).toEqual(1);
            expect(spyGivebackSocket.callCount).toEqual(1);
            expect(spyRemoveAllListeners.callCount).toEqual(1);
        });
    });

});
