<commit_before>var test = require('tape')
var DeferredLevelDOWN = require('./')

test('deferred open gets correct options', function (t) {
  var OPTIONS = { foo: 'BAR' }
  var db = {
    open: function (options, callback) {
      t.same(options, OPTIONS, 'options passed on to open')
      process.nextTick(callback)
    }
  }

  var ld = new DeferredLevelDOWN(db)
  ld.open(OPTIONS, function (err) {
    t.error(err, 'no error')
    t.end()
  })
})

test('single operation', function (t) {
  var called = false
  var db = {
    put: function (key, value, options, callback) {
      t.equal(key, 'foo', 'correct key')
      t.equal(value, 'bar', 'correct value')
      t.deepEqual({}, options, 'empty options')
      callback(null, 'called')
    },
    open: function (options, callback) {
      process.nextTick(callback)
    }
  }

  var ld = new DeferredLevelDOWN(db)
  ld.put('foo', 'bar', function (err, v) {
    t.error(err, 'no error')
    called = v
  })

  t.ok(called === false, 'not called')

  ld.open(function (err) {
    t.error(err, 'no error')
    t.ok(called === 'called', 'function called')
    t.end()
  })
})

test('many operations', function (t) {
  var calls = []
  var db = {
    put: function (key, value, options, callback) {
      if (puts++ === 0) {
        t.equal(key, 'foo1', 'correct key')
        t.equal(value, 'bar1', 'correct value')
        t.deepEqual(options, {}, 'empty options')
      } else {
        t.equal(key, 'foo2', 'correct key')
        t.equal(value, 'bar2', 'correct value')
        t.deepEqual(options, {}, 'empty options')
      }
      callback(null, 'put' + puts)
    },
    get: function (key, options, callback) {
      if (gets++ === 0) {
        t.equal('woo1', key, 'correct key')
        t.deepEqual(options, { asBuffer: true }, 'empty options')
      } else {
        t.equal('woo2', key, 'correct key')
        t.deepEqual(options, { asBuffer: true }, 'empty options')
      }
      callback(null, 'gets' + gets)
    },
    del: function (key, options, callback) {
      t.equal('blergh', key, 'correct key')
      t.deepEqual(options, {}, 'empty options')
      callback(null, 'del')
    },
    batch: function (arr, options, callback) {
      if (batches++ === 0) {
        t.deepEqual(arr, [
          { type: 'put', key: 'k1', value: 'v1' },
          { type: 'put', key: 'k2', value: 'v2' }
        ], 'correct batch')
      } else {
        t.deepEqual(arr, [
          { type: 'put', key: 'k3', value: 'v3' },
          { type: 'put', key: 'k4', value: 'v4' }
        ], 'correct batch')
      }
      callback()
    },
    open: function (options, callback) {
      process.nextTick(callback)
    }
  }

  var ld = new DeferredLevelDOWN(db)
  var puts = 0
  var gets = 0
  var batches = 0

  ld.put('foo1', 'bar1', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'put', key: 'foo1', v: v })
  })
  ld.get('woo1', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'get', key: 'woo1', v: v })
  })
  ld.put('foo2', 'bar2', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'put', key: 'foo2', v: v })
  })
  ld.get('woo2', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'get', key: 'woo2', v: v })
  })
  ld.del('blergh', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'del', key: 'blergh', v: v })
  })
  ld.batch([
    { type: 'put', key: 'k1', value: 'v1' },
    { type: 'put', key: 'k2', value: 'v2' }
  ], function () {
    calls.push({ type: 'batch', keys: 'k1,k2' })
  })
  ld
    .batch()
    .put('k3', 'v3')
    .put('k4', 'v4')
    .write(function () {
      calls.push({ type: 'batch', keys: 'k3,k4' })
    })

  t.ok(calls.length === 0, 'not called')

  ld.open(function (err) {
    t.error(err, 'no error')

    t.equal(calls.length, 7, 'all functions called')
    t.deepEqual(calls, [
      { type: 'put', key: 'foo1', v: 'put1' },
      { type: 'get', key: 'woo1', v: 'gets1' },
      { type: 'put', key: 'foo2', v: 'put2' },
      { type: 'get', key: 'woo2', v: 'gets2' },
      { type: 'del', key: 'blergh', v: 'del' },
      { type: 'batch', keys: 'k1,k2' },
      { type: 'batch', keys: 'k3,k4' }
    ], 'calls correctly behaved')

    t.end()
  })
})

test('keys and values should not be serialized', function (t) {
  var DATA = []
  var ITEMS = [
    123,
    'a string',
    Buffer.from('w00t'),
    { an: 'object' }
  ]
  ITEMS.forEach(function (k) {
    ITEMS.forEach(function (v) {
      DATA.push({ key: k, value: v })
    })
  })

  function Db (m, fn) {
    var db = {
      open: function (options, cb) {
        process.nextTick(cb)
      }
    }
    var wrapper = function () {
      fn.apply(null, arguments)
    }
    db[m] = wrapper
    return new DeferredLevelDOWN(db)
  }

  function noop () {}

  t.test('put', function (t) {
    var calls = []
    var ld = Db('put', function (key, value, cb) {
      calls.push({ key: key, value: value })
    })
    DATA.forEach(function (d) { ld.put(d.key, d.value, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, DATA, 'value ok')
      t.end()
    })
  })

  t.test('get', function (t) {
    var calls = []
    var ld = Db('get', function (key, cb) { calls.push(key) })
    ITEMS.forEach(function (key) { ld.get(key, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, ITEMS, 'value ok')
      t.end()
    })
  })

  t.test('del', function (t) {
    var calls = []
    var ld = Db('del', function (key, cb) { calls.push(key) })
    ITEMS.forEach(function (key) { ld.del(key, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, ITEMS, 'value ok')
      t.end()
    })
  })

  t.test('approximateSize', function (t) {
    var calls = []
    var ld = Db('approximateSize', function (start, end, cb) {
      calls.push({ start: start, end: end })
    })
    ITEMS.forEach(function (key) { ld.approximateSize(key, key, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, ITEMS.map(function (i) {
        return { start: i, end: i }
      }), 'value ok')
      t.end()
    })
  })
})

test('iterators', function (t) {
  t.plan(8)

  var db = {
    iterator: function (options) {
      return {
        next: function (cb) {
          cb(null, 'key', 'value')
        },
        end: function (cb) {
          process.nextTick(cb)
        }
      }
    },
    open: function (options, callback) {
      process.nextTick(callback)
    }
  }
  var ld = new DeferredLevelDOWN(db)
  var it = ld.iterator()
  var nextFirst = false

  it.next(function (err, key, value) {
    nextFirst = true
    t.error(err, 'no error')
    t.equal(key, 'key')
    t.equal(value, 'value')
  })

  it.end(function (err) {
    t.error(err, 'no error')
    t.ok(nextFirst)
  })

  ld.open(function (err) {
    t.error(err, 'no error')
    var it2 = ld.iterator()
    it2.end(t.error.bind(t))
  })

  t.ok(require('./').DeferredIterator)
})
<commit_msg>add test for stores not supporting approximateSize<commit_after>var test = require('tape')
var DeferredLevelDOWN = require('./')

test('deferred open gets correct options', function (t) {
  var OPTIONS = { foo: 'BAR' }
  var db = {
    open: function (options, callback) {
      t.same(options, OPTIONS, 'options passed on to open')
      process.nextTick(callback)
    }
  }

  var ld = new DeferredLevelDOWN(db)
  ld.open(OPTIONS, function (err) {
    t.error(err, 'no error')
    t.end()
  })
})

test('single operation', function (t) {
  var called = false
  var db = {
    put: function (key, value, options, callback) {
      t.equal(key, 'foo', 'correct key')
      t.equal(value, 'bar', 'correct value')
      t.deepEqual({}, options, 'empty options')
      callback(null, 'called')
    },
    open: function (options, callback) {
      process.nextTick(callback)
    }
  }

  var ld = new DeferredLevelDOWN(db)
  ld.put('foo', 'bar', function (err, v) {
    t.error(err, 'no error')
    called = v
  })

  t.ok(called === false, 'not called')

  ld.open(function (err) {
    t.error(err, 'no error')
    t.ok(called === 'called', 'function called')
    t.end()
  })
})

