diff --git a/.travis.yml b/.travis.yml index b582cb8..1d18083 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,13 @@ language: node_js node_js: - - "0.6" - - "0.8" - "0.10" + - "0.11" + - "0.12" + - "4" + - "5" + - "6" + - "7" install: - npm install diff --git a/README.md b/README.md index 1820e7f..b725e5f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A node.js client for [Etsy](http://etsy.com)'s [StatsD](https://github.com/etsy/ This client will let you fire stats at your StatsD server from a node.js application. -node-statsd Runs and is tested on Node 0.6+ on all *nix platforms and 0.8+ on all platforms including Windows. +node-statsd Runs and is tested on Node 0.10+ on all platforms including Windows. [![Build Status](https://secure.travis-ci.org/sivy/node-statsd.png?branch=master)](http://travis-ci.org/sivy/node-statsd) @@ -27,6 +27,7 @@ Parameters (specified as an options hash): * `cacheDns`: Cache the initial dns lookup to *host* `default: false` * `mock`: Create a mock StatsD instance, sending no stats to the server? `default: false` * `global_tags`: Optional tags that will be added to every metric `default: []` +* `tcp`: Optional boolean indicating if the Client should use a TCP connection `default: false` All StatsD methods have the same API: * `name`: Stat name `required` diff --git a/lib/statsd.js b/lib/statsd.js index f27383b..167395f 100644 --- a/lib/statsd.js +++ b/lib/statsd.js @@ -1,4 +1,5 @@ var dgram = require('dgram'), + net = require('net'), dns = require('dns'); /** @@ -12,9 +13,10 @@ var dgram = require('dgram'), * @option cacheDns {boolean} An optional option to only lookup the hostname -> ip address once * @option mock {boolean} An optional boolean indicating this Client is a mock object, no stats are sent. * @option global_tags {Array=} Optional tags that will be added to every metric + * @option tcp {boolean} An optional boolean indicating if the Client should use a TCP connection * @constructor */ -var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, global_tags) { +var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, global_tags, tcp) { var options = host || {}, self = this; @@ -27,7 +29,8 @@ var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, gl globalize : globalize, cacheDns : cacheDns, mock : mock === true, - global_tags : global_tags + global_tags : global_tags, + tcp : tcp }; } @@ -35,9 +38,10 @@ var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, gl this.port = options.port || 8125; this.prefix = options.prefix || ''; this.suffix = options.suffix || ''; - this.socket = dgram.createSocket('udp4'); + this.socket = options.tcp ? net.connect(this.port, this.host) : dgram.createSocket('udp4'); this.mock = options.mock; this.global_tags = options.global_tags || []; + this.tcp = options.tcp; if(options.cacheDns === true){ dns.lookup(options.host, function(err, address, family){ @@ -50,6 +54,13 @@ var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, gl if(options.globalize){ global.statsd = this; } + + if(options.tcp === true) { + // Catch global TCP connection errors + // We don't have a way to get them to the consumer, so just discard them + this.socket.on('error', function() {}); + this.socket.setKeepAlive(true); + } }; /** @@ -167,7 +178,10 @@ Client.prototype.sendAll = function(stat, value, type, sampleRate, tags, callbac return callback(error); } - sentBytes += bytes; + if(bytes) { + sentBytes += bytes; + } + if(completed === stat.length){ callback(null, sentBytes); } @@ -215,10 +229,28 @@ Client.prototype.send = function (stat, value, type, sampleRate, tags, callback) message += '|#' + merged_tags.join(','); } + if(this.tcp) { + message += '\n'; + } + + this.sendRaw(message, callback); +} + +/** + * Sends a stat across the wire + * @param message {String} The fully qualified stat to send [prefix]stat[suffix]:value|type[|@sampleRate][|#tags] + * @param callback {Function=} Callback when message is done being delivered. Optional. + */ +Client.prototype.sendRaw = function (message, callback) { + // Only send this stat if we're not a mock Client. if(!this.mock) { buf = new Buffer(message); - this.socket.send(buf, 0, buf.length, this.port, this.host, callback); + if(this.tcp) { + this.socket.write(buf, 'ascii', callback); + } else { + this.socket.send(buf, 0, buf.length, this.port, this.host, callback); + } } else { if(typeof callback === 'function'){ callback(null, 0); diff --git a/package.json b/package.json index 5a672b3..3df7b8d 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ } , "bugs" : { "url" : "https://github.com/sivy/node-statsd/issues" } , "directories" : { "lib" : "./lib/" } -, "engines" : { "node" : ">=0.1.97" } +, "engines" : { "node" : ">=0.10" } , "scripts": { "test": "mocha -R spec" } diff --git a/test/test_statsd.js b/test/test_statsd.js index 0fbb314..1bf7063 100644 --- a/test/test_statsd.js +++ b/test/test_statsd.js @@ -1,4 +1,5 @@ var dgram = require('dgram'), + net = require('net'), assert = require('assert'), StatsD = require('../').StatsD; @@ -21,6 +22,49 @@ function udpTest(test, callback){ server.bind(0, '127.0.0.1'); } +/** + * Creates a test harness for TCP, that binds to an ephemeral port + * @param test {Function} The test to run, should take message as the argument + * @param callback {Function} The callback to call after the server is listening + * @private + */ +function tcpTest(test, callback){ + + // This is the actual TCP server implementation of etsy/statsd + // Including the metrics handler which splits the TCP packet on "\n" + // to deal with multiple metrics in a single TCP stream data flush + var server = net.createServer(function(stream) { + stream.setEncoding('ascii'); + + var buffer = ''; + stream.on('data', function(data) { + buffer += data; + var offset = buffer.lastIndexOf("\n"); + if (offset > -1) { + var packet = buffer.slice(0, offset + 1); + buffer = buffer.slice(offset + 1); + + var metrics; + if (packet.indexOf("\n") > -1) { + metrics = packet.split("\n"); + } else { + metrics = [ packet ]; + } + + metrics.forEach(function(metric) { + test(metric, server); + }); + } + }); + }); + + server.on('listening', function(){ + callback(server); + }); + + server.listen(0, '127.0.0.1'); +} + /** * Given a StatsD method, make sure no data is sent to the server * for this method when used on a mock Client. @@ -84,12 +128,13 @@ describe('StatsD', function(){ assert.equal(global.statsd, undefined); assert.equal(statsd.mock, undefined); assert.deepEqual(statsd.global_tags, []); + assert.ok(!statsd.tcp); assert.ok(!statsd.mock); }); it('should set the proper values when specified', function(){ // cachedDns isn't tested here; see below - var statsd = new StatsD('host', 1234, 'prefix', 'suffix', true, null, true, ['gtag']); + var statsd = new StatsD('host', 1234, 'prefix', 'suffix', true, null, true, ['gtag'], true); assert.equal(statsd.host, 'host'); assert.equal(statsd.port, 1234); assert.equal(statsd.prefix, 'prefix'); @@ -97,6 +142,7 @@ describe('StatsD', function(){ assert.equal(statsd, global.statsd); assert.equal(statsd.mock, true); assert.deepEqual(statsd.global_tags, ['gtag']); + assert.equal(statsd.tcp, true); }); it('should set the proper values with options hash format', function(){ @@ -108,7 +154,8 @@ describe('StatsD', function(){ suffix: 'suffix', globalize: true, mock: true, - global_tags: ['gtag'] + global_tags: ['gtag'], + tcp: true }); assert.equal(statsd.host, 'host'); assert.equal(statsd.port, 1234); @@ -117,6 +164,7 @@ describe('StatsD', function(){ assert.equal(statsd, global.statsd); assert.equal(statsd.mock, true); assert.deepEqual(statsd.global_tags, ['gtag']); + assert.equal(statsd.tcp, true); }); it('should attempt to cache a dns record if dnsCache is specified', function(done){ @@ -178,6 +226,13 @@ describe('StatsD', function(){ assert.ok(statsd.socket instanceof dgram.Socket); }); + it('should create a socket variable that is an instance of net.Socket if set to TCP', function(){ + var statsd = new StatsD({ + tcp: true + }); + assert.ok(statsd.socket instanceof net.Socket); + }); + }); describe('#global_tags', function(){ @@ -194,6 +249,23 @@ describe('StatsD', function(){ }); }); + it('should not add global tags if they are not specified (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:1|c'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.increment('test'); + }); + }); + it('should add global tags if they are specified', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:1|c|#gtag'); @@ -211,6 +283,24 @@ describe('StatsD', function(){ }); }); + it('should add global tags if they are specified (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:1|c|#gtag'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + global_tags: ['gtag'], + tcp: true + }); + + statsd.increment('test'); + }); + }); + it('should combine global tags and metric tags', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:1337|c|#foo,gtag'); @@ -227,6 +317,24 @@ describe('StatsD', function(){ statsd.increment('test', 1337, ['foo']); }); }); + + it('should combine global tags and metric tags (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:1337|c|#foo,gtag'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + global_tags: ['gtag'], + tcp: true + }); + + statsd.increment('test', 1337, ['foo']); + }); + }); }); describe('#timing', function(finished){ @@ -243,6 +351,23 @@ describe('StatsD', function(){ }); }); + it('should send proper time format without prefix, suffix, sampling and callback (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|ms'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.timing('test', 42); + }); + }); + it('should send proper time format with tags', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:42|ms|#foo,bar'); @@ -256,6 +381,23 @@ describe('StatsD', function(){ }); }); + it('should send proper time format with tags (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|ms|#foo,bar'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.timing('test', 42, ['foo', 'bar']); + }); + }); + it('should send proper time format with prefix, suffix, sampling and callback', function(finished){ var called = false; udpTest(function(message, server){ @@ -273,6 +415,29 @@ describe('StatsD', function(){ }); }); + it('should send proper time format with prefix, suffix, sampling and callback (TCP connection)', function(finished){ + var called = false; + tcpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|ms|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + prefix: 'foo.', + suffix: '.bar', + tcp: true + }); + + statsd.timing('test', 42, 0.5, function(){ + called = true; + }); + }); + }); + it('should properly send a and b with the same value', function(finished){ var called = false, messageNumber = 0; @@ -299,6 +464,36 @@ describe('StatsD', function(){ }); }); + it('should properly send a and b with the same value (TCP connection)', function(finished){ + var called = false, + messageNumber = 0; + + tcpTest(function(message, server){ + if(messageNumber === 0){ + assert.equal(message, 'a:42|ms'); + messageNumber += 1; + } else { + assert.equal(message, 'b:42|ms'); + server.close(); + finished(); + } + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.timing(['a', 'b'], 42, null, function(error, bytes){ + called += 1; + assert.ok(called === 1); //ensure it only gets called once + assert.equal(error, null); + assert.equal(bytes, 0); + }); + }); + }); + it('should send no timing stat when a mock Client is used', function(finished){ assertMockClientMethod('timing', finished); }); @@ -318,6 +513,23 @@ describe('StatsD', function(){ }); }); + it('should send proper histogram format without prefix, suffix, sampling and callback (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|h'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.histogram('test', 42); + }); + }); + it('should send proper histogram format with tags', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:42|h|#foo,bar'); @@ -331,6 +543,23 @@ describe('StatsD', function(){ }); }); + it('should send proper histogram format with tags (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|h|#foo,bar'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.histogram('test', 42, ['foo', 'bar']); + }); + }); + it('should send proper histogram format with prefix, suffix, sampling and callback', function(finished){ var called = false; udpTest(function(message, server){ @@ -348,6 +577,29 @@ describe('StatsD', function(){ }); }); + it('should send proper histogram format with prefix, suffix, sampling and callback (TCP connection)', function(finished){ + var called = false; + tcpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|h|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + prefix: 'foo.', + suffix: '.bar', + tcp: true + }); + + statsd.histogram('test', 42, 0.5, function(){ + called = true; + }); + }); + }); + it('should properly send a and b with the same value', function(finished){ var called = 0, messageNumber = 0; @@ -374,6 +626,36 @@ describe('StatsD', function(){ }); }); + it('should properly send a and b with the same value (TCP connection)', function(finished){ + var called = 0, + messageNumber = 0; + + tcpTest(function(message, server){ + if(messageNumber === 0){ + assert.equal(message, 'a:42|h'); + messageNumber += 1; + } else { + assert.equal(message, 'b:42|h'); + server.close(); + finished(); + } + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.histogram(['a', 'b'], 42, null, function(error, bytes){ + called += 1; + assert.ok(called === 1); //ensure it only gets called once + assert.equal(error, null); + assert.equal(bytes, 0); + }); + }); + }); + it('should send no histogram stat when a mock Client is used', function(finished){ assertMockClientMethod('histogram', finished); }); @@ -393,6 +675,23 @@ describe('StatsD', function(){ }); }); + it('should send proper gauge format without prefix, suffix, sampling and callback (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|g'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.gauge('test', 42); + }); + }); + it('should send proper gauge format with tags', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:42|g|#foo,bar'); @@ -406,6 +705,23 @@ describe('StatsD', function(){ }); }); + it('should send proper gauge format with tags (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|g|#foo,bar'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.gauge('test', 42, ['foo', 'bar']); + }); + }); + it('should send proper gauge format with prefix, suffix, sampling and callback', function(finished){ var called = false; udpTest(function(message, server){ @@ -423,6 +739,29 @@ describe('StatsD', function(){ }); }); + it('should send proper gauge format with prefix, suffix, sampling and callback (TCP connection)', function(finished){ + var called = false; + tcpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|g|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + prefix: 'foo.', + suffix: '.bar', + tcp: true + }); + + statsd.gauge('test', 42, 0.5, function(){ + called = true; + }); + }); + }); + it('should properly send a and b with the same value', function(finished){ var called = 0, messageNumber = 0; @@ -449,6 +788,36 @@ describe('StatsD', function(){ }); }); + it('should properly send a and b with the same value (TCP connection)', function(finished){ + var called = 0, + messageNumber = 0; + + tcpTest(function(message, server){ + if(messageNumber === 0){ + assert.equal(message, 'a:42|g'); + messageNumber += 1; + } else { + assert.equal(message, 'b:42|g'); + server.close(); + finished(); + } + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.gauge(['a', 'b'], 42, null, function(error, bytes){ + called += 1; + assert.ok(called === 1); //ensure it only gets called once + assert.equal(error, null); + assert.equal(bytes, 0); + }); + }); + }); + it('should send no gauge stat when a mock Client is used', function(finished){ assertMockClientMethod('gauge', finished); }); @@ -468,6 +837,23 @@ describe('StatsD', function(){ }); }); + it('should send count by 1 when no params are specified (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:1|c'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.increment('test'); + }); + }); + it('should send proper count format with tags', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:42|c|#foo,bar'); @@ -481,6 +867,23 @@ describe('StatsD', function(){ }); }); + it('should send proper count format with tags (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|c|#foo,bar'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.increment('test', 42, ['foo', 'bar']); + }); + }); + it('should send proper count format with prefix, suffix, sampling and callback', function(finished){ var called = false; udpTest(function(message, server){ @@ -498,6 +901,29 @@ describe('StatsD', function(){ }); }); + it('should send proper count format with prefix, suffix, sampling and callback (TCP connection)', function(finished){ + var called = false; + tcpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|c|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + prefix: 'foo.', + suffix: '.bar', + tcp: true + }); + + statsd.increment('test', 42, 0.5, function(){ + called = true; + }); + }); + }); + it('should properly send a and b with the same value', function(finished){ var called = 0, messageNumber = 0; @@ -524,6 +950,36 @@ describe('StatsD', function(){ }); }); + it('should properly send a and b with the same value (TCP connection)', function(finished){ + var called = 0, + messageNumber = 0; + + tcpTest(function(message, server){ + if(messageNumber === 0){ + assert.equal(message, 'a:1|c'); + messageNumber += 1; + } else { + assert.equal(message, 'b:1|c'); + server.close(); + finished(); + } + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.increment(['a', 'b'], null, function(error, bytes){ + called += 1; + assert.ok(called === 1); //ensure it only gets called once + assert.equal(error, null); + assert.equal(bytes, 0); + }); + }); + }); + it('should send no increment stat when a mock Client is used', function(finished){ assertMockClientMethod('increment', finished); }); @@ -543,6 +999,23 @@ describe('StatsD', function(){ }); }); + it('should send count by -1 when no params are specified (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:-1|c'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.decrement('test'); + }); + }); + it('should send proper count format with tags', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:-42|c|#foo,bar'); @@ -556,6 +1029,23 @@ describe('StatsD', function(){ }); }); + it('should send proper count format with tags (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:-42|c|#foo,bar'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.decrement('test', 42, ['foo', 'bar']); + }); + }); + it('should send proper count format with prefix, suffix, sampling and callback', function(finished){ var called = false; udpTest(function(message, server){ @@ -573,6 +1063,28 @@ describe('StatsD', function(){ }); }); + it('should send proper count format with prefix, suffix, sampling and callback (TCP connection)', function(finished){ + var called = false; + tcpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:-42|c|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + prefix: 'foo.', + suffix: '.bar', + tcp: true + }); + + statsd.decrement('test', 42, 0.5, function(){ + called = true; + }); + }); + }); it('should properly send a and b with the same value', function(finished){ var called = 0, @@ -600,6 +1112,36 @@ describe('StatsD', function(){ }); }); + it('should properly send a and b with the same value (TCP connection)', function(finished){ + var called = 0, + messageNumber = 0; + + tcpTest(function(message, server){ + if(messageNumber === 0){ + assert.equal(message, 'a:-1|c'); + messageNumber += 1; + } else { + assert.equal(message, 'b:-1|c'); + server.close(); + finished(); + } + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.decrement(['a', 'b'], null, function(error, bytes){ + called += 1; + assert.ok(called === 1); //ensure it only gets called once + assert.equal(error, null); + assert.equal(bytes, 0); + }); + }); + }); + it('should send no decrement stat when a mock Client is used', function(finished){ assertMockClientMethod('decrement', finished); }); @@ -619,6 +1161,23 @@ describe('StatsD', function(){ }); }); + it('should send proper set format without prefix, suffix, sampling and callback (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|s'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.set('test', 42); + }); + }); + it('should send proper set format with tags', function(finished){ udpTest(function(message, server){ assert.equal(message, 'test:42|s|#foo,bar'); @@ -632,6 +1191,23 @@ describe('StatsD', function(){ }); }); + it('should send proper set format with tags (TCP connection)', function(finished){ + tcpTest(function(message, server){ + assert.equal(message, 'test:42|s|#foo,bar'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.set('test', 42, ['foo', 'bar']); + }); + }); + it('should send proper set format with prefix, suffix, sampling and callback', function(finished){ var called = false; udpTest(function(message, server){ @@ -649,6 +1225,29 @@ describe('StatsD', function(){ }); }); + it('should send proper set format with prefix, suffix, sampling and callback (TCP connection)', function(finished){ + var called = false; + tcpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|s|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + prefix: 'foo.', + suffix: '.bar', + tcp: true + }); + + statsd.unique('test', 42, 0.5, function(){ + called = true; + }); + }); + }); + it('should properly send a and b with the same value', function(finished){ var called = 0, messageNumber = 0; @@ -675,6 +1274,36 @@ describe('StatsD', function(){ }); }); + it('should properly send a and b with the same value (TCP connection)', function(finished){ + var called = 0, + messageNumber = 0; + + tcpTest(function(message, server){ + if(messageNumber === 0){ + assert.equal(message, 'a:42|s'); + messageNumber += 1; + } else { + assert.equal(message, 'b:42|s'); + server.close(); + finished(); + } + }, function(server){ + var address = server.address(), + statsd = new StatsD({ + host: address.address, + port: address.port, + tcp: true + }); + + statsd.unique(['a', 'b'], 42, null, function(error, bytes){ + called += 1; + assert.ok(called === 1); //ensure it only gets called once + assert.equal(error, null); + assert.equal(bytes, 0); + }); + }); + }); + it('should send no set stat when a mock Client is used', function(finished){ assertMockClientMethod('set', finished); });