UNPKG

9.04 kBJavaScriptView Raw
1var fs = require('fs'),
2 net = require('net'),
3 temp = require('temp'),
4 spawn = require('child_process').spawn,
5 util = require('util'),
6 urlparse = require('url').parse,
7 _ = require('underscore'),
8 dgram = require('dgram'),
9 qsparse = require('querystring').parse,
10 http = require('http');
11
12
13var writeconfig = function(text,worker,cb,obj){
14 temp.open({suffix: '-statsdconf.js'}, function(err, info) {
15 if (err) throw err;
16 fs.writeSync(info.fd, text);
17 fs.close(info.fd, function(err) {
18 if (err) throw err;
19 worker(info.path,cb,obj);
20 });
21 });
22}
23
24var array_contents_are_equal = function(first,second){
25 var intlen = _.intersection(first,second).length;
26 var unlen = _.union(first,second).length;
27 return (intlen == unlen) && (intlen == first.length);
28}
29
30var statsd_send = function(data,sock,host,port,cb){
31 send_data = new Buffer(data);
32 sock.send(send_data,0,send_data.length,port,host,function(err,bytes){
33 if (err) {
34 throw err;
35 }
36 cb();
37 });
38}
39
40// keep collecting data until a specified timeout period has elapsed
41// this will let us capture all data chunks so we don't miss one
42var collect_for = function(server,timeout,cb){
43 var received = [];
44 var in_flight = 0;
45 var timed_out = false;
46 var collector = function(req,res){
47 in_flight += 1;
48 var body = '';
49 req.on('data',function(data){ body += data; });
50 req.on('end',function(){
51 received = received.concat(body.split("\n"));
52 in_flight -= 1;
53 if((in_flight < 1) && timed_out){
54 server.removeListener('request',collector);
55 cb(received);
56 }
57 });
58 }
59
60 setTimeout(function (){
61 timed_out = true;
62 if((in_flight < 1)) {
63 server.removeListener('connection',collector);
64 cb(received);
65 }
66 },timeout);
67
68 server.on('connection',collector);
69}
70
71module.exports = {
72 setUp: function (callback) {
73 this.testport = 31337;
74 this.myflush = 200;
75 var configfile = "{graphService: \"graphite\"\n\
76 , batch: 200 \n\
77 , flushInterval: " + this.myflush + " \n\
78 , percentThreshold: 90\n\
79 , port: 8125\n\
80 , dumpMessages: false \n\
81 , debug: false\n\
82 , graphite: { legacyNamespace: false, globalSuffix: \"statssuffix\" }\n\
83 , graphitePort: " + this.testport + "\n\
84 , graphiteHost: \"127.0.0.1\"}";
85
86 this.acceptor = net.createServer();
87 this.acceptor.listen(this.testport);
88 this.sock = dgram.createSocket('udp4');
89
90 this.server_up = true;
91 this.ok_to_die = false;
92 this.exit_callback_callback = process.exit;
93
94 writeconfig(configfile,function(path,cb,obj){
95 obj.path = path;
96 obj.server = spawn('node',['stats.js', path]);
97 obj.exit_callback = function (code) {
98 obj.server_up = false;
99 if(!obj.ok_to_die){
100 console.log('node server unexpectedly quit with code: ' + code);
101 process.exit(1);
102 }
103 else {
104 obj.exit_callback_callback();
105 }
106 };
107 obj.server.on('exit', obj.exit_callback);
108 obj.server.stderr.on('data', function (data) {
109 console.log('stderr: ' + data.toString().replace(/\n$/,''));
110 });
111 /*
112 obj.server.stdout.on('data', function (data) {
113 console.log('stdout: ' + data.toString().replace(/\n$/,''));
114 });
115 */
116 obj.server.stdout.on('data', function (data) {
117 // wait until server is up before we finish setUp
118 if (data.toString().match(/server is up/)) {
119 cb();
120 }
121 });
122
123 },callback,this);
124 },
125 tearDown: function (callback) {
126 this.sock.close();
127 this.acceptor.close();
128 this.ok_to_die = true;
129 if(this.server_up){
130 this.exit_callback_callback = callback;
131 this.server.kill();
132 } else {
133 callback();
134 }
135 },
136
137 send_well_formed_posts: function (test) {
138 test.expect(2);
139
140 // we should integrate a timeout into this
141 this.acceptor.once('connection',function(c){
142 var body = '';
143 c.on('data',function(d){ body += d; });
144 c.on('end',function(){
145 var rows = body.split("\n");
146
147 var entries = _.map(rows, function(x) {
148
149 var chunks = x.split(' ');
150 var data = {};
151 data[chunks[0]] = chunks[1];
152 return data;
153 });
154 test.ok(_.include(_.map(entries,function(x) { return _.keys(x)[0] }),'stats.statsd.numStats.statssuffix'),'graphite output includes numStats');
155
156 test.equal(_.find(entries, function(x) { return _.keys(x)[0] == 'stats.statsd.numStats.statssuffix' })['stats.statsd.numStats.statssuffix'],3);
157 test.done();
158 });
159 });
160 },
161
162 send_malformed_post: function (test) {
163 test.expect(3);
164
165 var testvalue = 1;
166 var me = this;
167 this.acceptor.once('connection',function(c){
168 statsd_send('a_bad_test_value|z',me.sock,'127.0.0.1',8125,function(){
169 collect_for(me.acceptor,me.myflush*2,function(strings){
170 test.ok(strings.length > 0,'should receive some data');
171 var hashes = _.map(strings, function(x) {
172 var chunks = x.split(' ');
173 var data = {};
174 data[chunks[0]] = chunks[1];
175 return data;
176 });
177 var numstat_test = function(post){
178 var mykey = 'stats.statsd.numStats.statssuffix';
179 return _.include(_.keys(post),mykey) && (post[mykey] == 4);
180 };
181 test.ok(_.any(hashes,numstat_test), 'numStats.statssuffix should be 4');
182
183 var bad_lines_seen_value_test = function(post){
184 var mykey = 'stats.counters.statsd.bad_lines_seen.count.statssuffix';
185 return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
186 };
187 test.ok(_.any(hashes,bad_lines_seen_value_test), 'stats.counters.statsd.bad_lines_seen.count.statssuffix should be ' + testvalue );
188
189 test.done();
190 });
191 });
192 });
193 },
194
195 timers_are_valid: function (test) {
196 test.expect(3);
197
198 var testvalue = 100;
199 var me = this;
200 this.acceptor.once('connection',function(c){
201 statsd_send('a_test_value:' + testvalue + '|ms',me.sock,'127.0.0.1',8125,function(){
202 collect_for(me.acceptor,me.myflush*2,function(strings){
203 test.ok(strings.length > 0,'should receive some data');
204 var hashes = _.map(strings, function(x) {
205 var chunks = x.split(' ');
206 var data = {};
207 data[chunks[0]] = chunks[1];
208 return data;
209 });
210 var numstat_test = function(post){
211 var mykey = 'stats.statsd.numStats.statssuffix';
212 return _.include(_.keys(post),mykey) && (post[mykey] == 5);
213 };
214 test.ok(_.any(hashes,numstat_test), 'stats.statsd.numStats.statssuffix should be 5');
215
216 var testtimervalue_test = function(post){
217 var mykey = 'stats.timers.a_test_value.mean_90.statssuffix';
218 return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
219 };
220 test.ok(_.any(hashes,testtimervalue_test), 'stats.timers.a_test_value.mean.statssuffix should be ' + testvalue);
221
222 test.done();
223 });
224 });
225 });
226 },
227
228 counts_are_valid: function (test) {
229 test.expect(4);
230
231 var testvalue = 100;
232 var me = this;
233 this.acceptor.once('connection',function(c){
234 statsd_send('a_test_value:' + testvalue + '|c',me.sock,'127.0.0.1',8125,function(){
235 collect_for(me.acceptor,me.myflush*2,function(strings){
236 test.ok(strings.length > 0,'should receive some data');
237 var hashes = _.map(strings, function(x) {
238 var chunks = x.split(' ');
239 var data = {};
240 data[chunks[0]] = chunks[1];
241 return data;
242 });
243 var numstat_test = function(post){
244 var mykey = 'stats.statsd.numStats.statssuffix';
245 return _.include(_.keys(post),mykey) && (post[mykey] == 5);
246 };
247 test.ok(_.any(hashes,numstat_test), 'numStats.statssuffix should be 5');
248
249 var testavgvalue_test = function(post){
250 var mykey = 'stats.counters.a_test_value.rate.statssuffix';
251 return _.include(_.keys(post),mykey) && (post[mykey] == (testvalue/(me.myflush / 1000)));
252 };
253 test.ok(_.any(hashes,testavgvalue_test), 'a_test_value.rate should be ' + (testvalue/(me.myflush / 1000)));
254
255 var testcountvalue_test = function(post){
256 var mykey = 'stats.counters.a_test_value.count.statssuffix';
257 return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
258 };
259 test.ok(_.any(hashes,testcountvalue_test), 'a_test_value.count should be ' + testvalue);
260
261 test.done();
262 });
263 });
264 });
265 }
266}