UNPKG

11.8 kBJavaScriptView Raw
1var assert = require('assert')
2 , Twit = require('../lib/twitter')
3 , config1 = require('../config1')
4 , config2 = require('../config2')
5 , colors = require('colors')
6 , util = require('util')
7 , async = require('async')
8 , restTest = require('./rest')
9
10/**
11 * Stop the stream and check the tweet we got back.
12 * Call @done on completion.
13 *
14 * @param {object} stream object returned by twit.stream()
15 * @param {Function} done completion callback
16 */
17exports.checkStream = function (stream, done) {
18 stream.on('connected', function () {
19 console.log('\nconnected'.grey)
20 });
21
22 stream.once('tweet', function (tweet) {
23 assert.equal(null, stream.abortedBy)
24
25 stream.stop()
26
27 assert.equal('twit-client', stream.abortedBy)
28 assert.ok(tweet)
29 assert.equal('string', typeof tweet.text)
30 assert.equal('string', typeof tweet.id_str)
31
32 console.log(('\ntweet: '+tweet.text).grey)
33
34 done()
35 });
36
37 stream.on('reconnect', function (req, res) {
38 console.log('Got disconnected. Scheduling reconnect! statusCode:', res.statusCode)
39 });
40}
41
42var generateRandomString = function (length) {
43 var length = length || 10
44 var ret = ''
45 for (var i = 0; i < length; i++) {
46 // use an easy set of unicode as an alphabet - twitter won't reformat them
47 // which makes testing easier
48 ret += String.fromCharCode(Math.floor(Math.random()*90) + 33)
49 }
50
51 ret = encodeURI(ret)
52
53 return ret
54}
55
56describe('Streaming API', function () {
57
58 it('statuses/sample', function (done) {
59 var twit = new Twit(config1);
60 var stream = twit.stream('statuses/sample')
61
62 exports.checkStream(stream, done)
63 })
64
65 it('statuses/filter using `track`', function (done) {
66 this.timeout(120000)
67 var twit = new Twit(config2);
68 var stream = twit.stream('statuses/filter', { track: 'fun' })
69
70 exports.checkStream(stream, done)
71 })
72
73 it('statuses/filter using `locations` string', function (done) {
74 var twit = new Twit(config1);
75 var world = '-180,-90,180,90';
76 var stream = twit.stream('statuses/filter', { locations: world })
77
78 exports.checkStream(stream, done)
79 })
80
81 it('statuses/filter using `locations` array for San Francisco and New York', function (done) {
82 var twit = new Twit(config2);
83 var params = {
84 locations: [ '-122.75', '36.8', '121.75', '37.8', '-74', '40', '73', '41' ]
85 }
86
87 var stream = twit.stream('statuses/filter', params)
88
89 exports.checkStream(stream, done)
90 })
91
92 it('statuses/filter using `track` array', function (done) {
93 var twit = new Twit(config1);
94 var params = {
95 track: [ 'spring', 'summer', 'fall', 'winter', 'weather', 'joy', 'laugh', 'sleep' ]
96 }
97
98 var stream = twit.stream('statuses/filter', params)
99
100 exports.checkStream(stream, done)
101 })
102
103 it('statuses/filter using `track` and `language`', function (done) {
104 var twit = new Twit(config1);
105 var params = {
106 track: [ '#apple', 'google', 'twitter', 'facebook', 'happy', 'party', ':)' ],
107 language: 'en'
108 }
109
110 var stream = twit.stream('statuses/filter', params)
111
112 exports.checkStream(stream, done)
113 })
114
115 it('stopping & restarting the stream works', function (done) {
116 var twit = new Twit(config2);
117 var stream = twit.stream('statuses/sample')
118
119 //stop the stream after 2 seconds
120 setTimeout(function () {
121 assert.equal(null, stream.abortedBy)
122 stream.stop()
123 assert.equal('twit-client', stream.abortedBy)
124 util.puts('\nstopped stream')
125 }, 2000)
126
127 //after 3 seconds, start the stream, and stop after 'connect'
128 setTimeout(function () {
129 stream.once('connected', function (req) {
130 util.puts('\nrestarted stream')
131 stream.stop()
132 assert.equal('twit-client', stream.abortedBy)
133 util.puts('\nstopped stream')
134 done()
135 })
136
137 //restart the stream
138 stream.start()
139 }, 3000)
140 })
141
142 it('stopping & restarting stream emits to previously assigned callbacks', function (done) {
143 var twit = new Twit(config2);
144 var stream = twit.stream('statuses/sample')
145
146 var started = false
147 var numTweets = 0
148 stream.on('tweet', function (tweet) {
149 process.stdout.write('.')
150 if (!started) {
151 started = true
152 numTweets++
153 console.log('received tweet', numTweets)
154
155 console.log('stopping stream')
156 stream.stop()
157
158 // we've successfully received a new tweet after restarting, test successful
159 if (numTweets === 2) {
160 done()
161 } else {
162 started = false
163 console.log('restarting stream')
164
165 setTimeout(function () {
166 stream.start()
167 }, 1000)
168 }
169 }
170 })
171
172 stream.on('limit', function (limitMsg) {
173 console.log('limit', limitMsg)
174 })
175
176 stream.on('disconnect', function (disconnMsg) {
177 console.log('disconnect', disconnMsg)
178 })
179
180 stream.on('reconnect', function (req, res, ival) {
181 console.log('reconnect. statusCode:', res.statusCode, 'interval:', ival)
182 })
183
184 stream.on('connect', function (req) {
185 console.log('connect')
186 })
187
188 })
189})
190
191describe('streaming API events', function () {
192 var senderScreenName
193 var receiverScreenName
194
195 var dmId
196 var twit, twit2
197
198 before(function (done) {
199 // before we send direct messages the user receiving the msg
200 // has to follow the sender
201 twit = new Twit(config1);
202 twit2 = new Twit(config2);
203
204 async.parallel({
205 // get sender screen name and set it for tests to use
206 getSenderScreenName: function (parNext) {
207 console.log('getting first screen_name')
208 twit.get('account/verify_credentials', function (err, reply) {
209 assert(!err, err)
210
211 assert(reply)
212 assert(reply.screen_name)
213
214 senderScreenName = reply.screen_name
215
216 return parNext()
217 })
218 },
219 // get receiver screen name and set it for tests to use
220 getReceiverScreenName: function (parNext) {
221 console.log('getting second screen_name')
222 twit2.get('account/verify_credentials', function (err, reply) {
223 assert(!err, err)
224
225 assert(reply)
226 assert(reply.screen_name)
227
228 receiverScreenName = reply.screen_name
229
230 return parNext()
231 })
232 }
233 }, function (err) {
234 assert(!err, err)
235
236 var followParams = { screen_name: senderScreenName }
237 console.log('making second user follow first one so first can DM')
238 // make receiver (twit2) follow sender (twit)
239 twit2.post('friendships/create', followParams, function (err, reply) {
240 assert(!err, err)
241 assert(reply.following)
242
243 done()
244 })
245 })
246 })
247
248 it('direct_message event', function (done) {
249 this.timeout(0);
250
251 var makeDmParams = function () {
252 return {
253 screen_name: receiverScreenName,
254 text: 'direct message streaming event test! :-) ' + generateRandomString(10),
255 twit_options: {
256 retry: true
257 }
258 }
259 }
260
261 var dmId = null
262 var dmsSent = []
263
264 // start listening for user stream events
265 var receiverStream = twit2.stream('user')
266
267 receiverStream.on('reconnect', function (request, response, connectInterval) {
268 console.log('stream reconnect: response status code', response.statusCode, 'connecting in', connectInterval)
269 });
270
271 console.log('\nlistening for DMs')
272 // listen for direct_message event and check DM once it's received
273 receiverStream.once('direct_message', function (directMsg) {
274 console.log('got DM', directMsg.direct_message.text)
275 restTest.checkDm(directMsg.direct_message)
276
277 // make sure one of the DMs sent was found
278 // (we can send multiple DMs if our stream has to reconnect)
279 var sentDmFound = dmsSent.some(function (dm) {
280 return (
281 directMsg.direct_message.text === dm.text &&
282 directMsg.direct_message.sender.screen_name === dm.sender.screen_name
283 )
284 })
285
286 if (!sentDmFound) {
287 console.log('this DM doesnt match our test DMs - still waiting for a matching one.')
288 }
289
290 if (sentDmFound) {
291 receiverStream.stop()
292 return done()
293 }
294 })
295
296 receiverStream.on('connected', function () {
297 var sendDm = function () {
298 var dmParams = makeDmParams()
299
300 twit.post('direct_messages/new', dmParams, function (err, reply) {
301 assert(!err, err)
302 assert(reply)
303 restTest.checkDm(reply)
304
305 // we will check this dm against the reply recieved in the message event
306 dmsSent.push(reply)
307
308 console.log('posted DM, dmId:', reply.text, reply.id_str)
309
310 dmId = reply.id_str
311 assert(dmId)
312 })
313 }
314
315 if (dmId) {
316 console.log('stream is connected again. deleting dmId', dmId)
317 // if we already sent a DM it means we got disconnected and now the stream is reconnected.
318 // Since we want to test listening on a DM event, destroy and recreate the
319 // message now that we're connected and can listen for it.
320 twit.post('direct_messages/destroy', { id: dmId }, function (err, reply) {
321
322 if (!err || err.statusCode !== 404) {
323 assert(!err, err)
324 restTest.checkDm(reply)
325 assert.equal(reply.id, dmId)
326 console.log('deleted DM', dmId)
327 }
328
329 dmId = null
330
331 sendDm()
332 })
333 } else {
334 // first execution of `connected` handler - send the DM
335 sendDm()
336 }
337 })
338
339 after(function (done) {
340 console.log('\ndeleting DM')
341 assert(dmId)
342 assert.equal(typeof dmId, 'string')
343 twit.post('direct_messages/destroy', { id: dmId }, function (err, reply) {
344 assert(!err, err)
345 restTest.checkDm(reply)
346 assert.equal(reply.id, dmId)
347
348 return done()
349 })
350 })
351 })
352})
353
354describe('streaming API bad request', function (done) {
355 it('emits an error for a 401 response', function (done) {
356 var badCredentials = {
357 consumer_key: 'a'
358 , consumer_secret: 'b'
359 , access_token: 'c'
360 , access_token_secret: 'd'
361 }
362
363 var twit = new Twit(badCredentials);
364
365 var stream = twit.stream('statuses/filter', { track : ['foo'] });
366
367 stream.on('error', function (err) {
368 assert.equal(err.response.statusCode, 401)
369
370 return done()
371 })
372 })
373})
374
375describe.skip('streaming reconnect', function (done) {
376
377 it('correctly implements 420 backoff', function (done) {
378 var twit = new Twit(config1);
379
380 var stream = twit.stream('statuses/filter', { track: [ 'fun', 'yolo']});
381
382 var expectedInterval = 0;
383
384 var numReconnectsTested = 0;
385 var numReconnectsToTest = 3;
386
387 stream.on('connected', function (res) {
388 // simulate twitter closing the connection with 420 status
389 res.statusCode = 420;
390 stream.request.abort()
391
392 // wait a bit before before checking if our connect interval got set
393 setTimeout(function () {
394 expectedInterval = expectedInterval ? 2*expectedInterval : 60000;
395
396 // make sure our connect interval is correct
397 assert.equal(stream.connectInterval, expectedInterval);
398 console.log('420 rate limiting backoff:', stream.connectInterval);
399
400 // simulate `scheduleReconnect` timer being called
401 stream.keepAlive();
402 delete stream.scheduledReconnect
403
404 numReconnectsTested += 1;
405
406 if (numReconnectsTested === numReconnectsToTest) {
407 return done();
408 }
409 }, 100);
410 });
411 });
412})
\No newline at end of file