test('many operations', function (t) {
  var calls = []
  var db = {
    put: function (key, value, options, callback) {
      if (puts++ === 0) {
        t.equal(key, 'foo1', 'correct key')
        t.equal(value, 'bar1', 'correct value')
        t.deepEqual(options, {}, 'empty options')
      } else {
        t.equal(key, 'foo2', 'correct key')
        t.equal(value, 'bar2', 'correct value')
        t.deepEqual(options, {}, 'empty options')
      }
      callback(null, 'put' + puts)
    },
    get: function (key, options, callback) {
      if (gets++ === 0) {
        t.equal('woo1', key, 'correct key')
        t.deepEqual(options, { asBuffer: true }, 'empty options')
      } else {
        t.equal('woo2', key, 'correct key')
        t.deepEqual(options, { asBuffer: true }, 'empty options')
      }
      callback(null, 'gets' + gets)
    },
    del: function (key, options, callback) {
      t.equal('blergh', key, 'correct key')
      t.deepEqual(options, {}, 'empty options')
      callback(null, 'del')
    },
    batch: function (arr, options, callback) {
      if (batches++ === 0) {
        t.deepEqual(arr, [
          { type: 'put', key: 'k1', value: 'v1' },
          { type: 'put', key: 'k2', value: 'v2' }
        ], 'correct batch')
      } else {
        t.deepEqual(arr, [
          { type: 'put', key: 'k3', value: 'v3' },
          { type: 'put', key: 'k4', value: 'v4' }
        ], 'correct batch')
      }
      callback()
    },
    open: function (options, callback) {
      process.nextTick(callback)
    }
  }

  var ld = new DeferredLevelDOWN(db)
  var puts = 0
  var gets = 0
  var batches = 0

  ld.put('foo1', 'bar1', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'put', key: 'foo1', v: v })
  })
  ld.get('woo1', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'get', key: 'woo1', v: v })
  })
  ld.put('foo2', 'bar2', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'put', key: 'foo2', v: v })
  })
  ld.get('woo2', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'get', key: 'woo2', v: v })
  })
  ld.del('blergh', function (err, v) {
    t.error(err, 'no error')
    calls.push({ type: 'del', key: 'blergh', v: v })
  })
  ld.batch([
    { type: 'put', key: 'k1', value: 'v1' },
    { type: 'put', key: 'k2', value: 'v2' }
  ], function () {
    calls.push({ type: 'batch', keys: 'k1,k2' })
  })
  ld
    .batch()
    .put('k3', 'v3')
    .put('k4', 'v4')
    .write(function () {
      calls.push({ type: 'batch', keys: 'k3,k4' })
    })

  t.ok(calls.length === 0, 'not called')

  ld.open(function (err) {
    t.error(err, 'no error')

    t.equal(calls.length, 7, 'all functions called')
    t.deepEqual(calls, [
      { type: 'put', key: 'foo1', v: 'put1' },
      { type: 'get', key: 'woo1', v: 'gets1' },
      { type: 'put', key: 'foo2', v: 'put2' },
      { type: 'get', key: 'woo2', v: 'gets2' },
      { type: 'del', key: 'blergh', v: 'del' },
      { type: 'batch', keys: 'k1,k2' },
      { type: 'batch', keys: 'k3,k4' }
    ], 'calls correctly behaved')

    t.end()
  })
})

test('keys and values should not be serialized', function (t) {
  var DATA = []
  var ITEMS = [
    123,
    'a string',
    Buffer.from('w00t'),
    { an: 'object' }
  ]
  ITEMS.forEach(function (k) {
    ITEMS.forEach(function (v) {
      DATA.push({ key: k, value: v })
    })
  })

  function Db (m, fn) {
    var db = {
      open: function (options, cb) {
        process.nextTick(cb)
      }
    }
    var wrapper = function () {
      fn.apply(null, arguments)
    }
    db[m] = wrapper
    return new DeferredLevelDOWN(db)
  }

  function noop () {}

  t.test('put', function (t) {
    var calls = []
    var ld = Db('put', function (key, value, cb) {
      calls.push({ key: key, value: value })
    })
    DATA.forEach(function (d) { ld.put(d.key, d.value, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, DATA, 'value ok')
      t.end()
    })
  })

  t.test('get', function (t) {
    var calls = []
    var ld = Db('get', function (key, cb) { calls.push(key) })
    ITEMS.forEach(function (key) { ld.get(key, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, ITEMS, 'value ok')
      t.end()
    })
  })

  t.test('del', function (t) {
    var calls = []
    var ld = Db('del', function (key, cb) { calls.push(key) })
    ITEMS.forEach(function (key) { ld.del(key, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, ITEMS, 'value ok')
      t.end()
    })
  })

  t.test('approximateSize', function (t) {
    var calls = []
    var ld = Db('approximateSize', function (start, end, cb) {
      calls.push({ start: start, end: end })
    })
    ITEMS.forEach(function (key) { ld.approximateSize(key, key, noop) })
    ld.open(function (err) {
      t.error(err, 'no error')
      t.same(calls, ITEMS.map(function (i) {
        return { start: i, end: i }
      }), 'value ok')
      t.end()
    })
  })

  t.test('store not supporting approximateSize', function (t) {
    var ld = Db('FOO', function () {})
    t.throws(function () {
      ld.approximateSize('key', 'key', noop)
    }, /approximateSize is not a function/)
    t.end()
  })
})

test('iterators', function (t) {
  t.plan(8)

  var db = {
    iterator: function (options) {
      return {
        next: function (cb) {
          cb(null, 'key', 'value')
        },
        end: function (cb) {
          process.nextTick(cb)
        }
      }
    },
    open: function (options, callback) {
      process.nextTick(callback)
    }
  }
  var ld = new DeferredLevelDOWN(db)
  var it = ld.iterator()
  var nextFirst = false

  it.next(function (err, key, value) {
    nextFirst = true
    t.error(err, 'no error')
    t.equal(key, 'key')
    t.equal(value, 'value')
  })

  it.end(function (err) {
    t.error(err, 'no error')
    t.ok(nextFirst)
  })

  ld.open(function (err) {
    t.error(err, 'no error')
    var it2 = ld.iterator()
    it2.end(t.error.bind(t))
  })

  t.ok(require('./').DeferredIterator)
})
<|endoftext|>


<commit_before>'use strict';

require('babel-polyfill');

import assert from 'power-assert';
import childProcess from 'child_process';

let exec = childProcess.exec;

import KyotoTocoon from '../index';

describe('kt-client', () => {
  describe('get test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set test_key test_value', resolve);
      });

      let client = new KyotoTocoon();

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('specify DB', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set -db blue test_key test_value', resolve);
      });

      let client = new KyotoTocoon();
      let options = {
        db: 'blue'
      };

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('utf-8 data', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set test_key test_value', resolve);
      });

      let client = new KyotoTocoon();
      let options = {
        encoding: 'utf8'
      };

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('binary data', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set test_key binary', resolve);
      });

      let client = new KyotoTocoon();
      let options = {
        encoding: 'binary'
      };

      client.get('test_key', options, (error, value, expire) => {
        assert(Buffer.isBuffer(value));
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('data and expiration time', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set -xt 300 test_key test_value', resolve);
      });

      let client = new KyotoTocoon();

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire instanceof Date);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('no data', (done) => {
      let client = new KyotoTocoon();

      client.get('test_key', (error, value, expire) => {
        assert(typeof value === 'undefined');
        assert(typeof expire === 'undefined');
        assert(error === 'No record was found');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.get('test_key', (error, value, expire) => {
        assert(typeof value === 'undefined');
        assert(typeof expire === 'undefined');
        assert(error === 'Connection error');
        done();
      });
    });
  });

  describe('set test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('specify DB', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        db: 'blue'
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.get('test_key', (error, value, expire) => {
          assert(typeof value === 'undefined');
          assert(typeof expire === 'undefined');
          assert(error === 'No record was found');
          resolve();
        });
      });

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('utf-8 data', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        encoding: 'utf8'
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('binary data', async (done) => {
      let client = new KyotoTocoon();
      let testValue = new Buffer('test_value');
      let options = {
        encoding: 'binary'
      };

      await new Promise((resolve) => {
        client.set('test_key', testValue, options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', options, (error, value, expire) => {
        assert(value.toString() === testValue.toString());
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('object data', async (done) => {
      let client = new KyotoTocoon();
      let testValue = {
        key: 'test_value'
      };

      await new Promise((resolve) => {
        client.set('test_key', testValue, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert.deepEqual(JSON.parse(value), testValue);
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('data and expiration time', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        expire: 300
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire instanceof Date);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.set('test_key', 'test_value', (error) => {
        assert(error === 'Connection error');
        done();
      });
    });
  });

  describe('add test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.add('test_key', 'test_value', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('already exists', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'foo', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.add('test_key', 'test_value', (error) => {
          assert(error === 'Connection error');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(value === 'foo');
        done();
      });
    });
  });

  describe('replace test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'foo', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.replace('test_key', 'test_value', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(value === 'test_value');
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('not exists', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'foo', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.add('test_key', 'test_value', (error) => {
          assert(error === 'Connection error');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(value === 'foo');
        done();
      });
    });
  });

  describe('remove test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.get('test_key', (error, value) => {
          assert(value === 'test_value');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.remove('test_key', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(typeof value === 'undefined');
        assert(error === 'No record was found');
        done();
      });
    });

    it('specify DB', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        db: 'blue'
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.get('test_key', options, (error, value) => {
          assert(value === 'test_value');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.remove('test_key', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', options, (error, value) => {
        assert(typeof value === 'undefined');
        assert(error === 'No record was found');
        done();
      });
    });

    it('not exists', (done) => {
      let client = new KyotoTocoon();

      client.remove('test_key', (error) => {
        assert(error === 'No record was found');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.remove('test_key', (error) => {
        assert(error === 'Connection error');
        done();
      });
    });
  });

  describe('report test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('success', (done) => {
      let client = new KyotoTocoon();

      client.report((error, data) => {
        assert(typeof data === 'object');
        assert(data.hasOwnProperty('repl_master_port'));
        assert(data.repl_master_port === '1978');
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.report((error, data) => {
        assert(typeof data === 'undefined');
        assert(error === 'Connection error');
        done();
      });
    });
  });
});
<commit_msg>Fixed test<commit_after>'use strict';

require('babel-polyfill');

import assert from 'power-assert';
import childProcess from 'child_process';

let exec = childProcess.exec;

import KyotoTocoon from '../index';

