rest.js (26089B)
1 var assert = require('assert') 2 , EventEmitter = require('events').EventEmitter 3 , fs = require('fs') 4 , sinon = require('sinon') 5 , Twit = require('../lib/twitter') 6 , config1 = require('../config1') 7 , config2 = require('../config2') 8 , helpers = require('./helpers') 9 , util = require('util') 10 , async = require('async') 11 12 describe('REST API', function () { 13 var twit = null 14 15 before(function () { 16 twit = new Twit(config1); 17 }) 18 19 it('GET `account/verify_credentials`', function (done) { 20 twit.get('account/verify_credentials', function (err, reply, response) { 21 checkReply(err, reply) 22 assert.notEqual(reply.followers_count, undefined) 23 assert.notEqual(reply.friends_count, undefined) 24 assert.ok(reply.id_str) 25 26 checkResponse(response) 27 28 assert(response.headers['x-rate-limit-limit']) 29 done() 30 }) 31 }) 32 33 it('GET `account/verify_credentials` using promise API only', function (done) { 34 twit 35 .get('account/verify_credentials', { skip_status: true }) 36 .catch(function (err) { 37 console.log('catch err', err.stack) 38 }) 39 .then(function (result) { 40 checkReply(null, result.data) 41 assert.notEqual(result.data.followers_count, undefined) 42 assert.notEqual(result.data.friends_count, undefined) 43 assert.ok(result.data.id_str) 44 checkResponse(result.resp) 45 assert(result.resp.headers['x-rate-limit-limit']) 46 47 done() 48 }) 49 }) 50 51 it.skip('GET `account/verify_credentials` using promise API AND callback API', function (done) { 52 var i = 0; 53 54 var _checkDataAndResp = function (data, resp) { 55 checkReply(null, data) 56 assert.notEqual(data.followers_count, undefined) 57 assert.notEqual(data.friends_count, undefined) 58 assert.ok(data.id_str) 59 checkResponse(resp) 60 assert(resp.headers['x-rate-limit-limit']) 61 62 i++; 63 if (i == 2) { 64 done() 65 } 66 } 67 68 var get = twit.get('account/verify_credentials', function (err, data, resp) { 69 assert(!err, err); 70 _checkDataAndResp(data, resp); 71 }); 72 get.catch(function (err) { 73 console.log('Got error:', err.stack) 74 }); 75 get.then(function (result) { 76 _checkDataAndResp(result.data, result.resp); 77 }); 78 }) 79 80 it('POST `account/update_profile`', function (done) { 81 twit.post('account/update_profile', function (err, reply, response) { 82 checkReply(err, reply) 83 console.log('\nscreen name:', reply.screen_name) 84 checkResponse(response) 85 done() 86 }) 87 }) 88 89 it('POST `statuses/update` and POST `statuses/destroy:id`', function (done) { 90 var tweetId = null 91 92 var params = { 93 status: '@tolga_tezel tweeting using github.com/ttezel/twit. ' + helpers.generateRandomString(7) 94 } 95 twit.post('statuses/update', params, function (err, reply, response) { 96 checkReply(err, reply) 97 console.log('\ntweeted:', reply.text) 98 99 tweetId = reply.id_str 100 assert(tweetId) 101 checkResponse(response) 102 103 var deleteParams = { id: tweetId } 104 // Try up to 2 times to delete the tweet. 105 // Even after a 200 response to statuses/update is returned, a delete might 404 so we retry. 106 exports.req_with_retries(twit, 2, 'post', 'statuses/destroy/:id', deleteParams, [404], function (err, body, response) { 107 checkReply(err, body) 108 checkTweet(body) 109 checkResponse(response) 110 111 done() 112 }) 113 }) 114 }) 115 116 it('POST `statuses/update` with characters requiring escaping', function (done) { 117 var params = { status: '@tolga_tezel tweeting using github.com/ttezel/twit :) !' + helpers.generateRandomString(15) } 118 119 twit.post('statuses/update', params, function (err, reply, response) { 120 checkReply(err, reply) 121 122 console.log('\ntweeted:', reply.text) 123 124 checkResponse(response) 125 126 var text = reply.text 127 128 assert(reply.id_str) 129 130 twit.post('statuses/destroy/:id', { id: reply.id_str }, function (err, reply, response) { 131 checkReply(err, reply) 132 checkTweet(reply) 133 assert.equal(reply.text, text) 134 135 checkResponse(response) 136 137 done() 138 }) 139 }) 140 }) 141 142 it('POST `statuses/update` with \'Hi!!\' works', function (done) { 143 var params = { status: 'Hi!!' } 144 145 twit.post('statuses/update', params, function (err, reply, response) { 146 checkReply(err, reply) 147 148 console.log('\ntweeted:', reply.text) 149 150 checkResponse(response) 151 152 var text = reply.text 153 154 var destroyRoute = 'statuses/destroy/'+reply.id_str 155 156 twit.post(destroyRoute, function (err, reply, response) { 157 checkReply(err, reply) 158 checkTweet(reply) 159 assert.equal(reply.text, text) 160 161 checkResponse(response) 162 163 done() 164 }) 165 }) 166 }) 167 168 it('GET `statuses/home_timeline`', function (done) { 169 twit.get('statuses/home_timeline', function (err, reply, response) { 170 checkReply(err, reply) 171 checkTweet(reply[0]) 172 173 checkResponse(response) 174 175 done() 176 }) 177 }) 178 179 it('GET `statuses/mentions_timeline`', function (done) { 180 twit.get('statuses/mentions_timeline', function (err, reply, response) { 181 checkReply(err, reply) 182 checkTweet(reply[0]) 183 done() 184 }) 185 }) 186 187 it('GET `statuses/user_timeline`', function (done) { 188 var params = { 189 screen_name: 'tolga_tezel' 190 } 191 192 twit.get('statuses/user_timeline', params, function (err, reply, response) { 193 checkReply(err, reply) 194 checkTweet(reply[0]) 195 196 checkResponse(response) 197 198 done() 199 }) 200 }) 201 202 it('GET `search/tweets` { q: "a", since_id: 12345 }', function (done) { 203 var params = { q: 'a', since_id: 12345 } 204 twit.get('search/tweets', params, function (err, reply, response) { 205 checkReply(err, reply) 206 assert.ok(reply.statuses) 207 checkTweet(reply.statuses[0]) 208 209 checkResponse(response) 210 211 done() 212 }) 213 }) 214 215 it('GET `search/tweets` { q: "fun since:2011-11-11" }', function (done) { 216 var params = { q: 'fun since:2011-11-11', count: 100 } 217 twit.get('search/tweets', params, function (err, reply, response) { 218 checkReply(err, reply) 219 assert.ok(reply.statuses) 220 checkTweet(reply.statuses[0]) 221 222 console.log('\nnumber of fun statuses:', reply.statuses.length) 223 224 checkResponse(response) 225 226 done() 227 }) 228 }) 229 230 it('GET `search/tweets`, using `q` array', function (done) { 231 var params = { 232 q: [ 'banana', 'mango', 'peach' ] 233 } 234 235 twit.get('search/tweets', params, function (err, reply, response) { 236 checkReply(err, reply) 237 assert.ok(reply.statuses) 238 checkTweet(reply.statuses[0]) 239 240 checkResponse(response) 241 242 done() 243 }) 244 }) 245 246 it('GET `search/tweets` with count set to 100', function (done) { 247 var params = { 248 q: 'happy', 249 count: 100 250 } 251 252 twit.get('search/tweets', params, function (err, reply, res) { 253 checkReply(err, reply) 254 console.log('\nnumber of tweets from search:', reply.statuses.length) 255 // twitter won't always send back 100 tweets if we ask for 100, 256 // but make sure it's close to 100 257 assert(reply.statuses.length > 95) 258 259 done() 260 }) 261 }) 262 263 it('GET `search/tweets` with geocode', function (done) { 264 var params = { 265 q: 'apple', geocode: [ '37.781157', '-122.398720', '1mi' ] 266 } 267 268 twit.get('search/tweets', params, function (err, reply) { 269 checkReply(err, reply) 270 271 done() 272 }) 273 }) 274 275 it('GET `direct_messages`', function (done) { 276 twit.get('direct_messages', function (err, reply, response) { 277 checkResponse(response) 278 checkReply(err, reply) 279 assert.ok(Array.isArray(reply)) 280 done() 281 }) 282 }) 283 284 it('GET `followers/ids`', function (done) { 285 twit.get('followers/ids', function (err, reply, response) { 286 checkReply(err, reply) 287 assert.ok(Array.isArray(reply.ids)) 288 289 checkResponse(response) 290 291 done() 292 }) 293 }) 294 295 it('GET `followers/ids` of screen_name tolga_tezel', function (done) { 296 twit.get('followers/ids', { screen_name: 'tolga_tezel' }, function (err, reply, response) { 297 checkReply(err, reply) 298 assert.ok(Array.isArray(reply.ids)) 299 300 checkResponse(response) 301 302 done() 303 }) 304 }) 305 306 it('POST `statuses/retweet`', function (done) { 307 // search for a tweet to retweet 308 twit.get('search/tweets', { q: 'apple' }, function (err, reply, response) { 309 checkReply(err, reply) 310 assert.ok(reply.statuses) 311 312 var tweet = reply.statuses[0] 313 checkTweet(tweet) 314 315 var tweetId = tweet.id_str 316 assert(tweetId) 317 318 twit.post('statuses/retweet/'+tweetId, function (err, reply, response) { 319 checkReply(err, reply) 320 321 var retweetId = reply.id_str 322 assert(retweetId) 323 324 twit.post('statuses/destroy/'+retweetId, function (err, reply, response) { 325 checkReply(err, reply) 326 327 done() 328 }) 329 }) 330 }) 331 }) 332 333 // 1.1.8 usage 334 it('POST `statuses/retweet/:id` without `id` in params returns error', function (done) { 335 twit.post('statuses/retweet/:id', function (err, reply, response) { 336 assert(err) 337 assert.equal(err.message, 'Twit: Params object is missing a required parameter for this request: `id`') 338 done() 339 }) 340 }) 341 342 // 1.1.8 usage 343 it('POST `statuses/retweet/:id`', function (done) { 344 // search for a tweet to retweet 345 twit.get('search/tweets', { q: 'banana' }, function (err, reply, response) { 346 checkReply(err, reply) 347 assert.ok(reply.statuses) 348 349 var tweet = reply.statuses[0] 350 checkTweet(tweet) 351 352 var tweetId = tweet.id_str 353 assert(tweetId) 354 355 twit.post('statuses/retweet/:id', { id: tweetId }, function (err, reply) { 356 checkReply(err, reply) 357 358 var retweetId = reply.id_str 359 assert(retweetId) 360 361 twit.post('statuses/destroy/:id', { id: retweetId }, function (err, reply, response) { 362 checkReply(err, reply) 363 364 done() 365 }) 366 }) 367 }) 368 }) 369 370 // 1.1.8 usage 371 // skip for now since this API call is having problems on Twitter's side (404) 372 it.skip('GET `users/suggestions/:slug`', function (done) { 373 twit.get('users/suggestions/:slug', { slug: 'funny' }, function (err, reply, res) { 374 checkReply(err, reply) 375 assert.equal(reply.slug, 'funny') 376 done() 377 }) 378 }) 379 380 // 1.1.8 usage 381 // skip for now since this API call is having problems on Twitter's side (404) 382 it.skip('GET `users/suggestions/:slug/members`', function (done) { 383 twit.get('users/suggestions/:slug/members', { slug: 'funny' }, function (err, reply, res) { 384 checkReply(err, reply) 385 386 assert(reply[0].id_str) 387 assert(reply[0].screen_name) 388 389 done() 390 }) 391 }) 392 393 // 1.1.8 usage 394 it('GET `geo/id/:place_id`', function (done) { 395 var placeId = 'df51dec6f4ee2b2c' 396 397 twit.get('geo/id/:place_id', { place_id: placeId }, function (err, reply, res) { 398 checkReply(err, reply) 399 400 assert(reply.country) 401 assert(reply.bounding_box) 402 assert.equal(reply.id, placeId) 403 404 done() 405 }) 406 }) 407 408 it('POST `direct_messages/new`', function (done) { 409 var dmId 410 411 async.series({ 412 postDm: function (next) { 413 414 var dmParams = { 415 screen_name: 'tolga_tezel', 416 text: 'hey this is a direct message from twit! :) ' + helpers.generateRandomString(15) 417 } 418 // post a direct message from the sender's account 419 twit.post('direct_messages/new', dmParams, function (err, reply) { 420 assert(!err, err) 421 assert(reply) 422 423 dmId = reply.id_str 424 425 exports.checkDm(reply) 426 427 assert.equal(reply.text, dmParams.text) 428 assert(dmId) 429 430 return next() 431 }) 432 }, 433 deleteDm: function (next) { 434 twit.post('direct_messages/destroy', { id: dmId }, function (err, reply) { 435 assert(!err, err) 436 exports.checkDm(reply) 437 assert.equal(reply.id, dmId) 438 439 return next() 440 }) 441 } 442 }, done); 443 }) 444 445 describe('Url construction', function () { 446 var twit = null 447 var parameters = { 448 elem1: 'hello world', 449 foo: 'bar' 450 } 451 452 before(function () { 453 twit = new Twit(config1) 454 }) 455 456 it('adds query parameters to url', function (done) { 457 var resp = twit._buildReqOpts('POST', 'account/verify_credentials', parameters, false, function (err, data) { 458 assert.equal(data.url, 'https://api.twitter.com/1.1/account/verify_credentials.json?elem1=hello%20world&foo=bar') 459 done() 460 }) 461 }) 462 463 it('does not add query parameters to url when json should be in the payload', function (done) { 464 var resp = twit._buildReqOpts('POST', 'direct_messages/welcome_messages/new', parameters, false, function (err, data) { 465 assert.equal(data.url, 'https://api.twitter.com/1.1/direct_messages/welcome_messages/new.json') 466 done() 467 }) 468 }) 469 }) 470 471 describe('Media Upload', function () { 472 var twit = null 473 474 before(function () { 475 twit = new Twit(config1) 476 }) 477 478 it('POST media/upload with png', function (done) { 479 var b64content = fs.readFileSync(__dirname + '/img/cutebird.png', { encoding: 'base64' }) 480 481 twit.post('media/upload', { media_data: b64content }, function (err, data, response) { 482 assert.equal(response.statusCode, 200) 483 assert(!err, err) 484 exports.checkMediaUpload(data) 485 assert(data.image.image_type == 'image/png' || data.image.image_type == 'image\/png') 486 done() 487 }) 488 }) 489 490 it('POST media/upload with JPG', function (done) { 491 var b64content = fs.readFileSync(__dirname + '/img/bigbird.jpg', { encoding: 'base64' }) 492 493 twit.post('media/upload', { media_data: b64content }, function (err, data, response) { 494 assert(!err, err) 495 exports.checkMediaUpload(data) 496 assert.equal(data.image.image_type, 'image/jpeg') 497 done() 498 }) 499 }) 500 501 it('POST media/upload with static GIF', function (done) { 502 var b64content = fs.readFileSync(__dirname + '/img/twitterbird.gif', { encoding: 'base64' }) 503 504 twit.post('media/upload', { media_data: b64content }, function (err, data, response) { 505 assert(!err, err) 506 exports.checkMediaUpload(data) 507 assert.equal(data.image.image_type, 'image/gif') 508 done() 509 }) 510 }) 511 512 it('POST media/upload with animated GIF using `media_data` parameter', function (done) { 513 var b64content = fs.readFileSync(__dirname + '/img/snoopy-animated.gif', { encoding: 'base64' }) 514 515 twit.post('media/upload', { media_data: b64content }, function (err, data, response) { 516 assert(!err, err) 517 exports.checkMediaUpload(data) 518 var expected_image_types = ['image/gif', 'image/animatedgif'] 519 var image_type = data.image.image_type 520 assert.ok(expected_image_types.indexOf(image_type) !== -1, 'got unexpected image type:' + image_type) 521 done() 522 }) 523 }) 524 525 it('POST media/upload with animated GIF, then POST a tweet referencing the media', function (done) { 526 var b64content = fs.readFileSync(__dirname + '/img/snoopy-animated.gif', { encoding: 'base64' }); 527 528 twit.post('media/upload', { media_data: b64content }, function (err, data, response) { 529 assert(!err, err) 530 exports.checkMediaUpload(data) 531 var expected_image_types = ['image/gif', 'image/animatedgif'] 532 var image_type = data.image.image_type 533 assert.ok(expected_image_types.indexOf(image_type) !== -1, 'got unexpected image type:' + image_type) 534 535 var mediaIdStr = data.media_id_string 536 assert(mediaIdStr) 537 var params = { status: '#nofilter', media_ids: [mediaIdStr] } 538 twit.post('statuses/update', params, function (err, data, response) { 539 assert(!err, err) 540 var tweetIdStr = data.id_str 541 assert(tweetIdStr) 542 543 exports.req_with_retries(twit, 3, 'post', 'statuses/destroy/:id', { id: tweetIdStr }, [404], function (err, data, response) { 544 checkReply(err, data) 545 done() 546 }) 547 }) 548 }) 549 }) 550 551 it('POST media/upload with animated GIF using `media` parameter', function (done) { 552 var b64Content = fs.readFileSync(__dirname + '/img/snoopy-animated.gif', { encoding: 'base64' }); 553 554 twit.post('media/upload', { media: b64Content }, function (err, data, response) { 555 assert(!err, err) 556 exports.checkMediaUpload(data) 557 var expected_image_types = ['image/gif', 'image/animatedgif'] 558 var image_type = data.image.image_type 559 assert.ok(expected_image_types.indexOf(image_type) !== -1, 'got unexpected image type:' + image_type) 560 done() 561 }) 562 }) 563 564 it('POST media/upload with JPG, then POST media/metadata/create with alt text', function (done) { 565 var b64content = fs.readFileSync(__dirname + '/img/bigbird.jpg', { encoding: 'base64' }) 566 567 twit.post('media/upload', { media_data: b64content }, function (err, data, response) { 568 assert(!err, err) 569 exports.checkMediaUpload(data) 570 assert.equal(data.image.image_type, 'image/jpeg') 571 572 var mediaIdStr = data.media_id_string 573 assert(mediaIdStr) 574 var altText = 'a very small Big Bird' 575 var params = { media_id: mediaIdStr, alt_text: { text: altText } } 576 twit.post('media/metadata/create', params, function (err, data, response) { 577 assert(!err, err) 578 // data is empty on media/metadata/create success; nothing more to assert 579 done(); 580 }) 581 }) 582 }) 583 }) 584 585 it('POST account/update_profile_image', function (done) { 586 var b64content = fs.readFileSync(__dirname + '/img/snoopy-animated.gif', { encoding: 'base64' }) 587 588 twit.post('account/update_profile_image', { image: b64content }, function (err, data, response) { 589 assert(!err, err); 590 exports.checkReply(err, data); 591 exports.checkUser(data); 592 593 done() 594 }) 595 }) 596 597 it('POST friendships/create', function (done) { 598 var params = { screen_name: 'tolga_tezel', follow: false }; 599 twit.post('friendships/create', params, function (err, data, resp) { 600 assert(!err, err); 601 exports.checkReply(err, data); 602 exports.checkUser(data); 603 done(); 604 }); 605 }) 606 607 describe('Favorites', function () { 608 it('POST favorites/create and POST favorites/destroy work', function (done) { 609 twit.post('favorites/create', { id: '583531943624597504' }, function (err, data, resp) { 610 assert(!err, err); 611 exports.checkReply(err, data); 612 var tweetIdStr = data.id_str; 613 assert(tweetIdStr); 614 615 twit.post('favorites/destroy', { id: tweetIdStr }, function (err, data, resp) { 616 assert(!err, err); 617 exports.checkReply(err, data); 618 assert(data.id_str); 619 assert(data.text); 620 621 done(); 622 }) 623 }) 624 }) 625 }) 626 627 describe('error handling', function () { 628 describe('handling errors from the twitter api', function () { 629 var twit = new Twit({ 630 consumer_key: 'a', 631 consumer_secret: 'b', 632 access_token: 'c', 633 access_token_secret: 'd' 634 }) 635 it('should callback with an Error object with all the info and a response object', function (done) { 636 twit.get('account/verify_credentials', function (err, reply, res) { 637 assert(err instanceof Error) 638 assert(err.statusCode === 401) 639 assert(err.code > 0) 640 assert(err.message.match(/token/)) 641 assert(err.twitterReply) 642 assert(err.allErrors) 643 assert(res) 644 assert(res.headers) 645 assert.equal(res.statusCode, 401) 646 done() 647 }) 648 }) 649 it('should return a rejected promise with the Twitter API errors', function() { 650 twit.get('account/verify_credentials') 651 .then(result => { 652 throw Error("Twitter API exception was not caught in the promise API") 653 }) 654 .catch(err => { 655 assert(err instanceof Error) 656 assert(err.statusCode === 401) 657 assert(err.code > 0) 658 assert(err.message.match(/token/)) 659 assert(err.twitterReply) 660 assert(err.allErrors) 661 return; 662 }) 663 }) 664 }) 665 describe('handling other errors', function () { 666 it('should just forward errors raised by underlying request lib', function (done) { 667 var twit = new Twit(config1); 668 var fakeError = new Error('derp') 669 670 var FakeRequest = function () { 671 EventEmitter.call(this) 672 } 673 util.inherits(FakeRequest, EventEmitter) 674 675 var stubGet = function () { 676 var fakeRequest = new FakeRequest() 677 process.nextTick(function () { 678 fakeRequest.emit('error', fakeError) 679 }) 680 return fakeRequest 681 } 682 683 var request = require('request') 684 var stubGet = sinon.stub(request, 'get', stubGet) 685 686 twit.get('account/verify_credentials', function (err, reply, res) { 687 assert(err === fakeError) 688 689 // restore request.get 690 stubGet.restore() 691 692 done() 693 }) 694 }) 695 }) 696 697 describe('Request timeout', function () { 698 it('set to 1ms should return with a timeout error', function (done) { 699 config1.timeout_ms = 1; 700 var twit = new Twit(config1); 701 twit.get('account/verify_credentials', function (err, reply, res) { 702 assert(err) 703 assert.equal(err.message, 'ETIMEDOUT') 704 delete config1.timeout_ms 705 done() 706 }) 707 }) 708 }) 709 }); 710 }); 711 712 describe('Twit agent_options config', function () { 713 it.skip('config.trusted_cert_fingerprints works against cert fingerprint for api.twitter.com:443', function (done) { 714 // TODO: mock getPeerCertificate so we don't have to pin here 715 config1.trusted_cert_fingerprints = [ 716 '50:D9:10:E8:B4:CD:A9:82:E1:FA:6A:43:48:6F:3B:3F:3C:31:A0:8B' 717 ]; 718 var t = new Twit(config1); 719 720 t.get('account/verify_credentials', function (err, data, resp) { 721 assert(!err, err) 722 assert(data) 723 assert(data.id_str) 724 assert(data.name) 725 assert(data.screen_name) 726 727 delete config1.trusted_cert_fingerprints 728 done(); 729 }) 730 }) 731 732 it.skip('config.trusted_cert_fingerprints responds with Error when fingerprint mismatch occurs', function (done) { 733 config1.trusted_cert_fingerprints = [ 734 'AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA' 735 ]; 736 var t = new Twit(config1); 737 738 t.get('account/verify_credentials', function (err, data, resp) { 739 assert(err) 740 assert(err.toString().indexOf('Trusted fingerprints are: ' + config1.trusted_cert_fingerprints[0]) !== -1) 741 742 delete config1.trusted_cert_fingerprints 743 done(); 744 }) 745 }) 746 }) 747 748 describe('Local time offset compensation', function () { 749 it('Compensates for local time being behind', function (done) { 750 var t1 = Date.now(); 751 var t = new Twit(config2); 752 753 var stubNow = function () { 754 return 0; 755 } 756 var stubDateNow = sinon.stub(Date, 'now', stubNow); 757 758 t.get('account/verify_credentials', function (err, data, resp) { 759 assert(err); 760 761 t.get('account/verify_credentials', function (err, data, resp) { 762 assert(!err, err); 763 exports.checkReply(err, data); 764 exports.checkUser(data); 765 assert(t._twitter_time_minus_local_time_ms > 0) 766 767 stubDateNow.restore(); 768 769 done(); 770 }) 771 }) 772 }) 773 }) 774 775 /** 776 * Basic validation to verify we have no error and reply is an object 777 * 778 * @param {error} err error object (or null) 779 * @param {object} reply reply object received from twitter 780 */ 781 var checkReply = exports.checkReply = function (err, reply) { 782 assert.equal(err, null, 'reply err:'+util.inspect(err, true, 10, true)) 783 assert.equal(typeof reply, 'object') 784 } 785 786 /** 787 * check the http response object and its headers 788 * @param {object} response http response object 789 */ 790 var checkResponse = exports.checkResponse = function (response) { 791 assert(response) 792 assert(response.headers) 793 assert.equal(response.statusCode, 200) 794 } 795 796 /** 797 * validate that @tweet is a tweet object 798 * 799 * @param {object} tweet `tweet` object received from twitter 800 */ 801 var checkTweet = exports.checkTweet = function (tweet) { 802 assert.ok(tweet) 803 assert.equal('string', typeof tweet.id_str, 'id_str wasnt string:'+tweet.id_str) 804 assert.equal('string', typeof tweet.text) 805 806 assert.ok(tweet.user) 807 assert.equal('string', typeof tweet.user.id_str) 808 assert.equal('string', typeof tweet.user.screen_name) 809 } 810 811 /** 812 * Validate that @dm is a direct message object 813 * 814 * @param {object} dm `direct message` object received from twitter 815 */ 816 exports.checkDm = function checkDm (dm) { 817 assert.ok(dm) 818 assert.equal('string', typeof dm.id_str) 819 assert.equal('string', typeof dm.text) 820 821 var recipient = dm.recipient 822 823 assert.ok(recipient) 824 assert.equal('string', typeof recipient.id_str) 825 assert.equal('string', typeof recipient.screen_name) 826 827 var sender = dm.sender 828 829 assert.ok(sender) 830 assert.equal('string', typeof sender.id_str) 831 assert.equal('string', typeof sender.screen_name) 832 833 assert.equal('string', typeof dm.text) 834 } 835 836 exports.checkMediaUpload = function checkMediaUpload (data) { 837 assert.ok(data) 838 assert.ok(data.image) 839 assert.ok(data.image.w) 840 assert.ok(data.image.h) 841 assert.ok(data.media_id) 842 assert.equal('string', typeof data.media_id_string) 843 assert.ok(data.size) 844 } 845 846 exports.checkUser = function checkUser (data) { 847 assert.ok(data) 848 assert.ok(data.id_str) 849 assert.ok(data.name) 850 assert.ok(data.screen_name) 851 } 852 853 exports.assertTweetHasText = function (tweet, text) { 854 assert(tweet.text.toLowerCase().indexOf(text) !== -1, 'expected to find '+text+' in text: '+tweet.text); 855 } 856 857 exports.req_with_retries = function (twit_instance, num_tries, verb, path, params, status_codes_to_retry, cb) { 858 twit_instance[verb](path, params, function (err, data, response) { 859 if (!num_tries || (status_codes_to_retry.indexOf(response.statusCode) === -1)) { 860 return cb(err, data, response) 861 } 862 863 exports.req_with_retries(twit_instance, num_tries - 1, verb, path, params, status_codes_to_retry, cb) 864 }) 865 }