twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

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 }