1 | var 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 |
|
13 | var 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 |
|
24 | var 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 |
|
30 | var 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 |
|
41 |
|
42 | var 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 |
|
71 | module.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 |
|
113 |
|
114 |
|
115 |
|
116 | obj.server.stdout.on('data', function (data) {
|
117 |
|
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 |
|
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 | }
|