UNPKG

11.7 kBJavaScriptView Raw
1let Mixpanel;
2const Sinon = require('sinon');
3const proxyquire = require('proxyquire');
4const https = require('https');
5const events = require('events');
6const httpProxyOrig = process.env.HTTP_PROXY;
7const httpsProxyOrig = process.env.HTTPS_PROXY;
8let HttpsProxyAgent;
9
10exports.send_request = {
11 setUp: function(next) {
12 HttpsProxyAgent = Sinon.stub();
13 Mixpanel = proxyquire('../lib/mixpanel-node', {
14 'https-proxy-agent': HttpsProxyAgent,
15 });
16
17 Sinon.stub(https, 'request');
18
19 this.http_emitter = new events.EventEmitter;
20 this.http_end_spy = Sinon.spy();
21 this.http_write_spy = Sinon.spy();
22 this.http_emitter.write = this.http_write_spy;
23 this.http_emitter.end = this.http_end_spy;
24 this.res = new events.EventEmitter;
25 https.request.returns(this.http_emitter);
26 https.request.callsArgWith(1, this.res);
27
28 this.mixpanel = Mixpanel.init('token');
29
30 next();
31 },
32
33 tearDown: function(next) {
34 https.request.restore();
35
36 // restore proxy variables
37 process.env.HTTP_PROXY = httpProxyOrig;
38 process.env.HTTPS_PROXY = httpsProxyOrig;
39
40 next();
41 },
42
43 "sends correct data on GET": function(test) {
44 var endpoint = "/track",
45 data = {
46 event: 'test',
47 properties: {
48 key1: 'val1',
49 token: 'token',
50 time: 1346876621
51 }
52 },
53 expected_http_request = {
54 method: 'GET',
55 host: 'api.mixpanel.com',
56 headers: {},
57 path: '/track?ip=0&verbose=0&data=eyJldmVudCI6InRlc3QiLCJwcm9wZXJ0aWVzIjp7ImtleTEiOiJ2YWwxIiwidG9rZW4iOiJ0b2tlbiIsInRpbWUiOjEzNDY4NzY2MjF9fQ%3D%3D'
58 };
59
60 this.mixpanel.send_request({ method: 'get', endpoint: endpoint, data: data });
61
62 test.expect(3);
63 test.ok(https.request.calledWithMatch(expected_http_request), "send_request didn't call https.request with correct arguments");
64 test.ok(this.http_end_spy.callCount === 1, "send_request didn't end https.request");
65 test.ok(this.http_write_spy.callCount === 0, "send_request called write for a GET");
66
67 test.done();
68 },
69
70 "defaults to GET": function(test) {
71 var endpoint = "/track",
72 data = {
73 event: 'test',
74 properties: {
75 key1: 'val1',
76 token: 'token',
77 time: 1346876621
78 }
79 },
80 expected_http_request = {
81 method: 'GET',
82 host: 'api.mixpanel.com',
83 headers: {},
84 path: '/track?ip=0&verbose=0&data=eyJldmVudCI6InRlc3QiLCJwcm9wZXJ0aWVzIjp7ImtleTEiOiJ2YWwxIiwidG9rZW4iOiJ0b2tlbiIsInRpbWUiOjEzNDY4NzY2MjF9fQ%3D%3D'
85 };
86
87 this.mixpanel.send_request({ endpoint: endpoint, data: data }); // method option not defined
88
89 test.ok(https.request.calledWithMatch(expected_http_request), "send_request didn't call https.request with correct method argument");
90
91 test.done();
92 },
93
94 "sends correct data on POST": function(test) {
95 var endpoint = "/track",
96 data = {
97 event: 'test',
98 properties: {
99 key1: 'val1',
100 token: 'token',
101 time: 1346876621
102 }
103 },
104 expected_http_request = {
105 method: 'POST',
106 host: 'api.mixpanel.com',
107 headers: {},
108 path: '/track?ip=0&verbose=0'
109 },
110 expected_http_request_body = "data=eyJldmVudCI6InRlc3QiLCJwcm9wZXJ0aWVzIjp7ImtleTEiOiJ2YWwxIiwidG9rZW4iOiJ0b2tlbiIsInRpbWUiOjEzNDY4NzY2MjF9fQ==";
111
112 this.mixpanel.send_request({ method: 'post', endpoint: endpoint, data: data });
113
114 test.expect(3);
115 test.ok(https.request.calledWithMatch(expected_http_request), "send_request didn't call https.request with correct arguments");
116 test.ok(this.http_end_spy.callCount === 1, "send_request didn't end https.request");
117 test.ok(this.http_write_spy.calledWithExactly(expected_http_request_body), "send_request did not write data correctly for a POST");
118
119 test.done();
120 },
121
122 "handles mixpanel errors": function(test) {
123 test.expect(1);
124 this.mixpanel.send_request({ endpoint: "/track", data: { event: "test" } }, function(e) {
125 test.equal(e.message, 'Mixpanel Server Error: 0', "error did not get passed back to callback");
126 test.done();
127 });
128
129 this.res.emit('data', '0');
130 this.res.emit('end');
131 },
132
133 "handles https.request errors": function(test) {
134 test.expect(1);
135 this.mixpanel.send_request({ endpoint: "/track", data: { event: "test" } }, function(e) {
136 test.equal(e, 'error', "error did not get passed back to callback");
137 test.done();
138 });
139
140 this.http_emitter.emit('error', 'error');
141 },
142
143 "default use keepAlive agent": function(test) {
144 test.expect(2);
145 var agent = new https.Agent({ keepAlive: false });
146 var httpsStub = {
147 request: Sinon.stub().returns(this.http_emitter).callsArgWith(1, this.res),
148 Agent: Sinon.stub().returns(agent),
149 };
150 // force SDK not use `undefined` string to initialize proxy-agent
151 delete process.env.HTTP_PROXY
152 delete process.env.HTTPS_PROXY
153 Mixpanel = proxyquire('../lib/mixpanel-node', {
154 'https': httpsStub
155 });
156 var proxyMixpanel = Mixpanel.init('token');
157 proxyMixpanel.send_request({ endpoint: '', data: {} });
158
159 var getConfig = httpsStub.request.firstCall.args[0];
160 var agentOpts = httpsStub.Agent.firstCall.args[0];
161 test.ok(agentOpts.keepAlive === true, "HTTP Agent wasn't initialized with keepAlive by default");
162 test.ok(getConfig.agent === agent, "send_request didn't call https.request with agent");
163
164 test.done();
165 },
166
167 "uses correct hostname": function(test) {
168 var host = 'testhost.fakedomain';
169 var customHostnameMixpanel = Mixpanel.init('token', { host: host });
170 var expected_http_request = {
171 host: host
172 };
173
174 customHostnameMixpanel.send_request({ endpoint: "", data: {} });
175
176 test.ok(https.request.calledWithMatch(expected_http_request), "send_request didn't call https.request with correct hostname");
177
178 test.done();
179 },
180
181 "uses correct port": function(test) {
182 var host = 'testhost.fakedomain:1337';
183 var customHostnameMixpanel = Mixpanel.init('token', { host: host });
184 var expected_http_request = {
185 host: 'testhost.fakedomain',
186 port: 1337
187 };
188
189 customHostnameMixpanel.send_request({ endpoint: "", data: {} });
190
191 test.ok(https.request.calledWithMatch(expected_http_request), "send_request didn't call https.request with correct hostname and port");
192
193 test.done();
194 },
195
196 "uses correct path": function(test) {
197 var host = 'testhost.fakedomain';
198 var customPath = '/mypath';
199 var customHostnameMixpanel = Mixpanel.init('token', {
200 host,
201 path: customPath,
202 });
203 var expected_http_request = {
204 host,
205 path: '/mypath?ip=0&verbose=0&data=e30%3D',
206 };
207
208 customHostnameMixpanel.send_request({endpoint: "", data: {}});
209 test.ok(https.request.calledWithMatch(expected_http_request), "send_request didn't call https.request with correct hostname and port");
210
211 test.done();
212 },
213
214 "combines custom path and endpoint": function(test) {
215 var host = 'testhost.fakedomain';
216 var customPath = '/mypath';
217 var customHostnameMixpanel = Mixpanel.init('token', {
218 host,
219 path: customPath,
220 });
221 var expected_http_request = {
222 host,
223 path: '/mypath/track?ip=0&verbose=0&data=e30%3D',
224 };
225
226 customHostnameMixpanel.send_request({endpoint: '/track', data: {}});
227 test.ok(https.request.calledWithMatch(expected_http_request), "send_request didn't call https.request with correct hostname and port");
228
229 test.done();
230 },
231
232 "uses HTTP_PROXY if set": function(test) {
233 HttpsProxyAgent.reset(); // Mixpanel is instantiated in setup, need to reset callcount
234 delete process.env.HTTPS_PROXY;
235 process.env.HTTP_PROXY = 'this.aint.real.https';
236
237 var proxyMixpanel = Mixpanel.init('token');
238 proxyMixpanel.send_request({ endpoint: '', data: {} });
239
240 test.ok(HttpsProxyAgent.calledOnce, "HttpsProxyAgent was not called when process.env.HTTP_PROXY was set");
241
242 var agentOpts = HttpsProxyAgent.firstCall.args[0];
243 test.ok(agentOpts.pathname === "this.aint.real.https", "HttpsProxyAgent was not called with the correct proxy path");
244 test.ok(agentOpts.keepAlive === true, "HttpsProxyAgent was not called with the correct proxy path");
245
246 var getConfig = https.request.firstCall.args[0];
247 test.ok(getConfig.agent !== undefined, "send_request didn't call https.request with agent");
248
249 test.done();
250 },
251
252 "uses HTTPS_PROXY if set": function(test) {
253 HttpsProxyAgent.reset(); // Mixpanel is instantiated in setup, need to reset callcount
254 delete process.env.HTTP_PROXY;
255 process.env.HTTPS_PROXY = 'this.aint.real.https';
256
257 var proxyMixpanel = Mixpanel.init('token');
258 proxyMixpanel.send_request({ endpoint: '', data: {} });
259
260 test.ok(HttpsProxyAgent.calledOnce, "HttpsProxyAgent was not called when process.env.HTTPS_PROXY was set");
261
262 var proxyOpts = HttpsProxyAgent.firstCall.args[0];
263 test.ok(proxyOpts.pathname === 'this.aint.real.https', "HttpsProxyAgent was not called with the correct proxy path");
264
265 var getConfig = https.request.firstCall.args[0];
266 test.ok(getConfig.agent !== undefined, "send_request didn't call https.request with agent");
267
268 test.done();
269 },
270
271 "requires credentials for import requests": function(test) {
272 test.throws(
273 this.mixpanel.send_request.bind(this, {
274 endpoint: `/import`,
275 data: {event: `test event`},
276 }),
277 /The Mixpanel Client needs a Mixpanel API Secret when importing old events/,
278 "import request didn't throw error when no credentials provided"
279 );
280 test.done();
281 },
282
283 "sets basic auth header if API secret is provided": function(test) {
284 this.mixpanel.set_config({secret: `foobar`});
285 this.mixpanel.send_request({
286 endpoint: `/import`,
287 data: {event: `test event`},
288 });
289 test.ok(https.request.calledOnce);
290 test.deepEqual(https.request.args[0][0].headers, {
291 'Authorization': `Basic Zm9vYmFyOg==`, // base64 of "foobar:"
292 }, "send_request didn't pass correct auth header to https.request");
293 test.done();
294 },
295
296 "still supports import with api_key (legacy)": function(test) {
297 this.mixpanel.set_config({key: `barbaz`});
298 this.mixpanel.send_request({
299 endpoint: `/import`,
300 data: {},
301 });
302 test.ok(https.request.calledOnce);
303 test.equal(
304 https.request.args[0][0].path,
305 `/import?ip=0&verbose=0&data=e30%3D&api_key=barbaz`,
306 "send_request didn't pass correct query params to https.request"
307 );
308 test.done();
309 },
310};