script/lib/tweetstream/tests/test_tweetstream.py
changeset 12 4daf47fcf792
child 13 79b6e132e3d7
child 15 5d552b6a0e55
equal deleted inserted replaced
11:54d7f1486ac4 12:4daf47fcf792
       
     1 import contextlib
       
     2 import threading
       
     3 import time
       
     4 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
       
     5 
       
     6 from nose.tools import assert_raises
       
     7 from tweetstream import TweetStream, FollowStream, TrackStream
       
     8 from tweetstream import ConnectionError, AuthenticationError
       
     9 
       
    10 from servercontext import test_server
       
    11 
       
    12 single_tweet = r"""{"in_reply_to_status_id":null,"in_reply_to_user_id":null,"favorited":false,"created_at":"Tue Jun 16 10:40:14 +0000 2009","in_reply_to_screen_name":null,"text":"record industry just keeps on amazing me: http:\/\/is.gd\/13lFo - $150k per song you've SHARED, not that somebody has actually DOWNLOADED.","user":{"notifications":null,"profile_background_tile":false,"followers_count":206,"time_zone":"Copenhagen","utc_offset":3600,"friends_count":191,"profile_background_color":"ffffff","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/250715794\/profile_normal.png","description":"Digital product developer, currently at Opera Software. My tweets are my opinions, not those of my employer.","verified_profile":false,"protected":false,"favourites_count":0,"profile_text_color":"3C3940","screen_name":"eiriksnilsen","name":"Eirik Stridsklev N.","following":null,"created_at":"Tue May 06 12:24:12 +0000 2008","profile_background_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_background_images\/10531192\/160x600opera15.gif","profile_link_color":"0099B9","profile_sidebar_fill_color":"95E8EC","url":"http:\/\/www.stridsklev-nilsen.no\/eirik","id":14672543,"statuses_count":506,"profile_sidebar_border_color":"5ED4DC","location":"Oslo, Norway"},"id":2190767504,"truncated":false,"source":"<a href=\"http:\/\/widgets.opera.com\/widget\/7206\">Twitter Opera widget<\/a>"}"""
       
    13 
       
    14 
       
    15 def test_bad_auth():
       
    16     """Test that the proper exception is raised when the user could not be
       
    17     authenticated"""
       
    18     def auth_denied(request):
       
    19         request.send_error(401)
       
    20 
       
    21     with test_server(handler=auth_denied, methods=("post", "get"),
       
    22                      port="random") as server:
       
    23         stream = TweetStream("foo", "bar", url=server.baseurl)
       
    24         assert_raises(AuthenticationError, stream.next)
       
    25 
       
    26         stream = FollowStream("foo", "bar", [1, 2, 3], url=server.baseurl)
       
    27         assert_raises(AuthenticationError, stream.next)
       
    28 
       
    29         stream = TrackStream("foo", "bar", ["opera"], url=server.baseurl)
       
    30         assert_raises(AuthenticationError, stream.next)
       
    31 
       
    32 
       
    33 def test_404_url():
       
    34     """Test that the proper exception is raised when the stream URL can't be
       
    35     found"""
       
    36     def not_found(request):
       
    37         request.send_error(404)
       
    38 
       
    39     with test_server(handler=not_found, methods=("post", "get"),
       
    40                      port="random") as server:
       
    41         stream = TweetStream("foo", "bar", url=server.baseurl)
       
    42         assert_raises(ConnectionError, stream.next)
       
    43 
       
    44         stream = FollowStream("foo", "bar", [1, 2, 3], url=server.baseurl)
       
    45         assert_raises(ConnectionError, stream.next)
       
    46 
       
    47         stream = TrackStream("foo", "bar", ["opera"], url=server.baseurl)
       
    48         assert_raises(ConnectionError, stream.next)
       
    49 
       
    50 
       
    51 def test_bad_content():
       
    52     """Test error handling if we are given invalid data"""
       
    53     def bad_content(request):
       
    54         for n in xrange(10):
       
    55             # what json we pass doesn't matter. It's not verifying the
       
    56             # strcuture, only checking that it's parsable
       
    57             yield "[1,2,3]"
       
    58         yield "[1,2, I need no stinking close brace"
       
    59         yield "[1,2,3]"
       
    60 
       
    61     def do_test(klass, *args):
       
    62         with test_server(handler=bad_content, methods=("post", "get"),
       
    63                          port="random") as server:
       
    64             stream = klass("foo", "bar", *args, url=server.baseurl)
       
    65             for tweet in stream:
       
    66                 pass
       
    67 
       
    68     assert_raises(ConnectionError, do_test, TweetStream)
       
    69     assert_raises(ConnectionError, do_test, FollowStream, [1, 2, 3])
       
    70     assert_raises(ConnectionError, do_test, TrackStream, ["opera"])
       
    71 
       
    72 
       
    73 def test_closed_connection():
       
    74     """Test error handling if server unexpectedly closes connection"""
       
    75     cnt = 1000
       
    76     def bad_content(request):
       
    77         for n in xrange(cnt):
       
    78             # what json we pass doesn't matter. It's not verifying the
       
    79             # strcuture, only checking that it's parsable
       
    80             yield "[1,2,3]"
       
    81 
       
    82     def do_test(klass, *args):
       
    83         with test_server(handler=bad_content, methods=("post", "get"),
       
    84                          port="random") as server:
       
    85             stream = klass("foo", "bar", *args, url=server.baseurl)
       
    86             for tweet in stream:
       
    87                 pass
       
    88 
       
    89     assert_raises(ConnectionError, do_test, TweetStream)
       
    90     assert_raises(ConnectionError, do_test, FollowStream, [1, 2, 3])
       
    91     assert_raises(ConnectionError, do_test, TrackStream, ["opera"])
       
    92 
       
    93 
       
    94 def test_bad_host():
       
    95     """Test behaviour if we can't connect to the host"""
       
    96     stream = TweetStream("foo", "bar", url="http://bad.egewdvsdswefdsf.com/")
       
    97     assert_raises(ConnectionError, stream.next)
       
    98 
       
    99     stream = FollowStream("foo", "bar", [1, 2, 3], url="http://zegwefdsf.com/")
       
   100     assert_raises(ConnectionError, stream.next)
       
   101 
       
   102     stream = TrackStream("foo", "bar", ["foo"], url="http://aswefdsews.com/")
       
   103     assert_raises(ConnectionError, stream.next)
       
   104 
       
   105 
       
   106 def smoke_test_receive_tweets():
       
   107     """Receive 100k tweets and disconnect (slow)"""
       
   108     total = 100000
       
   109 
       
   110     def tweetsource(request):
       
   111         while True:
       
   112             yield single_tweet + "\n"
       
   113 
       
   114     def do_test(klass, *args):
       
   115         with test_server(handler=tweetsource,
       
   116                          methods=("post", "get"), port="random") as server:
       
   117             stream = klass("foo", "bar", *args, url=server.baseurl)
       
   118             for tweet in stream:
       
   119                 if stream.count == total:
       
   120                     break
       
   121 
       
   122     do_test(TweetStream)
       
   123     do_test(FollowStream, [1, 2, 3])
       
   124     do_test(TrackStream, ["foo", "bar"])
       
   125 
       
   126 
       
   127 def test_keepalive():
       
   128     """Make sure we behave sanely when there are keepalive newlines in the
       
   129     data recevived from twitter"""
       
   130     def tweetsource(request):
       
   131         yield single_tweet+"\n"
       
   132         yield "\n"
       
   133         yield "\n"
       
   134         yield single_tweet+"\n"
       
   135         yield "\n"
       
   136         yield "\n"
       
   137         yield "\n"
       
   138         yield "\n"
       
   139         yield "\n"
       
   140         yield "\n"
       
   141         yield "\n"
       
   142         yield single_tweet+"\n"
       
   143         yield "\n"
       
   144 
       
   145     def do_test(klass, *args):
       
   146         with test_server(handler=tweetsource, methods=("post", "get"),
       
   147                          port="random") as server:
       
   148             stream = klass("foo", "bar", *args, url=server.baseurl)
       
   149             try:
       
   150                 for tweet in stream:
       
   151                     pass
       
   152             except ConnectionError:
       
   153                 assert stream.count == 3, "Got %s, wanted 3" % stream.count
       
   154             else:
       
   155                 assert False, "Didn't handle keepalive"
       
   156 
       
   157 
       
   158     do_test(TweetStream)
       
   159     do_test(FollowStream, [1, 2, 3])
       
   160     do_test(TrackStream, ["foo", "bar"])
       
   161 
       
   162 
       
   163 def test_buffering():
       
   164     """Test if buffering stops data from being returned immediately.
       
   165     If there is some buffering in play that might mean data is only returned
       
   166     from the generator when the buffer is full. If buffer is bigger than a
       
   167     tweet, this will happen. Default buffer size in the part of socket lib
       
   168     that enables readline is 8k. Max tweet length is around 3k."""
       
   169 
       
   170     def tweetsource(request):
       
   171         yield single_tweet+"\n"
       
   172         time.sleep(2)
       
   173         # need to yield a bunch here so we're sure we'll return from the
       
   174         # blocking call in case the buffering bug is present.
       
   175         for n in xrange(100):
       
   176             yield single_tweet+"\n"
       
   177 
       
   178     def do_test(klass, *args):
       
   179         with test_server(handler=tweetsource, methods=("post", "get"),
       
   180                          port="random") as server:
       
   181             stream = klass("foo", "bar", *args, url=server.baseurl)
       
   182 
       
   183             start = time.time()
       
   184             stream.next()
       
   185             first = time.time()
       
   186             diff = first - start
       
   187             assert diff < 1, "Getting first tweet took more than a second!"
       
   188 
       
   189     do_test(TweetStream)
       
   190     do_test(FollowStream, [1, 2, 3])
       
   191     do_test(TrackStream, ["foo", "bar"])
       
   192 
       
   193