diff --git a/README.md b/README.md index e019d04..f1b09e9 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,14 @@ you will need **ONLY** if you decide to contribute to HTTPretty which means you' > [sudo] pip install nose * [sure](http://github.com/gabrielfalcao/sure/) > [sudo] pip install sure +* [httplib2](http://code.google.com/p/httplib2/) + > [sudo] pip install httplib2 +* [bolacha](http://github.com/gabrielfalcao/bolacha/) + > [sudo] pip install bolacha +* [tornado](http://tornadoweb.org/) + > [sudo] pip install tornado +* [multiprocessing](http://tornadoweb.org/) + > [sudo] pip install multiprocessing # Contributing diff --git a/httpretty/__init__.py b/httpretty/__init__.py index 205cf91..4a7b201 100644 --- a/httpretty/__init__.py +++ b/httpretty/__init__.py @@ -30,8 +30,11 @@ from datetime import datetime from StringIO import StringIO from urlparse import urlsplit +old_socket = socket.socket +old_create_connection = socket.create_connection + class fakesock(object): - old_socket = socket.socket + class socket(object): _entry = None @@ -40,8 +43,10 @@ class fakesock(object): self.family = family self.type = type self.protocol = protocol + self.truesock = old_socket(family, type, protocol) def connect(self, address): + self._address = (self._host, self._port) = address self._closed = False @@ -272,7 +277,18 @@ class HTTPretty(object): def Response(cls, body, adding_headers=None, forcing_headers=None, status=200, **headers): return Entry(method=None, uri=None, body=body, adding_headers=adding_headers, forcing_headers=forcing_headers, status=status, **headers) -socket.socket = fakesock.socket -socket.create_connection = create_fake_connection -socket.create = fakesock.socket -socket.__dict__.update(fakesock.__dict__) + @classmethod + def disable(cls): + socket.socket = old_socket + socket.create_connection = old_create_connection + socket.__dict__['socket'] = old_socket + socket.__dict__['create_connection'] = old_create_connection + + @classmethod + def enable(cls): + socket.socket = fakesock.socket + socket.create_connection = create_fake_connection + socket.__dict__['socket'] = fakesock.socket + socket.__dict__['create_connection'] = create_fake_connection + +HTTPretty.enable() diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py index 1e59f4f..284a158 100644 --- a/tests/functional/__init__.py +++ b/tests/functional/__init__.py @@ -24,3 +24,5 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. +import warnings +warnings.simplefilter('ignore') diff --git a/tests/functional/test_bypass.py b/tests/functional/test_bypass.py new file mode 100644 index 0000000..b685abf --- /dev/null +++ b/tests/functional/test_bypass.py @@ -0,0 +1,62 @@ +# #!/usr/bin/env python +# -*- coding: utf-8 -*- + +# +# Copyright (C) <2011> Gabriel Falcão +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +import urllib2 +from testserver import Server +from sure import that, that_with_context +from httpretty import HTTPretty + +def start_server(context): + context.server = Server(9999) + context.server.start() + HTTPretty.enable() + +def stop_server(context): + context.server.stop() + HTTPretty.enable() + +@that_with_context(start_server, stop_server) +def test_httpretty_bypasses_a_unregistered_request(): + u"HTTPretty should bypass a unregistered request by disabling it" + + HTTPretty.register_uri(HTTPretty.GET, "http://localhost:9999/go-for-bubbles/", + body="glub glub") + + fd = urllib2.urlopen('http://localhost:9999/go-for-bubbles/') + got1 = fd.read() + fd.close() + + assert that(got1).equals('glub glub') + + HTTPretty.disable() + + fd = urllib2.urlopen('http://localhost:9999/come-again/') + got2 = fd.read() + fd.close() + + assert that(got2).equals('<- HELLO WORLD ->') + + diff --git a/tests/functional/test_urllib2.py b/tests/functional/test_urllib2.py index 5d222cd..130cfa2 100644 --- a/tests/functional/test_urllib2.py +++ b/tests/functional/test_urllib2.py @@ -25,7 +25,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. import urllib2 -from sure import that, within, microseconds +from sure import that, within, microseconds, that_with_context from httpretty import HTTPretty @within(two=microseconds) @@ -162,5 +162,3 @@ def test_httpretty_should_support_a_list_of_successive_responses(now): request3.close() assert that(request3.code).equals(202) assert that(body3).equals('second and last response') - - diff --git a/tests/functional/testserver.py b/tests/functional/testserver.py new file mode 100644 index 0000000..a3e764d --- /dev/null +++ b/tests/functional/testserver.py @@ -0,0 +1,78 @@ +# #!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# +# Copyright (C) <2011> Gabriel Falcão +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +import os +import sys +from StringIO import StringIO +from tornado.web import Application +from tornado.web import RequestHandler +from tornado.httpserver import HTTPServer +from tornado.ioloop import IOLoop +from multiprocessing import Process + +class BubblesHandler(RequestHandler): + def get(self): + self.write(". o O 0 O o . o O 0 O o . o O 0 O o . o O 0 O o . o O 0 O o .") + +class ComeHandler(RequestHandler): + def get(self): + self.write("<- HELLO WORLD ->") + +class Server(object): + is_running = False + def __init__(self, port): + self.port = int(port) + self.process = None + + @classmethod + def get_handlers(cls): + return Application([ + (r"/go-for-bubbles/?", BubblesHandler), + (r"/come-again/?", ComeHandler), + ]) + + def start(self): + def go(app, port, data={}): + from httpretty import HTTPretty + HTTPretty.disable() + http = HTTPServer(app) + http.listen(int(port)) + IOLoop.instance().start() + + app = self.get_handlers() + + data = {} + args = (app, self.port, data) + self.process = Process(target=go, args=args) + self.process.start() + + def stop(self): + try: + os.kill(self.process.pid, 9) + except OSError: + self.process.terminate() + finally: + self.is_running = False