├── .gitignore ├── index.js ├── Makefile ├── test ├── run.js ├── common.js ├── unit │ ├── test-CarbonClient.js │ ├── test-LazySocket.js │ └── test-GraphiteClient.js ├── integration │ ├── test-regular-connect-write-disconnect.js │ ├── test-lazy-socket-failed-then-working-write.js │ ├── test-send.js │ └── test-lazy-socket-connection-interrupt.js └── helper │ └── CarbonServer.js ├── .travis.yml ├── package.json ├── lib ├── CarbonClient.js ├── GraphiteClient.js └── LazySocket.js ├── .github └── workflows │ └── node.js.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/GraphiteClient'); 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | test: 4 | @node test/run.js $(type) 5 | 6 | .PHONY: test 7 | -------------------------------------------------------------------------------- /test/run.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | require('urun')(path.join(__dirname, process.argv[2])); 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 4 4 | - 6 5 | - 8 6 | - 10 7 | - 12 8 | - 14 9 | - 16 10 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | var common = exports; 2 | var path = require('path'); 3 | 4 | common.dir = {} 5 | common.dir.root = path.dirname(__dirname); 6 | common.dir.lib = path.join(common.dir.root, 'lib'); 7 | 8 | common.graphite = require(common.dir.root); 9 | common.port = 12523; 10 | common.carbonDsn = 'plaintext://localhost:' + common.port + '/'; 11 | -------------------------------------------------------------------------------- /test/unit/test-CarbonClient.js: -------------------------------------------------------------------------------- 1 | var common = require('../common'); 2 | var test = require('utest'); 3 | var assert = require('assert'); 4 | var sinon = require('sinon'); 5 | var CarbonClient = require('../../lib/CarbonClient'); 6 | 7 | test('CarbonClient#end', { 8 | 'closes socket if it has one': function() { 9 | var socket = sinon.stub({end: function() {}}); 10 | var client = new CarbonClient({socket: socket}); 11 | 12 | client.end(); 13 | 14 | assert.equal(socket.end.callCount, 1); 15 | }, 16 | 17 | 'does not crash if it has no socket': function() { 18 | var client = new CarbonClient(); 19 | client.end(); 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Felix Geisendörfer (http://debuggable.com/)", 3 | "name": "graphite", 4 | "description": "A node.js client for graphite.", 5 | "version": "0.1.5", 6 | "homepage": "https://github.com/felixge/node-graphite", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/felixge/node-graphite.git" 10 | }, 11 | "main": "./index", 12 | "scripts": { 13 | "test": "npm run test:unit && npm run test:integration", 14 | "test:unit": "make test type=unit", 15 | "test:integration": "make test type=integration" 16 | }, 17 | "engines": { 18 | "node": ">=4" 19 | }, 20 | "devDependencies": { 21 | "sinon": "~7.1.1", 22 | "urun": "0.0.8", 23 | "utest": "0.0.8" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/integration/test-regular-connect-write-disconnect.js: -------------------------------------------------------------------------------- 1 | var common = require('../common'); 2 | var assert = require('assert'); 3 | var net = require('net'); 4 | var LazySocket = require('../../lib/LazySocket'); 5 | var data = ''; 6 | 7 | var server = net.createServer(function(socket) { 8 | socket 9 | .on('data', function(chunk) { 10 | data += chunk; 11 | }) 12 | .on('end', function() { 13 | server.close(); 14 | }); 15 | }); 16 | 17 | server.listen(common.port, function() { 18 | var socket = LazySocket.createConnection(common.port); 19 | socket.write('high ', 'utf-8'); 20 | socket.write('five', 'utf-8', function(err) { 21 | assert.ok(!err); 22 | socket.end(); 23 | }); 24 | }); 25 | 26 | process.on('exit', function() { 27 | assert.equal(data, 'high five'); 28 | }); 29 | -------------------------------------------------------------------------------- /test/integration/test-lazy-socket-failed-then-working-write.js: -------------------------------------------------------------------------------- 1 | var common = require('../common'); 2 | var assert = require('assert'); 3 | var net = require('net'); 4 | var LazySocket = require('../../lib/LazySocket'); 5 | var data = ''; 6 | 7 | var server = net.createServer(function(socket) { 8 | socket 9 | .on('data', function(chunk) { 10 | data += chunk; 11 | }) 12 | .on('end', function() { 13 | server.close(); 14 | }); 15 | }); 16 | 17 | var socket = LazySocket.createConnection(common.port); 18 | 19 | var connectError; 20 | socket.write('high', 'utf-8', function(err) { 21 | connectError = err; 22 | 23 | server.listen(common.port, function() { 24 | socket.write('five', 'utf-8', function(err) { 25 | assert.ok(!err); 26 | socket.end(); 27 | }); 28 | }); 29 | 30 | }); 31 | 32 | process.on('exit', function() { 33 | assert.ok(connectError); 34 | assert.equal(data, 'five'); 35 | }); 36 | -------------------------------------------------------------------------------- /lib/CarbonClient.js: -------------------------------------------------------------------------------- 1 | var LazySocket = require('./LazySocket'); 2 | var url = require('url'); 3 | 4 | module.exports = CarbonClient; 5 | function CarbonClient(properties) { 6 | properties = properties || {}; 7 | 8 | this._dsn = properties.dsn; 9 | this._socket = properties.socket || null; 10 | } 11 | 12 | CarbonClient.prototype.write = function(metrics, timestamp, cb) { 13 | this._lazyConnect(); 14 | 15 | var lines = ''; 16 | for (var path in metrics) { 17 | var value = metrics[path]; 18 | lines += [path, value, timestamp].join(' ') + '\n'; 19 | } 20 | 21 | this._socket.write(lines, 'utf-8', cb); 22 | }; 23 | 24 | CarbonClient.prototype._lazyConnect = function() { 25 | if (this._socket) return; 26 | 27 | var dsn = url.parse(this._dsn); 28 | var port = parseInt(dsn.port, 10) || 2003; 29 | var host = dsn.hostname; 30 | 31 | this._socket = LazySocket.createConnection(port, host); 32 | }; 33 | 34 | CarbonClient.prototype.end = function() { 35 | if (this._socket) this._socket.end(); 36 | }; 37 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x, 16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - name: Install dependencies 29 | run: npm install 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /test/helper/CarbonServer.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | 3 | module.exports = CarbonServer; 4 | function CarbonServer() { 5 | this.metrics = []; 6 | this._server = null; 7 | } 8 | 9 | CarbonServer.prototype.listen = function(port, cb) { 10 | if (!this._server) { 11 | this._server = net.createServer(this._handleSocket.bind(this)); 12 | } 13 | 14 | this._server.listen(port, cb); 15 | }; 16 | 17 | CarbonServer.prototype.close = function() { 18 | this._server.close(); 19 | }; 20 | 21 | CarbonServer.prototype._handleSocket = function(socket) { 22 | var self = this; 23 | var buffer = ''; 24 | 25 | socket.setEncoding('utf8'); 26 | socket 27 | .on('data', function(chunk) { 28 | buffer += chunk; 29 | 30 | while (buffer.length) { 31 | var index = buffer.indexOf('\n'); 32 | 33 | if (index === -1) break; 34 | 35 | var line = buffer.substr(0, index); 36 | buffer = buffer.substr(index + 1); 37 | 38 | self._parseLine(line); 39 | } 40 | }) 41 | .on('end', function() { 42 | self.close(); 43 | }); 44 | }; 45 | 46 | CarbonServer.prototype._parseLine = function(line) { 47 | var parts = line.split(/ /g); 48 | this.metrics.push({ 49 | path : parts[0], 50 | value : parts[1], 51 | timestamp : parseInt(parts[2], 10), 52 | }); 53 | }; 54 | -------------------------------------------------------------------------------- /test/integration/test-send.js: -------------------------------------------------------------------------------- 1 | var common = require('../common'); 2 | var assert = require('assert'); 3 | var graphite = common.graphite; 4 | var CarbonServer = require('../helper/CarbonServer'); 5 | 6 | var server = new CarbonServer(); 7 | server.listen(common.port, function() { 8 | var client = graphite.createClient(common.carbonDsn); 9 | var metrics = { 10 | foo: 1, 11 | deep: { 12 | down: { 13 | a: 2, 14 | b: 3, 15 | } 16 | } 17 | }; 18 | 19 | client.write(metrics, function(err) { 20 | assert.ok(!err); 21 | 22 | client.end(); 23 | }); 24 | }); 25 | 26 | process.on('exit', function() { 27 | var metric = server.metrics.shift(); 28 | assert.equal(metric.path, 'foo'); 29 | assert.equal(metric.value, 1); 30 | assert.ok(metric.timestamp + 1 >= Date.now() / 1000); 31 | assert.ok(metric.timestamp - 1 <= Date.now() / 1000); 32 | 33 | metric = server.metrics.shift(); 34 | assert.equal(metric.path, 'deep.down.a'); 35 | assert.equal(metric.value, 2); 36 | assert.ok(metric.timestamp + 1 >= Date.now() / 1000); 37 | assert.ok(metric.timestamp - 1 <= Date.now() / 1000); 38 | 39 | metric = server.metrics.shift(); 40 | assert.equal(metric.path, 'deep.down.b'); 41 | assert.equal(metric.value, 3); 42 | assert.ok(metric.timestamp + 1 >= Date.now() / 1000); 43 | assert.ok(metric.timestamp - 1 <= Date.now() / 1000); 44 | }); 45 | 46 | -------------------------------------------------------------------------------- /test/integration/test-lazy-socket-connection-interrupt.js: -------------------------------------------------------------------------------- 1 | var common = require('../common'); 2 | var assert = require('assert'); 3 | var net = require('net'); 4 | var LazySocket = require('../../lib/LazySocket'); 5 | var data = ''; 6 | 7 | var num = 0; 8 | var server = net.createServer(function(socket) { 9 | socket 10 | .on('data', function(chunk) { 11 | data += chunk; 12 | }); 13 | 14 | num++; 15 | if (num === 1) { 16 | socket 17 | .on('end', sendSecondMessage) 18 | .end(); 19 | 20 | server.close(); 21 | } 22 | 23 | if (num === 2) { 24 | socket.on('end', function() { 25 | server.close(); 26 | }); 27 | } 28 | }); 29 | 30 | server.listen(common.port, sendFirstMessage); 31 | 32 | var socket = LazySocket.createConnection(common.port); 33 | function sendFirstMessage() { 34 | server.removeAllListeners('listening') 35 | socket.write('first', 'utf-8', function(err) { 36 | assert.ok(!err); 37 | }); 38 | } 39 | 40 | function sendSecondMessage() { 41 | socket.write('second ', 'utf-8', function(err) { 42 | assert.ok(err); 43 | server.listen(common.port, sendThirdMessage); 44 | }); 45 | } 46 | 47 | function sendThirdMessage() { 48 | socket.write('third', 'utf-8', function(err) { 49 | assert.ok(!err); 50 | socket.end(); 51 | }); 52 | } 53 | 54 | process.on('exit', function() { 55 | assert.equal(data, 'firstthird'); 56 | }); 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graphite 2 | 3 | [![Node.js CI](https://github.com/felixge/node-graphite/actions/workflows/node.js.yml/badge.svg)](https://github.com/felixge/node-graphite/actions/workflows/node.js.yml) 4 | 5 | A node.js client for graphite. 6 | 7 | ## Install 8 | 9 | ``` 10 | npm install graphite 11 | ``` 12 | 13 | ## Usage 14 | 15 | You first have to define the Graphite client: 16 | 17 | ```js 18 | var graphite = require('graphite'); 19 | var client = graphite.createClient('plaintext://graphite.example.org:2003/'); 20 | ``` 21 | 22 | You can send metrics without a timestamp. The current Unix epoch will then be used in place: 23 | 24 | ```js 25 | var metrics = {foo: 23}; 26 | client.write(metrics, function(err) { 27 | // if err is null, your data was sent to graphite! 28 | }); 29 | ``` 30 | 31 | If you wish to set your own timestamp, you must use `Date.now()` (millisecond precision) as parameter and not `Math.floor(Date.now() / 1000)` (second precision), otherwise your metrics will probably get ignored by Graphite: 32 | 33 | ```js 34 | var metrics = {foo: 23}; 35 | var timestamp = Date.now(); 36 | client.write(metrics, timestamp, function(err) { 37 | // if err is null, your data was sent to graphite! 38 | }); 39 | ``` 40 | 41 | In Graphite [1.1.1 (21.12.17)](http://graphite.readthedocs.io/en/latest/releases/1_1_1.html), [tagging](http://graphite.readthedocs.io/en/latest/tags.html) becomes available. You can send tagged metrics as follows: 42 | 43 | ```js 44 | var metrics = {foo: 23}; 45 | var tags = {'name': 'foo.bar', 'some.fancy.tag': 'somefancyvalue'}; 46 | client.writeTagged(metrics, tags, function(err) { 47 | // if err is null, your data was sent to graphite! 48 | }); 49 | ``` 50 | 51 | ## Todo 52 | 53 | * More docs 54 | 55 | ## License 56 | 57 | Licensed under the MIT license. 58 | -------------------------------------------------------------------------------- /test/unit/test-LazySocket.js: -------------------------------------------------------------------------------- 1 | var test = require('utest'); 2 | var assert = require('assert'); 3 | var LazySocket = require('../../lib/LazySocket'); 4 | var sinon = require('sinon'); 5 | var net = require('net'); 6 | 7 | test('LazySocket#createConnection', { 8 | 'returns a new LazySocket': function() { 9 | var socket = LazySocket.createConnection(); 10 | assert.ok(socket instanceof LazySocket); 11 | }, 12 | 13 | 'sets the passed host / port': function() { 14 | var socket = LazySocket.createConnection(8080, 'example.org'); 15 | assert.equal(socket.port, 8080); 16 | assert.equal(socket.host, 'example.org'); 17 | }, 18 | }); 19 | 20 | var socket; 21 | var fakeSocket; 22 | test('LazySocket', { 23 | before: function() { 24 | socket = new LazySocket(); 25 | fakeSocket = sinon.stub({ 26 | once: function() {}, 27 | destroy: function() {}, 28 | end: function() {}, 29 | write: function() {}, 30 | }); 31 | 32 | sinon.stub(net, 'createConnection').returns(fakeSocket); 33 | fakeSocket.once.returns(fakeSocket); 34 | 35 | // To establish a connection 36 | socket.write(); 37 | }, 38 | 39 | after: function() { 40 | net.createConnection.restore(); 41 | }, 42 | 43 | '#end when disconnected (does not blow up)': function() { 44 | socket = new LazySocket(); 45 | socket.end(); 46 | }, 47 | 48 | '#end when connected': function() { 49 | socket.end(); 50 | 51 | assert.ok(fakeSocket.end.calledOnce); 52 | }, 53 | 54 | '#destroy when disconnected (does not blow up)': function() { 55 | var socket = new LazySocket(); 56 | socket.destroy(); 57 | }, 58 | 59 | '#destroy when connected': function() { 60 | socket.destroy(); 61 | 62 | assert.ok(fakeSocket.destroy.calledOnce); 63 | }, 64 | }); 65 | -------------------------------------------------------------------------------- /lib/GraphiteClient.js: -------------------------------------------------------------------------------- 1 | var CarbonClient = require('./CarbonClient'); 2 | 3 | module.exports = GraphiteClient; 4 | function GraphiteClient(properties) { 5 | this._carbon = properties.carbon; 6 | } 7 | 8 | GraphiteClient.createClient = function(carbonDsn) { 9 | var client = new this({ 10 | carbon: new CarbonClient({dsn: carbonDsn}), 11 | }); 12 | return client; 13 | }; 14 | 15 | GraphiteClient.flatten = function(obj, flat, prefix) { 16 | flat = flat || {}; 17 | prefix = prefix || ''; 18 | 19 | for (var key in obj) { 20 | var value = obj[key]; 21 | if (typeof value === 'object') { 22 | this.flatten(value, flat, prefix + key + '.'); 23 | } else { 24 | flat[prefix + key] = value; 25 | } 26 | } 27 | 28 | return flat; 29 | }; 30 | 31 | GraphiteClient.appendTags = function(flatMetrics, tags) { 32 | tagSuffix = ''; 33 | res = {}; 34 | 35 | flatTags = GraphiteClient.flatten(tags); 36 | for (var key in flatTags) { 37 | tagSuffix += ';' + key + '=' + flatTags[key]; 38 | } 39 | 40 | for (var key in flatMetrics) { 41 | res[key + tagSuffix] = flatMetrics[key]; 42 | } 43 | 44 | return res; 45 | }; 46 | 47 | /** 48 | * Writes the given metrics to the underlying plaintext socket to Graphite 49 | * 50 | * If no timestamp is given, the current Unix epoch is used (second precision). 51 | * 52 | * If a timestamp is provided, it must have a millisecond precision, otherwise 53 | * Graphite will probably reject the data. 54 | * 55 | * @param {object} metrics 56 | * @param {object} timestamp 57 | * @param {function} cb 58 | */ 59 | GraphiteClient.prototype.write = function(metrics, timestamp, cb) { 60 | if (typeof timestamp === 'function') { 61 | cb = timestamp; 62 | timestamp = null; 63 | } 64 | 65 | // default timestamp to now 66 | timestamp = timestamp || Date.now(); 67 | 68 | // cutting timestamp for precision up to the second 69 | timestamp = Math.floor(timestamp / 1000); 70 | 71 | this._carbon.write(GraphiteClient.flatten(metrics), timestamp, cb); 72 | }; 73 | 74 | GraphiteClient.prototype.writeTagged = function(metrics, tags, timestamp, cb) { 75 | taggedMetrics = GraphiteClient.appendTags(GraphiteClient.flatten(metrics), tags); 76 | this.write(taggedMetrics, timestamp, cb); 77 | } 78 | 79 | GraphiteClient.prototype.end = function() { 80 | this._carbon.end(); 81 | }; 82 | -------------------------------------------------------------------------------- /lib/LazySocket.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | 3 | module.exports = LazySocket; 4 | function LazySocket(properties) { 5 | properties = properties || {}; 6 | 7 | this.port = properties.port; 8 | this.host = properties.host; 9 | 10 | this._socket = null; 11 | this._closed = false; 12 | this._callbacks = []; 13 | } 14 | 15 | LazySocket.createConnection = function(port, host) { 16 | var socket = new this({port: port, host: host}); 17 | return socket; 18 | }; 19 | 20 | LazySocket.prototype.write = function(/* data, encoding, cb */) { 21 | var self = this; 22 | var args = Array.prototype.slice.call(arguments); 23 | var cb = args[args.length - 1]; 24 | 25 | if (typeof cb === 'function') { 26 | var cbProxy = function() { 27 | var index = self._callbacks.indexOf(cbProxy); 28 | self._callbacks.splice(index, 1); 29 | 30 | return cb.apply(this, arguments); 31 | }; 32 | 33 | args[args.length - 1] = cbProxy; 34 | this._callbacks.push(cbProxy); 35 | } 36 | 37 | this._lazyConnect(); 38 | 39 | try { 40 | this._socket.write.apply(this._socket, args); 41 | } catch (err) { 42 | if (cbProxy) cbProxy(err); 43 | 44 | this._socket.destroy(); 45 | this._socket = null; 46 | } 47 | }; 48 | 49 | LazySocket.prototype._lazyConnect = function() { 50 | if (this._socket) return; 51 | 52 | var self = this; 53 | 54 | function onerror(err) { 55 | self._socket = null; 56 | self._callbacks.forEach(function(cb) { 57 | cb(err); 58 | }); 59 | } 60 | 61 | function onend() { 62 | // "end" is called when the socket connection is terminated, regardless of the termination being unexpected or not 63 | // to distinguish between unexpected terminations (e.g need reconnection) 64 | // from expected terminations (e.g calling LazySocket's .end() or .destroy()), the later are flagged as "closed" 65 | 66 | if (!self._closed) { 67 | self._socket = null; 68 | } 69 | } 70 | 71 | this._socket = net 72 | .createConnection(this.port, this.host) 73 | .once('error', onerror) 74 | .once('end', onend); 75 | }; 76 | 77 | LazySocket.prototype.end = function() { 78 | this._closed = true; 79 | if (this._socket) this._socket.end(); 80 | }; 81 | 82 | LazySocket.prototype.destroy = function() { 83 | this._closed = true; 84 | if (this._socket) this._socket.destroy(); 85 | }; 86 | -------------------------------------------------------------------------------- /test/unit/test-GraphiteClient.js: -------------------------------------------------------------------------------- 1 | var common = require('../common'); 2 | var test = require('utest'); 3 | var assert = require('assert'); 4 | var sinon = require('sinon'); 5 | var graphite = common.graphite; 6 | var GraphiteClient = graphite; 7 | 8 | test('graphite.createClient', { 9 | 'returns a new GraphiteClient': function() { 10 | var client = graphite.createClient(); 11 | assert.ok(client instanceof GraphiteClient); 12 | }, 13 | 14 | 'takes carbon dsn first and creates lazy socket': function() { 15 | var client = graphite.createClient('plaintext://example.org:8080/'); 16 | }, 17 | }); 18 | 19 | test('graphite.flatten', { 20 | 'returns an already flat object as is': function() { 21 | var obj = {foo: 'bar'}; 22 | assert.deepEqual(graphite.flatten(obj), {foo: 'bar'}); 23 | }, 24 | 25 | 'returns a copy of the object': function() { 26 | var obj = {foo: 'bar'}; 27 | var flat = graphite.flatten(obj); 28 | 29 | assert.notStrictEqual(obj, flat); 30 | }, 31 | 32 | 'flattens a deep object': function() { 33 | var obj = { 34 | a: 1, 35 | deep: { 36 | we: { 37 | go: { 38 | b: 2, 39 | c: 3, 40 | } 41 | } 42 | }, 43 | d: 4, 44 | }; 45 | var flat = graphite.flatten(obj); 46 | 47 | assert.deepEqual(flat, { 48 | 'a' : 1, 49 | 'deep.we.go.b' : 2, 50 | 'deep.we.go.c' : 3, 51 | 'd' : 4, 52 | }); 53 | }, 54 | }); 55 | 56 | var client; 57 | var carbon; 58 | test('GraphiteClient', { 59 | before: function() { 60 | carbon = sinon.stub({ 61 | write: function() {}, 62 | }); 63 | client = new GraphiteClient({carbon: carbon}); 64 | }, 65 | 66 | '#write flattens metrics before passing to carbon': function() { 67 | var metrics = {foo: {bar: 1}}; 68 | client.write(metrics); 69 | 70 | assert.ok(carbon.write.calledWith({'foo.bar': 1})); 71 | }, 72 | 73 | '#write passes the current time to carbon': function() { 74 | client.write({}); 75 | 76 | var now = Math.floor(Date.now() / 1000); 77 | assert.ok(carbon.write.getCall(0).args[1] >= now); 78 | }, 79 | 80 | '#write lets you pass a timestamp to carbon': function() { 81 | client.write({}, 23000); 82 | 83 | assert.equal(carbon.write.getCall(0).args[1], 23); 84 | }, 85 | 86 | '#write passes a callback to carbon': function() { 87 | var cb = function() {}; 88 | client.write({}, null, cb); 89 | 90 | assert.equal(carbon.write.getCall(0).args[2], cb); 91 | }, 92 | 93 | '#write takes callback as second argument as well': function() { 94 | var cb = function() {}; 95 | client.write({}, cb); 96 | 97 | assert.equal(carbon.write.getCall(0).args[2], cb); 98 | }, 99 | }); 100 | --------------------------------------------------------------------------------