describe('kt-client', () => {
  describe('get test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set test_key test_value', resolve);
      });

      let client = new KyotoTocoon();

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('specify DB', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set -db blue test_key test_value', resolve);
      });

      let client = new KyotoTocoon();
      let options = {
        db: 'blue'
      };

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('utf-8 data', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set test_key test_value', resolve);
      });

      let client = new KyotoTocoon();
      let options = {
        encoding: 'utf8'
      };

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('binary data', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set test_key binary', resolve);
      });

      let client = new KyotoTocoon();
      let options = {
        encoding: 'binary'
      };

      client.get('test_key', options, (error, value, expire) => {
        assert(Buffer.isBuffer(value));
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('data and expiration time', async (done) => {
      await new Promise((resolve) => {
        exec('ktremotemgr set -xt 300 test_key test_value', resolve);
      });

      let client = new KyotoTocoon();

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire instanceof Date);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('no data', (done) => {
      let client = new KyotoTocoon();

      client.get('test_key', (error, value, expire) => {
        assert(typeof value === 'undefined');
        assert(typeof expire === 'undefined');
        assert(error === 'No record was found');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.get('test_key', (error, value, expire) => {
        assert(typeof value === 'undefined');
        assert(typeof expire === 'undefined');
        assert(error === 'Connection error');
        done();
      });
    });
  });

  describe('set test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('specify DB', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        db: 'blue'
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.get('test_key', (error, value, expire) => {
          assert(typeof value === 'undefined');
          assert(typeof expire === 'undefined');
          assert(error === 'No record was found');
          resolve();
        });
      });

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('utf-8 data', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        encoding: 'utf8'
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', options, (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('binary data', async (done) => {
      let client = new KyotoTocoon();
      let testValue = new Buffer('test_value');
      let options = {
        encoding: 'binary'
      };

      await new Promise((resolve) => {
        client.set('test_key', testValue, options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', options, (error, value, expire) => {
        assert(value.toString() === testValue.toString());
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('object data', async (done) => {
      let client = new KyotoTocoon();
      let testValue = {
        key: 'test_value'
      };

      await new Promise((resolve) => {
        client.set('test_key', testValue, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert.deepEqual(JSON.parse(value), testValue);
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('data and expiration time', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        expire: 300
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire instanceof Date);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.set('test_key', 'test_value', (error) => {
        assert(error === 'Connection error');
        done();
      });
    });
  });

  describe('add test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.add('test_key', 'test_value', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value, expire) => {
        assert(value === 'test_value');
        assert(expire === null);
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('already exists', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'foo', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.add('test_key', 'test_value', (error) => {
          assert(error === 'Connection error');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(value === 'foo');
        done();
      });
    });
  });

  describe('replace test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'foo', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.replace('test_key', 'test_value', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(value === 'test_value');
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('not exists', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'foo', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.add('test_key', 'test_value', (error) => {
          assert(error === 'Connection error');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(value === 'foo');
        done();
      });
    });
  });

  describe('remove test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('data', async (done) => {
      let client = new KyotoTocoon();

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', () => {
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.get('test_key', (error, value) => {
          assert(value === 'test_value');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.remove('test_key', (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', (error, value) => {
        assert(typeof value === 'undefined');
        assert(error === 'No record was found');
        done();
      });
    });

    it('specify DB', async (done) => {
      let client = new KyotoTocoon();
      let options = {
        db: 'blue'
      };

      await new Promise((resolve) => {
        client.set('test_key', 'test_value', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.get('test_key', options, (error, value) => {
          assert(value === 'test_value');
          resolve();
        });
      });

      await new Promise((resolve) => {
        client.remove('test_key', options, (error) => {
          assert(typeof error === 'undefined');
          resolve();
        });
      });

      client.get('test_key', options, (error, value) => {
        assert(typeof value === 'undefined');
        assert(error === 'No record was found');
        done();
      });
    });

    it('not exists', (done) => {
      let client = new KyotoTocoon();

      client.remove('test_key', (error) => {
        assert(error === 'No record was found');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.remove('test_key', (error) => {
        assert(error === 'Connection error');
        done();
      });
    });
  });

  describe('report test', () => {
    beforeEach((done) => {
      exec('ktremotemgr clear', () => {
        done();
      });
    });

    it('success', (done) => {
      let client = new KyotoTocoon();

      client.report((error, data) => {
        assert(typeof data === 'object');
        assert(data.hasOwnProperty('db_total_count'));
        assert(data.db_total_count === '0');
        assert(typeof error === 'undefined');
        done();
      });
    });

    it('connection error', (done) => {
      let client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });

      client.report((error, data) => {
        assert(typeof data === 'undefined');
        assert(error === 'Connection error');
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>/*
 * lib/dragnet.js: dragnet library interface
 */

var mod_assertplus = require('assert-plus');
var mod_jsprim = require('jsprim');
var mod_krill = require('krill');
var mod_skinner = require('skinner');
var VError = require('verror');

/* Public interface */
exports.indexLoad = indexLoad;
exports.queryLoad = queryLoad;

/*
 * This is more of a struct than a class.  Its fields are used directly by
 * consuming code.  The constructor is private, and should only be invoked with
 * a validated index configuration.
 */
function IndexConfig(args)
{
	var rawindex, byname;

	mod_assertplus.object(args, 'args');
	mod_assertplus.object(args.rawindex, 'args.rawindex');
	mod_assertplus.arrayOfObject(args.columns, 'args.columns');
	mod_assertplus.optionalObject(args.timeBefore, 'args.timeBefore');
	mod_assertplus.optionalObject(args.timeAfter, 'args.timeAfter');

	rawindex = args.rawindex;

	/*
	 * The following fields are exactly the same as the corresponding
	 * top-level properties in the "index" schema, with optional fields
	 * being "null" if unspecified.
	 */
	this.ic_name = rawindex.name;
	this.ic_filter = rawindex.filter || null;
	this.ic_interval = args.interval;
	this.ic_before = args.timeBefore || null;
	this.ic_after = args.timeAfter || null;

	/*
	 * Columns are also just as they appear in the schema, except that the
	 * string shorthand is expanded into an object so that consumers can
	 * always assume that canonical form.  Besides having the columns in
	 * order, it's useful to have them indexed by name.
	 */
	this.ic_columns = mod_jsprim.deepCopy(args.columns);
	this.ic_columns_byname = byname = {};
	this.ic_columns.forEach(function (col) { byname[col.name] = col; });

	/*
	 * Synthetic columns are those derived from other columns.  The main
	 * reason to do this today is for parsing datestamps.
	 */
	this.ic_synthetic = this.ic_columns.filter(
	    function (c) { return (c.hasOwnProperty('date')); });
	if (this.ic_synthetic.length > 0)
		this.ic_timefield = this.ic_synthetic[0];
	else
		this.ic_timefield = null;
}

/*
 * Validate the given index configuration, and load helper objects used to work
 * with this index.  On success, returns an IndexConfig object.  On failure,
 * return an Error describing the problem.
 */
function indexLoad(args)
{
	var rawindex, filter, columns, timebounds, indexconf;

	mod_assertplus.object(args, 'args');
	mod_assertplus.object(args.index, 'args.index');

	/*
	 * Parse and validate the filter.
	 * XXX validate basic structure first (fields present and with the
	 * correct types; interval is present)
	 */
	rawindex = args.index;
	filter = rawindex.filter;
	if (filter) {
		try {
			mod_krill.createPredicate(filter);
		} catch (ex) {
			return (new VError(ex, 'invalid filter'));
		}
	}

	/*
	 * Expand and validate the index column definitions.
	 */
	columns = parseFields(rawindex.columns);
	if (columns instanceof Error)
		return (columns);

	/*
	 * Check the "before" and "after" fields.
	 */
	timebounds = parseTimeBounds({
	    'timeAfter': args.timeAfter,
	    'timeBefore': args.timeBefore
	});
	if (timebounds instanceof Error)
		return (timebounds);

	switch (rawindex.interval) {
	case 'hour':
	case 'day':
		break;
	default:
		return (new VError('unsupported interval: "%s"',
		    rawindex.interval));
	}

	indexconf = new IndexConfig({
	    'rawindex': rawindex,
	    'columns': columns,
	    'interval': rawindex.interval,
	    'timeAfter': timebounds.timeAfter,
	    'timeBefore': timebounds.timeBefore
	});

	return (indexconf);
}

/*
 * Like IndexConfig, this is a struct-like class that represents the immutable
 * parameters of a specific query.
 */
function QueryConfig(args)
{
	var self = this;

	mod_assertplus.object(args, 'args');
	mod_assertplus.optionalObject(args.filter, 'args.filter');
	mod_assertplus.arrayOfObject(args.breakdowns, 'args.breakdowns');

	this.qc_filter = args.filter || null;
	this.qc_breakdowns = mod_jsprim.deepCopy(args.breakdowns);
	this.qc_before = args.timeBefore || null;
	this.qc_after = args.timeAfter || null;
	this.qc_fieldsbyname = {};
	this.qc_bucketizers = {};
	this.qc_synthetic = [];

	if (args.timeField) {
		this.qc_synthetic.push({
		    'name': args.timeField,
		    'field': args.timeField,
		    'date': ''
		});
	}

	this.qc_breakdowns.forEach(function (fieldconf) {
		self.qc_fieldsbyname[fieldconf.name] = fieldconf;

		if (fieldconf.hasOwnProperty('date'))
			self.qc_synthetic.push(fieldconf);

		if (!fieldconf.hasOwnProperty('aggr'))
			return;

		if (fieldconf.aggr == 'quantize') {
			self.qc_bucketizers[fieldconf.name] =
			    mod_skinner.makeP2Bucketizer();
			return;
		}

		mod_assertplus.equal(fieldconf.aggr, 'lquantize');
		mod_assertplus.number(fieldconf.step);
		self.qc_bucketizers[fieldconf.name] =
		    mod_skinner.makeLinearBucketizer(fieldconf.step);
	});

	if (this.qc_before !== null) {
		mod_assertplus.ok(this.qc_after !== null);
		mod_assertplus.ok(this.qc_synthetic.length > 0);
	} else {
		mod_assertplus.ok(this.qc_after === null);
	}

	if (this.qc_synthetic.length > 0)
		this.qc_timefield = this.qc_synthetic[0];
	else
		this.qc_timefield = null;
}

/*
 * Normalize and validate the requested query.  On success, returns a
 * QueryConfig object that describes the query parameters.  On failure, returns
 * an Error describing what's invalid.  Named arguments include:
 *
 *     query		describes the query parameters, including:
 *
 *         [filter]		node-krill-syntax plain-JS-object filter
 *
 *         breakdowns		List of fields to break out results by.  These
 *         			should be strings in the same format as for
 *         			index definitions.
 *
 *         [timeAfter]		Prune indexes covering times before this time.
 *
 *         [timeBefore]		Prune indexes covering times after this time.
 *
 *         [timeField]		Extra field (not part of the breakdowns) that's
 *         			used for processing "before" and "after"
 *         			constraints.
 *
 *     [index]		An index, as constructed with indexLoad.  If specified,
 *			all fields of "breakdowns" must be simple names that
 *			refer to columns from the index.
 *
 *     allowReserved	Allow reserved field names to be used.  This should only
 *     			be used internally.
 */
function queryLoad(args)
{
	var filter, breakdowns, timebounds;

	mod_assertplus.object(args, 'args');
	mod_assertplus.object(args.query, 'args.query');
	mod_assertplus.optionalObject(args.query.filter, 'args.query.filter');
	mod_assertplus.ok(Array.isArray(args.query.breakdowns));
	mod_assertplus.optionalObject(args.index, 'args.index');

	if (args.query.filter) {
		filter = args.query.filter;

		try {
			mod_krill.createPredicate(filter);
		} catch (ex) {
			return (new VError(ex,
			    'invalid query: invalid filter'));
		}
	} else {
		filter = null;
	}

	breakdowns = parseFields(args.query.breakdowns, args.index,
	    { 'allowReserved': args.allowReserved });
	if (breakdowns instanceof Error)
		return (new VError(breakdowns, 'invalid query'));

	if (args.query.timeBefore !== undefined &&
	    args.query.timeField === undefined && !hasDateField(breakdowns))
		return (new VError('must specify a "date" field to use ' +
		    '"before" and "after" constraints'));

	timebounds = parseTimeBounds({
	    'timeAfter': args.query.timeAfter,
	    'timeBefore': args.query.timeBefore
	});
	if (timebounds instanceof Error)
		return (timebounds);

	return (new QueryConfig({
	    'filter': filter,
	    'breakdowns': breakdowns,
	    'timeAfter': timebounds.timeAfter,
	    'timeBefore': timebounds.timeBefore,
	    'timeField': args.query.timeField
	}));
}

/*
 * Parse and validate the "before" and "after" timestamps.  Because of the way
 * we iterate files and objects later, we can only support neither or both of
 * these fields.
 */
function parseTimeBounds(args)
{
	var timeBefore, timeAfter;

	/*
	 * Check the "before" and "after" fields.  Because of the way we iterate
	 * them, we can only support neither or both.
	 */
	if (args.timeAfter) {
		if (!args.timeBefore) {
			return (new VError(
			    '"after" requires specifying "before" too'));
		}

		timeAfter = new Date(args.timeAfter);
		if (isNaN(timeAfter.getTime()))
			return (new VError('"after": not a valid date: "%s"',
			    args.timeAfter));

		timeBefore = new Date(args.timeBefore);
		if (isNaN(timeBefore.getTime()))
			return (new VError('"before": not a valid date: "%s"',
			    args.timeBefore));

		if (timeAfter.getTime() > timeBefore.getTime())
			return (new VError('"after" timestamp may not ' +
			    'come after "before"'));
	} else if (args.timeBefore) {
		return (new VError('"before" requires specifying "after" too'));
	}

	return ({
	    'timeAfter': timeAfter,
	    'timeBefore': timeBefore
	});
}

/*
 * Parse an array of columns, as specified for both a query and an index.
 */
function parseFields(inputs, index, options)
{
	var fields, i, b, ret;

	fields = new Array(inputs.length);
	for (i = 0; i < inputs.length; i++) {
		b = inputs[i];
		ret = parseField(b, index, options);
		if (ret instanceof Error) {
			return (new VError(ret,
			    'field %d ("%s") is invalid', i, b));
		}

		fields[i] = ret;
	}

	return (fields);
}

function parseField(b, index, options)
{
	var i, j, kvpairs, rv, step;

	if (index) {
		if (typeof (b) != 'string')
			return (new Error('expected string'));

		if (!index.ic_columns_byname.hasOwnProperty(b))
			return (new VError('not found in index'));

		return (index.ic_columns_byname[b]);
	}

	if (typeof (b) != 'string') {
		if (b.hasOwnProperty('aggr')) {
			if (b['aggr'] != 'quantize' && b['aggr'] != 'lquantize')
				return (new VError(
				    'unsupported aggr: "%s"', b['aggr']));

			if (b['aggr'] == 'lquantize') {
				if (!b.hasOwnProperty('step'))
					return (new VError('aggr "lquantize" ' +
					    'requires "step"'));

				step = parseInt(b['step'], 10);
				if (isNaN(step))
					return (new VError('aggr "lquzntize":' +
					    ' invalid value for "step": "%s"',
					    b['step']));
				b['step'] = step;
			}
		}

		if (b.hasOwnProperty('date')) {
			if (!b.hasOwnProperty('field'))
				return (new VError('"date" fields must ' +
				    'specify an underlying "field"'));
		}

		if ((!options || !options.allowReserved) &&
		    mod_jsprim.startsWith(b.name, '__dn'))
			return (new VError('field names starting with ' +
			    '"__dn" are reserved'));

		return (b);
	}

	i = b.indexOf('[');
	j = b.indexOf(']');
	if ((i == -1 && j != -1) ||
	    (i != -1 && j != b.length - 1))
		return (new VError('invalid field name'));

	rv = {};
	if (i == -1) {
		rv['name'] = b;
	} else {
		rv['name'] = b.substr(0, i);
		kvpairs = b.substr(i + 1, j - (i + 1)).split(';');
		kvpairs.forEach(function (kvpair) {
			var parts = kvpair.split('=');
			if (parts[0].trim().length === 0)
				return;
			rv[parts[0]] = parts[1] || '';
		});
	}

	if (!rv.hasOwnProperty('date') && !rv.hasOwnProperty('field'))
		rv['field'] = rv['name'];

	/* Use recursive call to validate. */
	return (parseField(rv, index));
}

function hasDateField(columns)
{
	for (var i = 0; i < columns.length; i++) {
		if (columns[i].hasOwnProperty('date'))
			return (true);
	}

	return (false);
}
<commit_msg>dragnet#1 eliminate bogus "field" requirement for "date" fields<commit_after>/*
 * lib/dragnet.js: dragnet library interface
 */

var mod_assertplus = require('assert-plus');
var mod_jsprim = require('jsprim');
var mod_krill = require('krill');
var mod_skinner = require('skinner');
var VError = require('verror');

/* Public interface */
exports.indexLoad = indexLoad;
exports.queryLoad = queryLoad;

/*
 * This is more of a struct than a class.  Its fields are used directly by
 * consuming code.  The constructor is private, and should only be invoked with
 * a validated index configuration.
 */
function IndexConfig(args)
{
	var rawindex, byname;

	mod_assertplus.object(args, 'args');
	mod_assertplus.object(args.rawindex, 'args.rawindex');
	mod_assertplus.arrayOfObject(args.columns, 'args.columns');
	mod_assertplus.optionalObject(args.timeBefore, 'args.timeBefore');
	mod_assertplus.optionalObject(args.timeAfter, 'args.timeAfter');

	rawindex = args.rawindex;

	/*
	 * The following fields are exactly the same as the corresponding
	 * top-level properties in the "index" schema, with optional fields
	 * being "null" if unspecified.
	 */
	this.ic_name = rawindex.name;
	this.ic_filter = rawindex.filter || null;
	this.ic_interval = args.interval;
	this.ic_before = args.timeBefore || null;
	this.ic_after = args.timeAfter || null;

	/*
	 * Columns are also just as they appear in the schema, except that the
	 * string shorthand is expanded into an object so that consumers can
	 * always assume that canonical form.  Besides having the columns in
	 * order, it's useful to have them indexed by name.
	 */
	this.ic_columns = mod_jsprim.deepCopy(args.columns);
	this.ic_columns_byname = byname = {};
	this.ic_columns.forEach(function (col) { byname[col.name] = col; });

	/*
	 * Synthetic columns are those derived from other columns.  The main
	 * reason to do this today is for parsing datestamps.
	 */
	this.ic_synthetic = this.ic_columns.filter(
	    function (c) { return (c.hasOwnProperty('date')); });
	if (this.ic_synthetic.length > 0)
		this.ic_timefield = this.ic_synthetic[0];
	else
		this.ic_timefield = null;
}

/*
 * Validate the given index configuration, and load helper objects used to work
 * with this index.  On success, returns an IndexConfig object.  On failure,
 * return an Error describing the problem.
 */
function indexLoad(args)
{
	var rawindex, filter, columns, timebounds, indexconf;

	mod_assertplus.object(args, 'args');
	mod_assertplus.object(args.index, 'args.index');

	/*
	 * Parse and validate the filter.
	 * XXX validate basic structure first (fields present and with the
	 * correct types; interval is present)
	 */
	rawindex = args.index;
	filter = rawindex.filter;
	if (filter) {
		try {
			mod_krill.createPredicate(filter);
		} catch (ex) {
			return (new VError(ex, 'invalid filter'));
		}
	}

	/*
	 * Expand and validate the index column definitions.
	 */
	columns = parseFields(rawindex.columns);
	if (columns instanceof Error)
		return (columns);

	/*
	 * Check the "before" and "after" fields.
	 */
	timebounds = parseTimeBounds({
	    'timeAfter': args.timeAfter,
	    'timeBefore': args.timeBefore
	});
	if (timebounds instanceof Error)
		return (timebounds);

	switch (rawindex.interval) {
	case 'hour':
	case 'day':
		break;
	default:
		return (new VError('unsupported interval: "%s"',
		    rawindex.interval));
	}

	indexconf = new IndexConfig({
	    'rawindex': rawindex,
	    'columns': columns,
	    'interval': rawindex.interval,
	    'timeAfter': timebounds.timeAfter,
	    'timeBefore': timebounds.timeBefore
	});

	return (indexconf);
}

/*
 * Like IndexConfig, this is a struct-like class that represents the immutable
 * parameters of a specific query.
 */
function QueryConfig(args)
{
	var self = this;

	mod_assertplus.object(args, 'args');
	mod_assertplus.optionalObject(args.filter, 'args.filter');
	mod_assertplus.arrayOfObject(args.breakdowns, 'args.breakdowns');

	this.qc_filter = args.filter || null;
	this.qc_breakdowns = mod_jsprim.deepCopy(args.breakdowns);
	this.qc_before = args.timeBefore || null;
	this.qc_after = args.timeAfter || null;
	this.qc_fieldsbyname = {};
	this.qc_bucketizers = {};
	this.qc_synthetic = [];

	if (args.timeField) {
		this.qc_synthetic.push({
		    'name': args.timeField,
		    'field': args.timeField,
		    'date': ''
		});
	}

	this.qc_breakdowns.forEach(function (fieldconf) {
		self.qc_fieldsbyname[fieldconf.name] = fieldconf;

		if (fieldconf.hasOwnProperty('date'))
			self.qc_synthetic.push(fieldconf);

		if (!fieldconf.hasOwnProperty('aggr'))
			return;

		if (fieldconf.aggr == 'quantize') {
			self.qc_bucketizers[fieldconf.name] =
			    mod_skinner.makeP2Bucketizer();
			return;
		}

		mod_assertplus.equal(fieldconf.aggr, 'lquantize');
		mod_assertplus.number(fieldconf.step);
		self.qc_bucketizers[fieldconf.name] =
		    mod_skinner.makeLinearBucketizer(fieldconf.step);
	});

	if (this.qc_before !== null) {
		mod_assertplus.ok(this.qc_after !== null);
		mod_assertplus.ok(this.qc_synthetic.length > 0);
	} else {
		mod_assertplus.ok(this.qc_after === null);
	}

	if (this.qc_synthetic.length > 0)
		this.qc_timefield = this.qc_synthetic[0];
	else
		this.qc_timefield = null;
}

/*
 * Normalize and validate the requested query.  On success, returns a
 * QueryConfig object that describes the query parameters.  On failure, returns
 * an Error describing what's invalid.  Named arguments include:
 *
 *     query		describes the query parameters, including:
 *
 *         [filter]		node-krill-syntax plain-JS-object filter
 *
 *         breakdowns		List of fields to break out results by.  These
 *         			should be strings in the same format as for
 *         			index definitions.
 *
 *         [timeAfter]		Prune indexes covering times before this time.
 *
 *         [timeBefore]		Prune indexes covering times after this time.
 *
 *         [timeField]		Extra field (not part of the breakdowns) that's
 *         			used for processing "before" and "after"
 *         			constraints.
 *
 *     [index]		An index, as constructed with indexLoad.  If specified,
 *			all fields of "breakdowns" must be simple names that
 *			refer to columns from the index.
 *
 *     allowReserved	Allow reserved field names to be used.  This should only
 *     			be used internally.
 */
function queryLoad(args)
{
	var filter, breakdowns, timebounds;

	mod_assertplus.object(args, 'args');
	mod_assertplus.object(args.query, 'args.query');
	mod_assertplus.optionalObject(args.query.filter, 'args.query.filter');
	mod_assertplus.ok(Array.isArray(args.query.breakdowns));
	mod_assertplus.optionalObject(args.index, 'args.index');

	if (args.query.filter) {
		filter = args.query.filter;

		try {
			mod_krill.createPredicate(filter);
		} catch (ex) {
			return (new VError(ex,
			    'invalid query: invalid filter'));
		}
	} else {
		filter = null;
	}

	breakdowns = parseFields(args.query.breakdowns, args.index,
	    { 'allowReserved': args.allowReserved });
	if (breakdowns instanceof Error)
		return (new VError(breakdowns, 'invalid query'));

	if (args.query.timeBefore !== undefined &&
	    args.query.timeField === undefined && !hasDateField(breakdowns))
		return (new VError('must specify a "date" field to use ' +
		    '"before" and "after" constraints'));

	timebounds = parseTimeBounds({
	    'timeAfter': args.query.timeAfter,
	    'timeBefore': args.query.timeBefore
	});
	if (timebounds instanceof Error)
		return (timebounds);

	return (new QueryConfig({
	    'filter': filter,
	    'breakdowns': breakdowns,
	    'timeAfter': timebounds.timeAfter,
	    'timeBefore': timebounds.timeBefore,
	    'timeField': args.query.timeField
	}));
}

/*
 * Parse and validate the "before" and "after" timestamps.  Because of the way
 * we iterate files and objects later, we can only support neither or both of
 * these fields.
 */
function parseTimeBounds(args)
{
	var timeBefore, timeAfter;

	/*
	 * Check the "before" and "after" fields.  Because of the way we iterate
	 * them, we can only support neither or both.
	 */
	if (args.timeAfter) {
		if (!args.timeBefore) {
			return (new VError(
			    '"after" requires specifying "before" too'));
		}

		timeAfter = new Date(args.timeAfter);
		if (isNaN(timeAfter.getTime()))
			return (new VError('"after": not a valid date: "%s"',
			    args.timeAfter));

		timeBefore = new Date(args.timeBefore);
		if (isNaN(timeBefore.getTime()))
			return (new VError('"before": not a valid date: "%s"',
			    args.timeBefore));

		if (timeAfter.getTime() > timeBefore.getTime())
			return (new VError('"after" timestamp may not ' +
			    'come after "before"'));
	} else if (args.timeBefore) {
		return (new VError('"before" requires specifying "after" too'));
	}

	return ({
	    'timeAfter': timeAfter,
	    'timeBefore': timeBefore
	});
}

/*
 * Parse an array of columns, as specified for both a query and an index.
 */
function parseFields(inputs, index, options)
{
	var fields, i, b, ret;

	fields = new Array(inputs.length);
	for (i = 0; i < inputs.length; i++) {
		b = inputs[i];
		ret = parseField(b, index, options);
		if (ret instanceof Error) {
			return (new VError(ret,
			    'field %d ("%s") is invalid', i, b));
		}

		fields[i] = ret;
	}

	return (fields);
}

function parseField(b, index, options)
{
	var i, j, kvpairs, rv, step;

	if (index) {
		if (typeof (b) != 'string')
			return (new Error('expected string'));

		if (!index.ic_columns_byname.hasOwnProperty(b))
			return (new VError('not found in index'));

		return (index.ic_columns_byname[b]);
	}

	if (typeof (b) != 'string') {
		if (b.hasOwnProperty('aggr')) {
			if (b['aggr'] != 'quantize' && b['aggr'] != 'lquantize')
				return (new VError(
				    'unsupported aggr: "%s"', b['aggr']));

			if (b['aggr'] == 'lquantize') {
				if (!b.hasOwnProperty('step'))
					return (new VError('aggr "lquantize" ' +
					    'requires "step"'));

				step = parseInt(b['step'], 10);
				if (isNaN(step))
					return (new VError('aggr "lquzntize":' +
					    ' invalid value for "step": "%s"',
					    b['step']));
				b['step'] = step;
			}
		}

		if ((!options || !options.allowReserved) &&
		    mod_jsprim.startsWith(b.name, '__dn'))
			return (new VError('field names starting with ' +
			    '"__dn" are reserved'));

		return (b);
	}

	i = b.indexOf('[');
	j = b.indexOf(']');
	if ((i == -1 && j != -1) ||
	    (i != -1 && j != b.length - 1))
		return (new VError('invalid field name'));

	rv = {};
	if (i == -1) {
		rv['name'] = b;
	} else {
		rv['name'] = b.substr(0, i);
		kvpairs = b.substr(i + 1, j - (i + 1)).split(';');
		kvpairs.forEach(function (kvpair) {
			var parts = kvpair.split('=');
			if (parts[0].trim().length === 0)
				return;
			rv[parts[0]] = parts[1] || '';
		});
	}

	if (!rv.hasOwnProperty('field'))
		rv['field'] = rv['name'];

	/* Use recursive call to validate. */
	return (parseField(rv, index));
}

function hasDateField(columns)
{
	for (var i = 0; i < columns.length; i++) {
		if (columns[i].hasOwnProperty('date'))
			return (true);
	}

	return (false);
}
<|endoftext|>


<commit_before>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length > 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<commit_msg>🤖(break): 2017-7-6 00:46:02<commit_after>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length < 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<|endoftext|>


<commit_before>'use strict';

var test = require('tape');

test('makes a rest call', function (t) {
	t.plan(2);

	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
	var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

	plotly.plot(data, layout, function (err, msg) {
		t.isEqual(msg.url, 'https://plot.ly/~node-test-account/0', 'url matches');
		t.notOk(err ,'no error');
		t.end();
	});
});

test('makes a rest call', function (t) {
	t.plan(2);

	var options = {
		username: 'node-test-account',
		apiKey: 'tpmz9ye8hg',
		host: 'plot.ly',
		port: 443
	};

	var plotly = require('../index')(options);
	var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
	var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

	plotly.plot(data, layout, function (err, msg) {
		t.isEqual(msg.url, 'https://plot.ly/~node-test-account/0', 'url matches');
		t.notOk(err ,'no error');
		t.end();
	});
});

test('makes a rest call', function (t) {
	t.plan(2);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
	var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

	plotly.plot(data, layout, function (err, msg) {
		t.isEqual(msg.url, 'https://plot.ly/~node-test-account/0', 'url matches');
		t.notOk(err, 'no error');
		t.end();
	});
});

test('plot with incorrect userdata and return error', function (t) {
	t.plan(1);

	var plotly = require('../index')('node-test-accountasdfadsgaghaha', 'tpmz9ye8hg');
	var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
	var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

	plotly.plot(data, layout, function (err, msg) {

		var errMessage = msg.message.split(',')[0];

		t.isEqual(errMessage, 'Aw', 'incorrect user info returns error... not properly though.');
		t.end();
	});
});

test('makes a rest call with host foo', function (t) {
	t.plan(1);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	plotly.host = 'foo';
	var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
	var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

	plotly.plot(data, layout, function (err) {
		t.ok(err, 'error received as host was set to "foo"');
		t.end();
	});
});

test('makes a rest call with no callback', function (t) {
	t.plan(1);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	plotly.host = 'foo';
	var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
	var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

	plotly.plot(data, layout);
	t.ok(true);
	t.end();

});

test('makes a rest call with object for data', function (t) {
	t.plan(1);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	plotly.host = 'foo';
	var data = {x:[0,1,2], y:[3,2,1], type: 'bar'};
	var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

	plotly.plot(data, layout);
	t.ok(true);
	t.end();

});

test('getFigure', function (t) {
	t.plan(1);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	plotly.getFigure('node-test-account', '0', function (err, figure) {
		t.ok(figure);
		t.end();
	});

});

test('getFigure error', function (t) {
	t.plan(1);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	plotly.getFigure('node-test-account', '99', function (err) {

		t.ok(err);
		t.end();
	});
});

test('getImage, good and error', function (t) {
	t.plan(2);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');

	var trace1 = {
	  x: [1, 2, 3, 4],
	  y: [10, 15, 13, 17],
	  type: 'scatter'
	};

	var trace2 = {
	  x: [1, 2, 3, 4],
	  y: [16, 5, 11, 9],
	  type: 'scatter'
	};

	var figure = {
	    'data': [trace1, trace2]
	};

	plotly.getImage(figure, {}, function (err, imageData) {
		t.notOk(err);
		t.ok(imageData);
		t.end();
	});
});

test('getImage, imageserver error', function (t) {
	t.plan(2);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');

	var trace1 = {
	  x: [1, 2, 3, 4],
	  y: [10, 15, 13, 17],
	  type: 'scatter'
	};

	var trace2 = {
	  x: [1, 2, 3, 4],
	  y: [16, 5, 11, 9],
	  type: 'scatter'
	};

	var data = [trace1, trace2];
	plotly.getImage(data, 'img', function (err, imageData) {
		t.ok(err);
		t.notOk(imageData);
		t.end();
	});

});

test('streams some data', function (t) {
	t.plan(1);
	var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
	var initdata = [{x:[], y:[], stream:{token:'i9zxn8goas', maxpoints:200}}];
	var initlayout = {fileopt : 'extend', filename : 'nodenodenode-test-stream'};

	plotly.plot(initdata, initlayout, function (err) {
		if (err) return console.log(err);

		var streamObject = JSON.stringify({ x : 1, y : 1 });

		var stream = plotly.stream({
			token: 'i9zxn8goas',
			host: undefined,
			port: null
		});

		setInterval(function () {
			stream.write(streamObject+'\n');
		}, 500);

		setTimeout(function () {
			t.ok(true, 'no errors');
			t.end();
			process.exit(0);
		}, 5000);
	});
});
<commit_msg>update test with UTF8 filename example<commit_after>'use strict';

var test = require('tape');

test('makes a rest call', function (t) {
    t.plan(2);

    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
    var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

    plotly.plot(data, layout, function (err, msg) {
        t.isEqual(msg.url, 'https://plot.ly/~node-test-account/0', 'url matches');
        t.notOk(err ,'no error');
        t.end();
    });
});

test('makes a rest call', function (t) {
    t.plan(2);

    var options = {
        username: 'node-test-account',
        apiKey: 'tpmz9ye8hg',
        host: 'plot.ly',
        port: 443
    };

    var plotly = require('../index')(options);
    var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
    var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

    plotly.plot(data, layout, function (err, msg) {
        t.isEqual(msg.url, 'https://plot.ly/~node-test-account/0', 'url matches');
        t.notOk(err ,'no error');
        t.end();
    });
});

test('makes a rest call', function (t) {
    t.plan(2);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
    var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

    plotly.plot(data, layout, function (err, msg) {
        t.isEqual(msg.url, 'https://plot.ly/~node-test-account/0', 'url matches');
        t.notOk(err, 'no error');
        t.end();
    });
});

test('plot with incorrect userdata and return error', function (t) {
    t.plan(1);

    var plotly = require('../index')('node-test-accountasdfadsgaghaha', 'tpmz9ye8hg');
    var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
    var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

    plotly.plot(data, layout, function (err, msg) {

        var errMessage = msg.message.split(',')[0];

        t.isEqual(errMessage, 'Aw', 'incorrect user info returns error... not properly though.');
        t.end();
    });
});

test('makes a rest call with host foo', function (t) {
    t.plan(1);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    plotly.host = 'foo';
    var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
    var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

    plotly.plot(data, layout, function (err) {
        t.ok(err, 'error received as host was set to "foo"');
        t.end();
    });
});

test('makes a rest call with no callback', function (t) {
    t.plan(1);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    plotly.host = 'foo';
    var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
    var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

    plotly.plot(data, layout);
    t.ok(true);
    t.end();

});

test('makes a rest call with object for data', function (t) {
    t.plan(1);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    plotly.host = 'foo';
    var data = {x:[0,1,2], y:[3,2,1], type: 'bar'};
    var layout = {fileopt : 'overwrite', filename : 'nodenodenodetest'};

    plotly.plot(data, layout);
    t.ok(true);
    t.end();

});

test('getFigure', function (t) {
    t.plan(1);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    plotly.getFigure('node-test-account', '0', function (err, figure) {
        t.ok(figure);
        t.end();
    });

});

test('getFigure error', function (t) {
    t.plan(1);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    plotly.getFigure('node-test-account', '99', function (err) {

        t.ok(err);
        t.end();
    });
});

test('getImage, good and error', function (t) {
    t.plan(2);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');

    var trace1 = {
        x: [1, 2, 3, 4],
        y: [10, 15, 13, 17],
        type: 'scatter'
    };

    var trace2 = {
        x: [1, 2, 3, 4],
        y: [16, 5, 11, 9],
        type: 'scatter'
    };

    var figure = {
        'data': [trace1, trace2]
    };

    plotly.getImage(figure, {}, function (err, imageData) {
        t.notOk(err);
        t.ok(imageData);
        t.end();
    });
});

test('getImage, imageserver error', function (t) {
    t.plan(2);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');

    var trace1 = {
        x: [1, 2, 3, 4],
        y: [10, 15, 13, 17],
        type: 'scatter'
    };

    var trace2 = {
        x: [1, 2, 3, 4],
        y: [16, 5, 11, 9],
        type: 'scatter'
    };

    var data = [trace1, trace2];
    plotly.getImage(data, 'img', function (err, imageData) {
        t.ok(err);
        t.notOk(imageData);
        t.end();
    });

});

test('creates a plot with UTF chars in filename', function (t) {
	t.plan(1);

    var options = {
        username: 'node-test-account',
        apiKey: 'tpmz9ye8hg',
        host: 'plot.ly',
        port: 443
    };

    var plotly = require('../index')(options);
    var data = [{x:[0,1,2], y:[3,2,1], type: 'bar'}];
    var layout = {fileopt : 'overwrite', filename : 'üüüü'};

    plotly.plot(data, layout, function (err, msg) {
        t.notOk(err ,'no error');
        t.end();
    });

});

test('streams some data', function (t) {
    t.plan(1);
    var plotly = require('../index')('node-test-account', 'tpmz9ye8hg');
    var initdata = [{x:[], y:[], stream:{token:'i9zxn8goas', maxpoints:200}}];
    var initlayout = {fileopt : 'extend', filename : 'nodenodenode-test-stream'};

    plotly.plot(initdata, initlayout, function (err) {
        if (err) return console.log(err);

        var streamObject = JSON.stringify({ x : 1, y : 1 });

        var stream = plotly.stream({
            token: 'i9zxn8goas',
            host: undefined,
            port: null
        });

        setInterval(function () {
            stream.write(streamObject+'\n');
        }, 500);

        setTimeout(function () {
            t.ok(true, 'no errors');
            t.end();
            process.exit(0);
        }, 5000);
    });
});
<|endoftext|>


<commit_before>var EventEmitter, e, objToString, isArray, slice, ref$;
try {
  EventEmitter = require('events').EventEmitter;
} catch (e$) {
  e = e$;
  objToString = Object.prototype.toString;
  isArray = Array.isArray || function(o){
    return objToString.call(o) === '[object Array]';
  };
  slice = [].slice;
  EventEmitter = (function(){
    EventEmitter.displayName = 'EventEmitter';
    var prototype = EventEmitter.prototype, constructor = EventEmitter;
    prototype.emit = function(type){
      var arg, ref$, queue, listener, args, i$, len$;
      arg = arguments[1];
      if (type === 'error' && !((ref$ = this._events) != null && ref$.error) || (isArray(this._events.error) && !this._events.error.length)) {
        if (arg instanceof Error) {
          throw arg;
        } else {
          throw new Error("Uncaught, unspecified 'error' event.");
        }
        return false;
      }
      queue = (ref$ = this._events) != null ? ref$[type] : void 8;
      if (typeof queue === 'function') {
        listener = queue;
        switch (arguments.length) {
        case 1:
          listener.call(this);
          break;
        case 2:
          listener.call(this, arg);
          break;
        case 3:
          listener.call(this, arg, arguments[2]);
          break;
        default:
          listener.apply(this, slice.call(arguments, 1));
        }
      } else if (isArray(queue)) {
        args = slice.call(arguments, 1);
        for (i$ = 0, len$ = queue.length; i$ < len$; ++i$) {
          listener = queue[i$];
          listener.apply(this, args);
        }
      } else {
        return false;
      }
      return true;
    };
    prototype.on = function(type, listener){
      var events, queue;
      if (typeof listener !== 'function') {
        throw new TypeError("addListener only takes instances of Function");
      }
      this.emit('newListener', type, typeof listener.listener === 'function' ? listener.listener : listener);
      events = this._events || (this._events = {});
      if (!(queue = events[type])) {
        events[type] = listener;
      } else if (!isArray(queue)) {
        events[type] = [queue, listener];
      } else {
        (events[type] = queue.slice()).push(listener);
      }
      return this;
    };
    prototype.addListener = EventEmitter.prototype.on;
    prototype.removeListener = function(type, listener){
      var ref$, queue, idx, i, len$, fn;
      if (typeof listener !== 'function') {
        throw new TypeError("addListener only takes instances of Function");
      }
      queue = (ref$ = this._events) != null ? ref$[type] : void 8;
      if (!queue) {
        return this;
      }
      if (typeof queue === 'function') {
        if (queue === listener || queue.listener === listener) {
          delete this._events[type];
        }
        return this;
      }
      idx = -1;
      for (i = 0, len$ = queue.length; i < len$; ++i) {
        fn = queue[i];
        if (fn === listener || fn.listener === listener) {
          idx = i;
          break;
        }
      }
      if (idx < 0) {
        return this;
      }
      if (queue.length === 1) {
        delete this._events[type];
      } else {
        queue = this._events[type] = queue.slice();
        queue.splice(idx, 1);
      }
      return this;
    };
    prototype.removeAllListeners = function(type){
      var ref$;
      if (arguments.length === 0) {
        this._events = {};
        return this;
      }
      if ((ref$ = this._events) != null && ref$[type]) {
        delete this._events[type];
      }
      return this;
    };
    prototype.once = function(type, listener){
      var self, _once;
      if (typeof listener !== 'function') {
        throw new TypeError("once only takes instances of Function");
      }
      self = this;
      _once = function(){
        self.removeListener(type, _once);
        listener.apply(this, arguments);
      };
      _once.listener = listener;
      return this.on(type, _once);
    };
    prototype.listeners = function(type){
      var events, queue;
      events = this._events || (this._events = {});
      queue = events[type] || (events[type] = []);
      if (!isArray(queue)) {
        queue = events[type] = [events[type]];
      }
      return queue;
    };
    prototype.setMaxListeners = function(n){
      return console.debug('setMaxListeners is unsupported');
    };
    function EventEmitter(){}
    return EventEmitter;
  }());
}
(ref$ = EventEmitter.prototype).trigger || (ref$.trigger = EventEmitter.prototype.emit);
(ref$ = EventEmitter.prototype).off || (ref$.off = EventEmitter.prototype.removeListener);
exports.EventEmitter = EventEmitter;<commit_msg>Rebuild.<commit_after>var EventEmitter, e, objToString, isArray, slice, ref$;
try {
  EventEmitter = require('events').EventEmitter;
  EventEmitter.displayName || (EventEmitter.displayName = 'EventEmitter');
} catch (e$) {
  e = e$;
  objToString = Object.prototype.toString;
  isArray = Array.isArray || function(o){
    return objToString.call(o) === '[object Array]';
  };
  slice = [].slice;
  EventEmitter = (function(){
    EventEmitter.displayName = 'EventEmitter';
    var prototype = EventEmitter.prototype, constructor = EventEmitter;
    prototype.emit = function(type){
      var arg, ref$, queue, listener, args, i$, len$;
      arg = arguments[1];
      if (type === 'error' && !((ref$ = this._events) != null && ref$.error) || (isArray(this._events.error) && !this._events.error.length)) {
        if (arg instanceof Error) {
          throw arg;
        } else {
          throw new Error("Uncaught, unspecified 'error' event.");
        }
        return false;
      }
      queue = (ref$ = this._events) != null ? ref$[type] : void 8;
      if (typeof queue === 'function') {
        listener = queue;
        switch (arguments.length) {
        case 1:
          listener.call(this);
          break;
        case 2:
          listener.call(this, arg);
          break;
        case 3:
          listener.call(this, arg, arguments[2]);
          break;
        default:
          listener.apply(this, slice.call(arguments, 1));
        }
      } else if (isArray(queue)) {
        args = slice.call(arguments, 1);
        for (i$ = 0, len$ = queue.length; i$ < len$; ++i$) {
          listener = queue[i$];
          listener.apply(this, args);
        }
      } else {
        return false;
      }
      return true;
    };
    prototype.on = function(type, listener){
      var events, queue;
      if (typeof listener !== 'function') {
        throw new TypeError("addListener only takes instances of Function");
      }
      this.emit('newListener', type, typeof listener.listener === 'function' ? listener.listener : listener);
      events = this._events || (this._events = {});
      if (!(queue = events[type])) {
        events[type] = listener;
      } else if (!isArray(queue)) {
        events[type] = [queue, listener];
      } else {
        (events[type] = queue.slice()).push(listener);
      }
      return this;
    };
    prototype.addListener = EventEmitter.prototype.on;
    prototype.removeListener = function(type, listener){
      var queue, ref$, idx, i, len$, fn;
      if (typeof listener !== 'function') {
        throw new TypeError("addListener only takes instances of Function");
      }
      queue = (ref$ = this._events) != null ? ref$[type] : void 8;
      if (!queue) {
        return this;
      }
      if (typeof queue === 'function') {
        if (queue === listener || queue.listener === listener) {
          delete this._events[type];
        }
        return this;
      }
      idx = -1;
      for (i = 0, len$ = queue.length; i < len$; ++i) {
        fn = queue[i];
        if (fn === listener || fn.listener === listener) {
          idx = i;
          break;
        }
      }
      if (idx < 0) {
        return this;
      }
      if (queue.length === 1) {
        delete this._events[type];
      } else {
        queue = this._events[type] = queue.slice();
        queue.splice(idx, 1);
      }
      return this;
    };
    prototype.removeAllListeners = function(type){
      var ref$;
      if (arguments.length === 0) {
        this._events = {};
        return this;
      }
      if ((ref$ = this._events) != null && ref$[type]) {
        delete this._events[type];
      }
      return this;
    };
    prototype.once = function(type, listener){
      var self, _once;
      if (typeof listener !== 'function') {
        throw new TypeError("once only takes instances of Function");
      }
      self = this;
      _once = function(){
        self.removeListener(type, _once);
        listener.apply(this, arguments);
      };
      _once.listener = listener;
      return this.on(type, _once);
    };
    prototype.listeners = function(type){
      var events, queue;
      events = this._events || (this._events = {});
      queue = events[type] || (events[type] = []);
      if (!isArray(queue)) {
        queue = events[type] = [events[type]];
      }
      return queue;
    };
    prototype.setMaxListeners = function(n){
      return console.debug('setMaxListeners is unsupported');
    };
    function EventEmitter(){}
    return EventEmitter;
  }());
}
(ref$ = EventEmitter.prototype).trigger || (ref$.trigger = EventEmitter.prototype.emit);
(ref$ = EventEmitter.prototype).off || (ref$.off = EventEmitter.prototype.removeListener);
exports.EventEmitter = EventEmitter;<|endoftext|>


<commit_before>var
	_         = require('lodash'),
	assert    = require('assert'),
	events    = require('events'),
	net       = require('net'),
	os        = require('os'),
	stream    = require('readable-stream'),
	UDPStream = require('./udp-stream'),
	url       = require('url'),
	util      = require('util')
;

var Emitter = module.exports = function Emitter(opts)
{
	assert(opts && _.isObject(opts), 'you must pass an options object to the Emitter constructor');
	assert((opts.host && opts.port) || opts.path || opts.uri, 'you must pass uri, path, or a host/port pair for the collector');
	if (opts.node && !opts.app) opts.app = opts.node;
	assert(opts.app && _.isString(opts.app), 'you must pass an `app` option naming this service or app');

	events.EventEmitter.call(this);

	if (opts.uri) Emitter.parseURI(opts);
	if (opts.maxretries) this.maxretries = parseInt(opts.maxretries, 10);
	if (opts.maxbacklog) this.maxbacklog = parseInt(opts.maxbacklog, 10);

	this.options = opts;
	this.defaults = {};
	this.defaults.host = os.hostname();
	this.defaults.app = opts.app;
	this.backlog = [];
	this.output = new JSONOutputStream();

	this.connect();
};
util.inherits(Emitter, events.EventEmitter);

Emitter.prototype.defaults = null;
Emitter.prototype.backlog  = null;
Emitter.prototype.maxbacklog = 1000;
Emitter.prototype.client   = null;
Emitter.prototype.ready    = false;
Emitter.prototype.retries  = 0;
Emitter.prototype.maxretries = 100;
Emitter.prototype.maxbacklog = 1000;

Emitter.parseURI = function(options)
{
	var parsed = url.parse(options.uri);

	switch (parsed.protocol)
	{
		case 'udp:':
			options.udp = true;
			options.host = parsed.hostname;
			options.port = parsed.port;
			break;

		case 'sock:':
		case 'socket:':
			options.path = parsed.pathname;
			break;

		case 'tcp:':
			options.host = parsed.hostname;
			options.port = parsed.port;
			break;

		default:
			throw(new Error('unsupported destination uri: ' + options.uri));
	}

	delete options.uri;
	return options;
};

Emitter.prototype.connect = function connect()
{
	if (this.ready) return; // TODO might throw an error instead

	if (this.client)
	{
		this.client.removeAllListeners();
		this.output.unpipe();
		this.client = null;
	}

	if (this.options.udp)
	{
		this.client = new UDPStream(this.options);
	}
	else
	{
		this.client = net.connect(this.options);
	}

	this.output.pipe(this.client);
	this.client.on('connect', this.onConnect.bind(this));
	this.client.on('error', this.onError.bind(this));
	this.client.on('close', this.onClose.bind(this));
};

Emitter.prototype.destroy = function destroy()
{
	if (!this.client) return;
	this.output.unpipe();
	this.client.removeAllListeners();
	this.client.end();
	this.client = null;
};

Emitter.prototype.onConnect = function onConnect()
{
	while (this.backlog.length)
		this._write(this.backlog.shift());
	this.retries = 0;
	this.ready = true;
	this.emit('ready');
};

Emitter.prototype.onError = function onError(err)
{
	this.ready = false;
};

Emitter.prototype.onClose = function onClose()
{
	this.ready = false;
	this.retries++;
	if (this.retries <= this.maxretries)
		setTimeout(this.connect.bind(this), this.nextBackoff());
	else
		this.emit('failed', 'retried ' + this.retries + ' times; giving up');
	this.emit('close');
};

Emitter.prototype.nextBackoff = function nextBackoff()
{
	return Math.min((Math.random() + 1) * 10 * Math.pow(2, this.retries), 60000);
};

Emitter.prototype.makeEvent = function makeEvent(attrs)
{
	assert(attrs && _.isObject(attrs), 'you cannot make an empty event');
	assert(attrs.name, 'you must give your metric a name');

	var event = {};
	_.defaults(event, attrs, this.defaults);
	if (!event.time) event.time = Date.now(); // milliseconds!
	if (!event.value) event.value = 1;

	return event;
};

Emitter.prototype._write = function _write(event, encoding, callback)
{
	var payload = event;
	if (_.isObject(event))
		payload = JSON.stringify(event) + '\n';

	this.output.write(payload);
};

Emitter.prototype.metric = function metric(attrs)
{
	var event = this.makeEvent(attrs);
	if (this.ready)
		this._write(event);
	else
		this.backlog.push(event);

	while (this.backlog.length > this.maxbacklog)
		this.backlog.shift();
};

function JSONOutputStream()
{
	stream.Transform.call(this);
	this._readableState.objectMode = false;
	this._writableState.objectMode = true;
}
util.inherits(JSONOutputStream, stream.Transform);

JSONOutputStream.prototype._transform = function _transformOut(object, encoding, callback)
{
	this.push(object);
	callback();
};
<commit_msg>Node name is distinct from app name. E.g., "www" vs "www:8081". To distinguish processes on the same host.<commit_after>var
	_         = require('lodash'),
	assert    = require('assert'),
	events    = require('events'),
	net       = require('net'),
	os        = require('os'),
	stream    = require('readable-stream'),
	UDPStream = require('./udp-stream'),
	url       = require('url'),
	util      = require('util')
;

var Emitter = module.exports = function Emitter(opts)
{
	assert(opts && _.isObject(opts), 'you must pass an options object to the Emitter constructor');
	assert((opts.host && opts.port) || opts.path || opts.uri, 'you must pass uri, path, or a host/port pair for the collector');
	if (opts.node && !opts.app) opts.app = opts.node;
	assert(opts.app && _.isString(opts.app), 'you must pass an `app` option naming this service or app');

	events.EventEmitter.call(this);

	if (opts.uri) Emitter.parseURI(opts);
	if (opts.maxretries) this.maxretries = parseInt(opts.maxretries, 10);
	if (opts.maxbacklog) this.maxbacklog = parseInt(opts.maxbacklog, 10);

	this.options = opts;
	this.defaults = {};
	this.defaults.host = os.hostname();
	this.defaults.app = opts.app;
	this.defaults.node = opts.node;
	this.backlog = [];
	this.output = new JSONOutputStream();

	this.connect();
};
util.inherits(Emitter, events.EventEmitter);

Emitter.prototype.defaults = null;
Emitter.prototype.backlog  = null;
Emitter.prototype.maxbacklog = 1000;
Emitter.prototype.client   = null;
Emitter.prototype.ready    = false;
Emitter.prototype.retries  = 0;
Emitter.prototype.maxretries = 100;
Emitter.prototype.maxbacklog = 1000;

Emitter.parseURI = function(options)
{
	var parsed = url.parse(options.uri);

	switch (parsed.protocol)
	{
		case 'udp:':
			options.udp = true;
			options.host = parsed.hostname;
			options.port = parsed.port;
			break;

		case 'sock:':
		case 'socket:':
			options.path = parsed.pathname;
			break;

		case 'tcp:':
			options.host = parsed.hostname;
			options.port = parsed.port;
			break;

		default:
			throw(new Error('unsupported destination uri: ' + options.uri));
	}

	delete options.uri;
	return options;
};

Emitter.prototype.connect = function connect()
{
	if (this.ready) return; // TODO might throw an error instead

	if (this.client)
	{
		this.client.removeAllListeners();
		this.output.unpipe();
		this.client = null;
	}

	if (this.options.udp)
	{
		this.client = new UDPStream(this.options);
	}
	else
	{
		this.client = net.connect(this.options);
	}

	this.output.pipe(this.client);
	this.client.on('connect', this.onConnect.bind(this));
	this.client.on('error', this.onError.bind(this));
	this.client.on('close', this.onClose.bind(this));
};

Emitter.prototype.destroy = function destroy()
{
	if (!this.client) return;
	this.output.unpipe();
	this.client.removeAllListeners();
	this.client.end();
	this.client = null;
};

Emitter.prototype.onConnect = function onConnect()
{
	while (this.backlog.length)
		this._write(this.backlog.shift());
	this.retries = 0;
	this.ready = true;
	this.emit('ready');
};

Emitter.prototype.onError = function onError(err)
{
	this.ready = false;
};

Emitter.prototype.onClose = function onClose()
{
	this.ready = false;
	this.retries++;
	if (this.retries <= this.maxretries)
		setTimeout(this.connect.bind(this), this.nextBackoff());
	else
		this.emit('failed', 'retried ' + this.retries + ' times; giving up');
	this.emit('close');
};

Emitter.prototype.nextBackoff = function nextBackoff()
{
	return Math.min((Math.random() + 1) * 10 * Math.pow(2, this.retries), 60000);
};

Emitter.prototype.makeEvent = function makeEvent(attrs)
{
	assert(attrs && _.isObject(attrs), 'you cannot make an empty event');
	assert(attrs.name, 'you must give your metric a name');

	var event = {};
	_.defaults(event, attrs, this.defaults);
	if (!event.time) event.time = Date.now(); // milliseconds!
	if (!event.value) event.value = 1;

	return event;
};

Emitter.prototype._write = function _write(event, encoding, callback)
{
	var payload = event;
	if (_.isObject(event))
		payload = JSON.stringify(event) + '\n';

	this.output.write(payload);
};

Emitter.prototype.metric = function metric(attrs)
{
	var event = this.makeEvent(attrs);
	if (this.ready)
		this._write(event);
	else
		this.backlog.push(event);

	while (this.backlog.length > this.maxbacklog)
		this.backlog.shift();
};

function JSONOutputStream()
{
	stream.Transform.call(this);
	this._readableState.objectMode = false;
	this._writableState.objectMode = true;
}
util.inherits(JSONOutputStream, stream.Transform);

JSONOutputStream.prototype._transform = function _transformOut(object, encoding, callback)
{
	this.push(object);
	callback();
};
<|endoftext|>