UNPKG

11.8 kBJavaScriptView Raw
1// this unit test, for deleteCounters and other stats related to deleteIdleStats
2// should probably be reviewed for sanity - I'm not sure it really tests appropriately
3// for example, it should test that data is written the first time
4// then test that the counter/etc is actually removed when it doesn't get data..
5// - keen99
6
7
8var fs = require('fs'),
9 net = require('net'),
10 temp = require('temp'),
11 spawn = require('child_process').spawn,
12 util = require('util'),
13 urlparse = require('url').parse,
14 _ = require('underscore'),
15 dgram = require('dgram'),
16 qsparse = require('querystring').parse,
17 http = require('http');
18
19
20var writeconfig = function(text,worker,cb,obj){
21 temp.open({suffix: '-statsdconf.js'}, function(err, info) {
22 if (err) throw err;
23 fs.writeSync(info.fd, text);
24 fs.close(info.fd, function(err) {
25 if (err) throw err;
26 worker(info.path,cb,obj);
27 });
28 });
29}
30
31var array_contents_are_equal = function(first,second){
32 var intlen = _.intersection(first,second).length;
33 var unlen = _.union(first,second).length;
34 return (intlen == unlen) && (intlen == first.length);
35}
36
37var statsd_send = function(data,sock,host,port,cb){
38 send_data = new Buffer(data);
39 sock.send(send_data,0,send_data.length,port,host,function(err,bytes){
40 if (err) {
41 throw err;
42 }
43 cb();
44 });
45}
46
47// keep collecting data until a specified timeout period has elapsed
48// this will let us capture all data chunks so we don't miss one
49var collect_for = function(server,timeout,cb){
50 var received = [];
51 var in_flight = 0;
52 var timed_out = false;
53 var collector = function(req,res){
54 in_flight += 1;
55 var body = '';
56 req.on('data',function(data){ body += data; });
57 req.on('end',function(){
58 received = received.concat(body.split("\n"));
59 in_flight -= 1;
60 if((in_flight < 1) && timed_out){
61 server.removeListener('request',collector);
62 cb(received);
63 }
64 });
65 }
66
67 setTimeout(function (){
68 timed_out = true;
69 if((in_flight < 1)) {
70 server.removeListener('connection',collector);
71 cb(received);
72 }
73 },timeout);
74
75 server.on('connection',collector);
76}
77
78module.exports = {
79 setUp: function (callback) {
80 this.testport = 31337;
81 this.myflush = 200;
82 var configfile = "{graphService: \"graphite\"\n\
83 , batch: 200 \n\
84 , flushInterval: " + this.myflush + " \n\
85 , percentThreshold: 90\n\
86 , port: 8125\n\
87 , dumpMessages: false \n\
88 , debug: false\n\
89 , deleteIdleStats: true\n\
90 , gaugesMaxTTL: 2\n\
91 , graphitePort: " + this.testport + "\n\
92 , graphiteHost: \"127.0.0.1\"}";
93
94 this.acceptor = net.createServer();
95 this.acceptor.listen(this.testport);
96 this.sock = dgram.createSocket('udp4');
97
98 this.server_up = true;
99 this.ok_to_die = false;
100 this.exit_callback_callback = process.exit;
101
102 writeconfig(configfile,function(path,cb,obj){
103 obj.path = path;
104 obj.server = spawn('node',['stats.js', path]);
105 obj.exit_callback = function (code) {
106 obj.server_up = false;
107 if(!obj.ok_to_die){
108 console.log('node server unexpectedly quit with code: ' + code);
109 process.exit(1);
110 }
111 else {
112 obj.exit_callback_callback();
113 }
114 };
115 obj.server.on('exit', obj.exit_callback);
116 obj.server.stderr.on('data', function (data) {
117 console.log('stderr: ' + data.toString().replace(/\n$/,''));
118 });
119 /*
120 obj.server.stdout.on('data', function (data) {
121 console.log('stdout: ' + data.toString().replace(/\n$/,''));
122 });
123 */
124 obj.server.stdout.on('data', function (data) {
125 // wait until server is up before we finish setUp
126 if (data.toString().match(/server is up/)) {
127 cb();
128 }
129 });
130
131 },callback,this);
132 },
133 tearDown: function (callback) {
134 this.sock.close();
135 this.acceptor.close();
136 this.ok_to_die = true;
137 if(this.server_up){
138 this.exit_callback_callback = callback;
139 this.server.kill();
140 } else {
141 callback();
142 }
143 },
144
145 send_well_formed_posts: function (test) {
146 test.expect(2);
147
148 // we should integrate a timeout into this
149 this.acceptor.once('connection',function(c){
150 var body = '';
151 c.on('data',function(d){ body += d; });
152 c.on('end',function(){
153 var rows = body.split("\n");
154 var entries = _.map(rows, function(x) {
155 var chunks = x.split(' ');
156 var data = {};
157 data[chunks[0]] = chunks[1];
158 return data;
159 });
160 test.ok(_.include(_.map(entries,function(x) { return _.keys(x)[0] }),'statsd.numStats'),'graphite output includes numStats');
161 test.equal(_.find(entries, function(x) { return _.keys(x)[0] == 'statsd.numStats' })['statsd.numStats'],3);
162 test.done();
163 });
164 });
165 },
166
167 send_malformed_post: function (test) {
168 test.expect(3);
169
170 var testvalue = 1;
171 var me = this;
172 this.acceptor.once('connection',function(c){
173 statsd_send('a_bad_test_value|z',me.sock,'127.0.0.1',8125,function(){
174 collect_for(me.acceptor,me.myflush*3,function(strings){
175 test.ok(strings.length > 0,'should receive some data');
176 var hashes = _.map(strings, function(x) {
177 var chunks = x.split(' ');
178 var data = {};
179 data[chunks[0]] = chunks[1];
180 return data;
181 });
182 var numstat_test = function(post){
183 var mykey = 'statsd.numStats';
184 return _.include(_.keys(post),mykey) && (post[mykey] == 4);
185 };
186 test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 4');
187
188 var bad_lines_seen_value_test = function(post){
189 var mykey = 'stats_counts.statsd.bad_lines_seen';
190 return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
191 };
192 test.ok(_.any(hashes,bad_lines_seen_value_test), 'stats_counts.statsd.bad_lines_seen should be ' + testvalue);
193
194 test.done();
195 });
196 });
197 });
198 },
199
200 timers_are_valid: function (test) {
201 test.expect(3);
202
203 var testvalue = 100;
204 var me = this;
205 this.acceptor.once('connection',function(c){
206 statsd_send('a_test_value:' + testvalue + '|ms',me.sock,'127.0.0.1',8125,function(){
207 collect_for(me.acceptor,me.myflush*3,function(strings){
208 test.ok(strings.length > 0,'should receive some data');
209 var hashes = _.map(strings, function(x) {
210 var chunks = x.split(' ');
211 var data = {};
212 data[chunks[0]] = chunks[1];
213 return data;
214 });
215 var numstat_test = function(post){
216 var mykey = 'statsd.numStats';
217 return _.include(_.keys(post),mykey) && (post[mykey] == 4);
218 };
219 test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 4');
220
221 var testtimervalue_test = function(post){
222 var mykey = 'stats.timers.a_test_value.mean_90';
223 return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
224 };
225 test.ok(_.any(hashes,testtimervalue_test), 'stats.timers.a_test_value.mean should be ' + testvalue);
226
227 test.done();
228 });
229 });
230 });
231 },
232
233 counts_are_valid: function (test) {
234 test.expect(4);
235
236 var testvalue = 100;
237 var me = this;
238 this.acceptor.once('connection',function(c){
239 statsd_send('a_test_value:' + testvalue + '|c',me.sock,'127.0.0.1',8125,function(){
240 collect_for(me.acceptor,me.myflush*3,function(strings){
241 test.ok(strings.length > 0,'should receive some data');
242 var hashes = _.map(strings, function(x) {
243 var chunks = x.split(' ');
244 var data = {};
245 data[chunks[0]] = chunks[1];
246 return data;
247 });
248 var numstat_test = function(post){
249 var mykey = 'statsd.numStats';
250 return _.include(_.keys(post),mykey) && (post[mykey] == 4);
251 };
252 test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 4');
253
254 var testavgvalue_test = function(post){
255 var mykey = 'stats.a_test_value';
256 return _.include(_.keys(post),mykey) && (post[mykey] == (testvalue/(me.myflush / 1000)));
257 };
258 test.ok(_.any(hashes,testavgvalue_test), 'stats.a_test_value should be ' + (testvalue/(me.myflush / 1000)));
259
260 var testcountvalue_test = function(post){
261 var mykey = 'stats_counts.a_test_value';
262 return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
263 };
264 test.ok(_.any(hashes,testcountvalue_test), 'stats_counts.a_test_value should be ' + testvalue);
265
266 test.done();
267 });
268 });
269 });
270 },
271
272 gauges_are_valid: function (test) {
273 test.expect(7);
274
275 var testvalue = "+1";
276 var me = this;
277 this.acceptor.once('connection',function(g){
278 statsd_send('a_test_value:' + testvalue + '|g',me.sock,'127.0.0.1',8125,function(){
279 collect_for(me.acceptor,me.myflush*3,function(strings){
280 test.ok(strings.length > 0,'should receive some data');
281 var hashes = _.map(strings, function(x) {
282 var chunks = x.split(' ');
283 var data = {};
284 data[chunks[0]] = chunks[1];
285 return data;
286 });
287
288 // create an associative array which has the flush cycle number as a key
289 // and a list of hashes of the metrics sent during that flush cycle as value
290 flushes = {};
291 i_number = 0;
292 hashes.forEach(function (item) {
293 key = Object.keys(item)[0]
294 if (key == '') {
295 i_number++;
296 return;
297 }
298 if (! (i_number in flushes)) {
299 flushes[i_number] = [];
300 }
301
302 flushes[i_number].push(item);
303 });
304
305 var testavgvalue_test = function(post){
306 var mykey = 'stats.gauges.a_test_value';
307 return _.include(_.keys(post),mykey) && (post[mykey] == 1);
308 };
309
310 test.ok(_.any(flushes[0],testavgvalue_test), 'stats.gauges.a_test_value after first flush should be ' + 1);
311 test.ok(_.any(flushes[1],testavgvalue_test), 'stats.gauges.a_test_value after second flush should be ' + 1);
312
313 var testavgvalue_test_after_delete = function(post){
314 var mykey = 'stats.gauges.a_test_value';
315 return !(_.include(_.keys(post),mykey));
316 };
317
318 test.ok(_.every(flushes[2],testavgvalue_test_after_delete), 'stats.gauges.a_test_value after third flush should not be present');
319
320 var numstat_test = function(post){
321 var mykey = 'statsd.numStats';
322 return _.include(_.keys(post),mykey) && (post[mykey] == 5);
323 };
324 test.ok(_.any(flushes[0],numstat_test), 'statsd.numStats after first flush should be 5');
325 test.ok(_.any(flushes[1],numstat_test), 'statsd.numStats after second flush should be 5');
326
327 var numstat_test_after_delete = function(post){
328 var mykey = 'statsd.numStats';
329 return _.include(_.keys(post),mykey) && (post[mykey] == 4);
330 };
331 test.ok(_.any(flushes[2],numstat_test_after_delete), 'statsd.numStats after third flush should be 4');
332
333 test.done()
334 });
335
336 });
337 });
338}
339}