text: const LimitdServer = require('..').Server;
  const LimitdClient = require('limitd-client');
  
  const assert   = require('chai').assert;
  const expect   = require('chai').expect;
  const rimraf   = require('rimraf');
  const path     = require('path');
  const async    = require('async');
  const _        = require('lodash');
  const MockDate = require('mockdate');
  
  var client;
  describe('limitd server', function () {
    var db_file = path.join(__dirname, 'dbs', 'server.tests.db');
  
    try{
      rimraf.sync(db_file);
    } catch(err){}
  
    const db_options = { db: db_file };
  
    var server;
  
    before(function (done) {
      server = new LimitdServer(_.extend(db_options, require('./fixture')));
  
      server.start(function (err, address) {
        if (err) return done(err);
        client = new LimitdClient(`limitd://localhost:${address.port}`);
        client.once('connect', done);
      });
    });
  
    after(function (done) {
      server.stop(done);
    });
  
    afterEach(function () {
      MockDate.reset();
    });
  
    describe('TAKE', function () {
      it('should work with a simple request', function (done) {
        var now = 1425920267;
        MockDate.set(now * 1000);
        client.take('ip', '211.123.12.12', function (err, response) {
          if (err) return done(err);
          assert.ok(response.conformant);
          assert.equal(response.remaining, 9);
          assert.equal(response.reset, now + 1);
          assert.equal(response.limit, 10);
          done();
        });
      });
  
      it('should work with a fixed bucket', function (done) {
        async.map(_.range(0, 3), function (i, cb) {
          client.take('wrong_password', 'tito', cb);
        }, function (err, results) {
          if (err) return done(err);
          assert.ok(results.every(function (r) {
            return r.conformant;
          }));
          client.take('wrong_password', 'tito', function (err, response) {
            assert.notOk(response.conformant);
            done();
          });
        });
      });
  
      it('should work with an override on a fixed bucket', function (done) {
        async.map(_.range(2), function (i, cb) {
          client.take('wrong_password', 'dudu', cb);
        }, function (err, results) {
          if (err) return done(err);
          assert.ok(results.every(function (r) {
            return r.conformant;
          }));
          client.take('wrong_password', 'dudu', function (err, response) {
            assert.notOk(response.conformant);
            done();
          });
        });
      });
  
      it('should return false when traffic is not conformant', function (done) {
        async.map(_.range(0, 10), function (i, cb) {
          client.take('ip', '211.123.12.24', cb);
        }, function (err, responses) {
          if (err) return done(err);
          assert.ok(responses.every(function (r) { return r.conformant; }));
          client.take('ip', '211.123.12.24', function (err, response) {
            assert.notOk(response.conformant);
            done();
          });
        });
      });
  
      it('should be conformant if an override allows it', function (done) {
        async.each(_.range(0, 10), function (i, cb) {
          client.take('ip', '127.0.0.1', cb);
        }, function (err) {
          if (err) return done(err);
          client.take('ip', '127.0.0.1', function (err, response) {
            assert.ok(response.conformant);
            done();
          });
        });
      });
  
      it('should be conformant if an override with regex allows it', function (done) {
        async.each(_.range(0, 10), function (i, cb) {
          client.take('ip', '127.4.4.4', cb);
        }, function (err) {
          if (err) return done(err);
          client.take('ip', '127.4.4.4', function (err, response) {
            assert.ok(response.conformant);
            done();
          });
        });
      });
  
      it('can expire an override', function (done) {
        async.each(_.range(0, 10), function (i, cb) {
          client.take('ip', '10.0.0.123', cb);
        }, function (err) {
          if (err) return done(err);
          client.take('ip', '10.0.0.123', function (err, response) {
            assert.notOk(response.conformant);
            done();
          });
        });
      });
  
      it('should use seconds ceiling for next reset', function (done) {
        // it takes ~1790 msec to fill the bucket with this test
        var now = 1425920267;
        MockDate.set(now * 1000);
        var requests = _.map(_.range(0, 9), function(){
          return function (cb) {
            client.take('ip', '211.123.12.36', cb);
          };
        });
        async.series(requests, function (err, results) {
          if (err) return done(err);
          var lastResponse = results[results.length -1];
          assert.ok(lastResponse.conformant);
          assert.equal(lastResponse.remaining, 1);
          assert.equal(lastResponse.reset, now + 2);
          assert.equal(lastResponse.limit, 10);
          done();
        });
      });
  
      it('should set reset to UNIX timestamp regardless of period', function(done){
        var now = 1425920267;
        MockDate.set(now * 1000);
        client.take('ip', '10.0.0.1', function (err, response) {
          if (err) { return done(err); }
          assert.ok(response.conformant);
          assert.equal(response.remaining, 0);
          assert.equal(response.reset, now + 1800);
          assert.equal(response.limit, 1);
          done();
        });
      });
  
      it('should work for unlimited', function (done) {
        var now = 1425920267;
        MockDate.set(now * 1000);
        client.take('ip', '0.0.0.0', function (err, response) {
          if (err) return done(err);
          assert.ok(response.conformant);
          assert.equal(response.remaining, 100);
          assert.equal(response.reset, now);
          assert.equal(response.limit, 100);
          done();
        });
      });
  
    });
  
    describe('GET', function () {
      it('should work with a simple request', function (done) {
        var now = 1425920267;
        MockDate.set(now * 1000);
  
        client.get('ip', '211.123.2.213', function (err, response) {
          if (err) return done(err);
          assert.equal(response.remaining, 10);
          assert.equal(response.reset, now);
          assert.equal(response.limit, 10);
          done();
        });
      });
    });
  
    describe('WAIT', function () {
      it('should work with a simple request', function (done) {
        var now = 1425920267;
        MockDate.set(now * 1000);
        client.wait('ip', '211.76.23.4', function (err, response) {
          if (err) return done(err);
          assert.ok(response.conformant);
          assert.notOk(response.delayed);
  
  
          assert.equal(response.remaining, 9);
          assert.equal(response.reset, now + 1);
  
          done();
        });
      });
  
  
      it('should be delayed when traffic is non conformant', function (done) {
        client.take('ip', '211.76.23.5', 10, function (err) {
          if (err) return done(err);
          var waitingSince = Date.now();
          client.wait('ip', '211.76.23.5', 3, function (err, response) {
            var waited = Date.now() - waitingSince;
            assert.ok(response.conformant);
            assert.ok(response.delayed);
            expect(waited).to.be.closeTo(600, 100);
            done();
          });
        });
      });
  
    });
  
    it('should fail when the bucket type doesnt exist', function (done) {
      client.take('blabla', '211.123.12.12', function (err) {
        assert.equal(err.message, 'Invalid bucket type');
        done();
      });
    });
  
    it('should autoremove unused buckets', function (done) {
      client.take('ip', '211.45.66.1', function (err) {
        if (err) return done(err);
        setTimeout(function () {
          server._db._types['ip'].db.get('211.45.66.1', function (err, result) {
            assert.isUndefined(result);
            done();
          });
        }, 2200);
      });
    });
  
  
    describe('PUT', function () {
      it('should restore the bucket when reseting', function (done) {
        client.take('ip', '211.123.12.123', function (err) {
          if (err) return done(err);
          client.put('ip', '211.123.12.123', function (err) {
            if (err) return done(err);
            client.take('ip', '211.123.12.123', function (err, response) {
              if (err) return done(err);
              assert.equal(response.remaining, 9);
              done();
            });
          });
        });
      });
  
      it('should be able to reset without callback', function (done) {
        client.take('ip', '211.123.5.12', function (err) {
          if (err) return done(err);
  
          client.put('ip', '211.123.5.12', 1);
  
          setTimeout(function (){
            client.take('ip', '211.123.5.12', function (err, response) {
              if (err) return done(err);
              assert.equal(response.remaining, 9);
              done();
            });
          }, 100);
        });
      });
  
      it('should be able to fully reset the bucket', function (done) {
        client.take('ip', '211.1.1.12', 5, function (err, response) {
          if (err) return done(err);
          assert.equal(response.remaining, 5);
  
          client.put('ip', '211.1.1.12', function (err, response) {
            if (err) return done(err);
            assert.equal(response.remaining, 10);
  
            client.take('ip', '211.1.1.12', function (err, response) {
              if (err) return done(err);
              assert.equal(response.remaining, 9);
              done();
            });
          });
        });
      });
  
      it('should work for a fixed bucket', function (done) {
        client.take('wrong_password', 'calocalaccc', function (err, response) {
          assert.ok(response.conformant);
          client.reset('wrong_password', 'calocalaccc', function (err, response) {
            if (err) return done(err);
            assert.equal(response.remaining, 3);
            done();
          });
        });
      });
  
      it('should work for a fixed bucket (override)', function (done) {
        client.take('wrong_password', 'dudu2', function (err, response) {
          assert.ok(response.conformant);
          client.reset('wrong_password', 'dudu2', function (err, response) {
            if (err) return done(err);
            assert.equal(response.remaining, 2);
            done();
          });
        });
      });
  
      it('should work after a fire and forget call', function (done) {
        client.reset('wrong_password', 'dudu2');
        client.take('wrong_password', 'dudu2', function (err, response) {
          assert.ok(response.conformant);
          client.reset('wrong_password', 'dudu2', function (err, response) {
            if (err) return done(err);
            assert.equal(response.remaining, 2);
            done();
          });
        });
      });
  
      it('should work after a fire and forget call with unknown bucket', function (done) {
        client.reset('unknown_bucket', 'dudu2');
        client.take('wrong_password', 'dudu2', function (err, response) {
          assert.ok(response.conformant);
          client.reset('wrong_password', 'dudu2', function (err, response) {
            if (err) return done(err);
            assert.equal(response.remaining, 2);
            done();
          });
        });
      });
    });
  
    describe('ping', function () {
  
      it('should work', function (done) {
        client.ping(function (err) {
          if (err) return done(err);
          done();
        });
      });
  
    });
  
    describe('STATUS', function () {
  
      it('should work', function (done) {
        var ip = '211.11.84.12';
  
        client.take('ip', ip, function (err) {
          if (err) return done(err);
          client.status('ip', ip, function (err, response) {
            if (err) return done(err);
            assert.equal(response.items[0].remaining, 9);
            done();
          });
        });
      });
  
      it('should work for unlimited', function (done) {
        var now = 1425920267;
        MockDate.set(now * 1000);
        client.take('ip', '0.0.0.0', function (err) {
          if (err) return done(err);
          client.status('ip', '0.0.0.0', function (err, response) {
            if (err) return done(err);
            assert.equal(response.items.length, 0);
            done();
          });
        });
      });
  
      it('should work for fixed buckets', function (done) {
        client.take('wrong_password', 'curichiaga', function (err) {
          if (err) return done(err);
          client.status('wrong_password', 'curichiaga', function (err, response) {
            if (err) return done(err);
            assert.equal(response.items[0].remaining, 2);
            done();
          });
        });
      });
  
  
      it('should not return fulfilled fixed buckets', function (done) {
        client.take('wrong_password', 'catrasca', function (err) {
          if (err) return done(err);
          client.put('wrong_password', 'catrasca', function (err) {
            if (err) return done(err);
            client.status('wrong_password', 'catrasca', function (err, response) {
              if (err) return done(err);
              assert.equal(response.items.length, 0);
              done();
            });
          });
        });
      });
  
      it('should not fail if bucket doesnt exists', function (done) {
        client.status('ip', '12312312321312321', function (err, response) {
          if (err) return done(err);
          assert.equal(response.items.length, 0);
          done();
        });
      });
  
      it.skip('should work with subclasses', function (done) {
  
        async.parallel([
          function (cb) { client.take('ip', 'class1|192.123.21.1', cb); },
          function (cb) { client.take('ip', 'class1|192.123.21.2', cb); },
          function (cb) { client.take('ip', 'class1|192.123.21.2', cb); },
          function (cb) { client.take('ip', 'class2|192.123.21.3', cb); },
        ], function (err) {
          if (err) return done(err);
          //this will retrieve all bucket instances of ip - class1
          client.status('ip', 'class1', function (err, response) {
            if (err) return done(err);
            assert.equal(response.items.length, 2);
            assert.equal(response.items[0].remaining, 9);
            assert.equal(response.items[0].instance, 'class1|192.123.21.1');
            assert.equal(response.items[1].remaining, 8);
            assert.equal(response.items[1].instance, 'class1|192.123.21.2');
            done();
          });
        });
  
      });
  
    });
  
  });
  
  
  