pytest-localserver-0.3.4/0000755000000000000000000000000012552474553014077 5ustar rootrootpytest-localserver-0.3.4/pytest_localserver.egg-info/0000755000000000000000000000000012552474553021522 5ustar rootrootpytest-localserver-0.3.4/pytest_localserver.egg-info/SOURCES.txt0000666000000000000000000000114412463453400023377 0ustar rootrootLICENSE MANIFEST.in README setup.py tox.ini pytest_localserver/__init__.py pytest_localserver/ca.crt pytest_localserver/http.py pytest_localserver/https.py pytest_localserver/plugin.py pytest_localserver/server.pem pytest_localserver/smtp.py pytest_localserver.egg-info/PKG-INFO pytest_localserver.egg-info/SOURCES.txt pytest_localserver.egg-info/dependency_links.txt pytest_localserver.egg-info/entry_points.txt pytest_localserver.egg-info/not-zip-safe pytest_localserver.egg-info/requires.txt pytest_localserver.egg-info/top_level.txt tests/__init__.py tests/test_http.py tests/test_https.py tests/test_smtp.pypytest-localserver-0.3.4/pytest_localserver.egg-info/top_level.txt0000666000000000000000000000002312463453400024240 0ustar rootrootpytest_localserver pytest-localserver-0.3.4/pytest_localserver.egg-info/not-zip-safe0000666000000000000000000000000112463433412023742 0ustar rootroot pytest-localserver-0.3.4/pytest_localserver.egg-info/requires.txt0000666000000000000000000000001612463453400024110 0ustar rootrootwerkzeug>=0.10pytest-localserver-0.3.4/pytest_localserver.egg-info/dependency_links.txt0000666000000000000000000000000112463453400025561 0ustar rootroot pytest-localserver-0.3.4/pytest_localserver.egg-info/PKG-INFO0000666000000000000000000002327612463453400022622 0ustar rootrootMetadata-Version: 1.1 Name: pytest-localserver Version: 0.3.4 Summary: py.test plugin to test server connections locally. Home-page: http://bitbucket.org/basti/pytest-localserver/ Author: Sebastian Rahlf Author-email: basti AT redtoad DOT de License: MIT License Download-URL: http://bitbucket.org/basti/pytest-localserver/downloads/ Description: ================== pytest-localserver ================== pytest-localserver is a plugin for the `pytest`_ testing framework which enables you to test server connections locally. Sometimes `monkeypatching`_ ``urllib2.urlopen()`` just does not cut it, for instance if you work with ``urllib2.Request``, define your own openers/handlers or work with ``httplib``. In these cases it may come in handy to have an HTTP server running locally which behaves just like the real thing [1]_. Well, look no further! Quickstart ========== Let's say you have a function to scrape HTML which only required to be pointed at a URL :: import requests def scrape(url): html = requests.get(url).text # some parsing happens here # ... return result You want to test this function in its entirety without having to rely on a remote server whose content you cannot control, neither do you want to waste time setting up a complex mechanism to mock or patch the underlying Python modules dealing with the actual HTTP request (of which there are more than one BTW). So what do you do? You simply use pytest's `funcargs feature`_ and simulate an entire server locally! :: def test_retrieve_some_content(httpserver): httpserver.serve_content(open('cached-content.html').read()) assert scrape(httpserver.url) == 'Found it!' What happened here is that for the duration of your tests an HTTP server is started on a random port on localhost which will serve the content you tell it to and behaves just like the real thing. The added bonus is that you can test whether your code behaves gracefully if there is a network problem:: def test_content_retrieval_fails_graciously(httpserver): httpserver.serve_content('File not found!', 404) pytest.raises(ContentNotFoundException, scrape, httpserver.url) The same thing works for SMTP servers, too:: def test_sending_some_message(smtpserver): mailer = MyMailer(host=smtpserver.addr[0], port=smtpserver.addr[1]) mailer.send(to='bob@example.com', from_='alice@example.com', subject='MyMailer v1.0', body='Check out my mailer!') assert len(smtpserver.outbox)==1 Here an SMTP server is started which accepts e-mails being sent to it. The nice feature here is that you can actually check if the message was received and what was sent by looking into the smtpserver's ``outbox``. It is really that easy! Available funcargs ================== Here is a short overview of the available funcargs. For more details I suggest poking around in the code itself. ``httpserver`` provides a threaded HTTP server instance running on localhost. It has the following attributes: * ``code`` - HTTP response code (int) * ``content`` - content of next response (str) * ``headers`` - response headers (dict) Once these attribute are set, all subsequent requests will be answered with these values until they are changed or the server is stopped. A more convenient way to change these is :: httpserver.serve_content(content=None, code=200, headers=None) The server address can be found in property * ``url`` which is the string representation of tuple ``server_address`` (host as str, port as int). If you want to check which form fields have been POSTed, Try :: httpserver.serve_content(..., show_post_vars=True) which will display them as parsable text. ``httpsserver`` is the same as ``httpserver`` only with SSL encryption. ``smtpserver`` provides a threaded instance of ``smtpd.SMTPServer`` runnning on localhost. It has the following attributes: * ``addr`` - server address as tuple (host as str, port as int) * ``outbox`` - list of ``email.message.Message`` instances received. Using your a WSGI application as test server ============================================ As of version 0.3 you can now use a `WSGI application`_ to run on the test server :: from pytest_localserver.http import WSGIServer def simple_app(environ, start_response): """Simplest possible WSGI application""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['Hello world!\n'] def pytest_funcarg__testserver(request): """Defines the testserver funcarg""" server = WSGIServer(application=simple_app) server.start() request.addfinalizer(server.stop) return server def test_retrieve_some_content(testserver): assert scrape(testserver.url) == 'Hello world!\n' Have a look at the following page for more information on WSGI: http://wsgi.readthedocs.org/en/latest/learn.html Download and Installation ========================= You can install the plugin by running :: pip install pytest-localserver Alternatively, get the latest stable version from `PyPI`_ or the latest `bleeding-edge archive`_ from bitbucket.org. License and Credits =================== This plugin is released under the MIT license. You can find the full text of the license in the LICENSE file. Copyright (C) 2010-2013 Sebastian Rahlf and others (see AUTHORS). Some parts of this package is based on ideas or code from other people: - I borrowed some implementation ideas for the httpserver from `linkchecker`_. - The implementation for the SMTP server is based on the `Mailsink recipe`_ by Adam Feuer, Matt Branthwaite and Troy Frever. - The HTTPS implementation is based on work by `Sebastien Martini`_. Thanks guys! Development and future plans ============================ Feel free to clone the repository and add your own changes. Pull requests are always welcome!:: hg clone https://bitbucket.org/basti/pytest-localserver If you find any bugs, please file a `report`_. Test can be run with tox. Note that you need virtualenv<1.8 to run tests for Python 2.4. I already have a couple of ideas for future versions: * support for FTP, SSH (maybe base all on twisted?) * making the SMTP outbox as convenient to use as ``django.core.mail.outbox`` * add your own here! ---- .. [1] The idea for this project was born when I needed to check that `a piece of software`_ behaved itself when receiving HTTP error codes 404 and 500. Having unsuccessfully tried to mock a server, I stumbled across `linkchecker`_ which uses a the same idea to test its internals. .. _monkeypatching: http://pytest.org/latest/monkeypatch.html .. _pytest: http://pytest.org/ .. _funcargs feature: http://pytest.org/latest/funcargs.html .. _linkchecker: http://linkchecker.sourceforge.net/ .. _WSGI application: http://www.python.org/dev/peps/pep-0333/ .. _PyPI: http://pypi.python.org/pypi/pytest-localserver/ .. _bleeding-edge archive: https://bitbucket.org/basti/pytest-localserver/get/tip.tar.gz .. _report: https://bitbucket.org/basti/pytest-localserver/issues/ .. _tox: http://testrun.org/tox/ .. _a piece of software: http://pypi.python.org/pypi/python-amazon-product-api/ .. _Mailsink recipe: http://code.activestate.com/recipes/440690/ .. _Sebastien Martini: http://code.activestate.com/recipes/442473/ Keywords: py.test pytest server localhost http smtp Platform: UNKNOWN Classifier: Operating System :: OS Independent Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Software Development :: Testing pytest-localserver-0.3.4/pytest_localserver.egg-info/entry_points.txt0000666000000000000000000000006412463453400025011 0ustar rootroot[pytest11] localserver = pytest_localserver.plugin pytest-localserver-0.3.4/tests/0000755000000000000000000000000012552474553015241 5ustar rootrootpytest-localserver-0.3.4/tests/__init__.py0000666000000000000000000000000012312374770017336 0ustar rootrootpytest-localserver-0.3.4/tests/test_https.py0000666000000000000000000000265712463451440020021 0ustar rootrootimport sys import pytest import requests from pytest_localserver import https, plugin def pytest_funcarg__httpsserver(request): # define funcargs here again in order to run tests without having to # install the plugin anew every single time return plugin.pytest_funcarg__httpsserver(request) def test_httpsserver_funcarg(httpsserver): assert isinstance(httpsserver, https.SecureContentServer) assert httpsserver.is_alive() assert httpsserver.server_address def test_server_does_not_serve_file_at_startup(httpsserver): assert httpsserver.code == 204 assert httpsserver.content == '' def test_some_content_retrieval(httpsserver): httpsserver.serve_content('TEST!') resp = requests.get(httpsserver.url, verify=False) assert resp.text == 'TEST!' assert resp.status_code == 200 def test_GET_request(httpsserver): httpsserver.serve_content('TEST!', headers={'Content-type': 'text/plain'}) resp = requests.get(httpsserver.url, headers={'User-Agent': 'Test method'}, verify=False) assert resp.text == 'TEST!' assert resp.status_code == 200 assert 'text/plain' in resp.headers['Content-type'] def test_HEAD_request(httpsserver): httpsserver.serve_content('TEST!', headers={'Content-type': 'text/plain'}) print(httpsserver.url) resp = requests.head(httpsserver.url, verify=False) assert resp.status_code == 200 assert resp.headers['Content-type'] == 'text/plain' pytest-localserver-0.3.4/tests/test_smtp.py0000666000000000000000000000444612312374770017643 0ustar rootrootimport smtplib try: # python 3 from email.mime.text import MIMEText except ImportError: # python 2? from email.MIMEText import MIMEText from pytest_localserver import plugin, smtp def send_plain_email(to, from_, subject, txt, server=('localhost', 25)): """ Sends a simple plain text message via SMTP. """ if type(to) in (tuple, list): to = ', '.join(to) # Create a text/plain message msg = MIMEText(txt) msg['Subject'] = subject msg['From'] = from_ msg['To'] = to host, port = server server = smtplib.SMTP(host, port) server.set_debuglevel(1) server.sendmail(from_, to, msg.as_string()) server.quit() def pytest_funcarg__smtpserver(request): # define funcargs here again in order to run tests without having to # install the plugin anew every single time return plugin.pytest_funcarg__smtpserver(request) def test_smtpserver_funcarg(smtpserver): assert isinstance(smtpserver, smtp.Server) assert smtpserver.is_alive() assert smtpserver.accepting and smtpserver.addr def test_server_is_killed(smtpserver): assert smtpserver.is_alive() smtpserver.stop() assert not smtpserver.is_alive() def test_server_is_deleted(smtpserver): assert smtpserver.is_alive() smtpserver.__del__() # need to call magic method here! assert not smtpserver.is_alive() def test_smtpserver_has_empty_outbox_at_startup(smtpserver): assert len(smtpserver.outbox) == 0 def test_send_email(smtpserver): # send one e-mail send_plain_email( 'alice@example.com', 'webmaster@example.com', 'Your e-mail is getting there', 'Seems like this test actually works!', smtpserver.addr) msg = smtpserver.outbox[-1] assert msg['To'] == 'alice@example.com' assert msg['From'] == 'webmaster@example.com' assert msg['Subject'] == 'Your e-mail is getting there' # send another e-mail send_plain_email( 'bob@example.com', 'webmaster@example.com', 'His e-mail too', 'Seems like this test actually works!', smtpserver.addr) msg = smtpserver.outbox[-1] assert msg['To'] == 'bob@example.com' assert msg['From'] == 'webmaster@example.com' assert msg['Subject'] == 'His e-mail too' # two mails are now in outbox assert len(smtpserver.outbox) == 2 pytest-localserver-0.3.4/tests/test_http.py0000666000000000000000000000502412463451300017620 0ustar rootrootimport gzip import requests import six from pytest_localserver import http, plugin from pytest_localserver import VERSION def pytest_funcarg__httpserver(request): # define funcargs here again in order to run tests without having to # install the plugin anew every single time return plugin.pytest_funcarg__httpserver(request) def test_httpserver_funcarg(httpserver): assert isinstance(httpserver, http.ContentServer) assert httpserver.is_alive() assert httpserver.server_address def test_server_does_not_serve_file_at_startup(httpserver): assert httpserver.code == 204 assert httpserver.content == '' def test_some_content_retrieval(httpserver): httpserver.serve_content('TEST!') resp = requests.get(httpserver.url) assert resp.text == 'TEST!' assert resp.status_code == 200 def test_GET_request(httpserver): httpserver.serve_content('TEST!', headers={'Content-type': 'text/plain'}) resp = requests.get(httpserver.url, headers={'User-Agent': 'Test method'}) assert resp.text == 'TEST!' assert resp.status_code == 200 assert 'text/plain' in resp.headers['Content-type'] # FIXME get compression working! # def test_gzipped_GET_request(httpserver): # httpserver.serve_content('TEST!', headers={'Content-type': 'text/plain'}) # httpserver.compress = 'gzip' # resp = requests.get(httpserver.url, headers={ # 'User-Agent': 'Test method', # 'Accept-encoding': 'gzip' # }) # assert resp.text == 'TEST!' # assert resp.status_code == 200 # assert resp.content_encoding == 'gzip' # assert resp.headers['Content-type'] == 'text/plain' # assert resp.headers['content-encoding'] == 'gzip' def test_HEAD_request(httpserver): httpserver.serve_content('TEST!', headers={'Content-type': 'text/plain'}) resp = requests.head(httpserver.url) assert resp.status_code == 200 assert resp.headers['Content-type'] == 'text/plain' # def test_POST_request(httpserver): # headers = {'Content-type': 'application/x-www-form-urlencoded', # 'set-cookie': 'some _cookie_content'} # # httpserver.serve_content('TEST!', headers=headers) # resp = requests.post(httpserver.url, data={'data': 'value'}, headers=headers) # assert resp.text == 'TEST!' # assert resp.status_code == 200 # # httpserver.serve_content('TEST!', headers=headers, show_post_vars=True) # resp = requests.post(httpserver.url, data={'data': 'value'}, headers=headers) # assert resp.json() == {'data': 'value'} # assert resp.status_code == 200 pytest-localserver-0.3.4/README0000666000000000000000000001603212463451462014760 0ustar rootroot================== pytest-localserver ================== pytest-localserver is a plugin for the `pytest`_ testing framework which enables you to test server connections locally. Sometimes `monkeypatching`_ ``urllib2.urlopen()`` just does not cut it, for instance if you work with ``urllib2.Request``, define your own openers/handlers or work with ``httplib``. In these cases it may come in handy to have an HTTP server running locally which behaves just like the real thing [1]_. Well, look no further! Quickstart ========== Let's say you have a function to scrape HTML which only required to be pointed at a URL :: import requests def scrape(url): html = requests.get(url).text # some parsing happens here # ... return result You want to test this function in its entirety without having to rely on a remote server whose content you cannot control, neither do you want to waste time setting up a complex mechanism to mock or patch the underlying Python modules dealing with the actual HTTP request (of which there are more than one BTW). So what do you do? You simply use pytest's `funcargs feature`_ and simulate an entire server locally! :: def test_retrieve_some_content(httpserver): httpserver.serve_content(open('cached-content.html').read()) assert scrape(httpserver.url) == 'Found it!' What happened here is that for the duration of your tests an HTTP server is started on a random port on localhost which will serve the content you tell it to and behaves just like the real thing. The added bonus is that you can test whether your code behaves gracefully if there is a network problem:: def test_content_retrieval_fails_graciously(httpserver): httpserver.serve_content('File not found!', 404) pytest.raises(ContentNotFoundException, scrape, httpserver.url) The same thing works for SMTP servers, too:: def test_sending_some_message(smtpserver): mailer = MyMailer(host=smtpserver.addr[0], port=smtpserver.addr[1]) mailer.send(to='bob@example.com', from_='alice@example.com', subject='MyMailer v1.0', body='Check out my mailer!') assert len(smtpserver.outbox)==1 Here an SMTP server is started which accepts e-mails being sent to it. The nice feature here is that you can actually check if the message was received and what was sent by looking into the smtpserver's ``outbox``. It is really that easy! Available funcargs ================== Here is a short overview of the available funcargs. For more details I suggest poking around in the code itself. ``httpserver`` provides a threaded HTTP server instance running on localhost. It has the following attributes: * ``code`` - HTTP response code (int) * ``content`` - content of next response (str) * ``headers`` - response headers (dict) Once these attribute are set, all subsequent requests will be answered with these values until they are changed or the server is stopped. A more convenient way to change these is :: httpserver.serve_content(content=None, code=200, headers=None) The server address can be found in property * ``url`` which is the string representation of tuple ``server_address`` (host as str, port as int). If you want to check which form fields have been POSTed, Try :: httpserver.serve_content(..., show_post_vars=True) which will display them as parsable text. ``httpsserver`` is the same as ``httpserver`` only with SSL encryption. ``smtpserver`` provides a threaded instance of ``smtpd.SMTPServer`` runnning on localhost. It has the following attributes: * ``addr`` - server address as tuple (host as str, port as int) * ``outbox`` - list of ``email.message.Message`` instances received. Using your a WSGI application as test server ============================================ As of version 0.3 you can now use a `WSGI application`_ to run on the test server :: from pytest_localserver.http import WSGIServer def simple_app(environ, start_response): """Simplest possible WSGI application""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['Hello world!\n'] def pytest_funcarg__testserver(request): """Defines the testserver funcarg""" server = WSGIServer(application=simple_app) server.start() request.addfinalizer(server.stop) return server def test_retrieve_some_content(testserver): assert scrape(testserver.url) == 'Hello world!\n' Have a look at the following page for more information on WSGI: http://wsgi.readthedocs.org/en/latest/learn.html Download and Installation ========================= You can install the plugin by running :: pip install pytest-localserver Alternatively, get the latest stable version from `PyPI`_ or the latest `bleeding-edge archive`_ from bitbucket.org. License and Credits =================== This plugin is released under the MIT license. You can find the full text of the license in the LICENSE file. Copyright (C) 2010-2013 Sebastian Rahlf and others (see AUTHORS). Some parts of this package is based on ideas or code from other people: - I borrowed some implementation ideas for the httpserver from `linkchecker`_. - The implementation for the SMTP server is based on the `Mailsink recipe`_ by Adam Feuer, Matt Branthwaite and Troy Frever. - The HTTPS implementation is based on work by `Sebastien Martini`_. Thanks guys! Development and future plans ============================ Feel free to clone the repository and add your own changes. Pull requests are always welcome!:: hg clone https://bitbucket.org/basti/pytest-localserver If you find any bugs, please file a `report`_. Test can be run with tox. Note that you need virtualenv<1.8 to run tests for Python 2.4. I already have a couple of ideas for future versions: * support for FTP, SSH (maybe base all on twisted?) * making the SMTP outbox as convenient to use as ``django.core.mail.outbox`` * add your own here! ---- .. [1] The idea for this project was born when I needed to check that `a piece of software`_ behaved itself when receiving HTTP error codes 404 and 500. Having unsuccessfully tried to mock a server, I stumbled across `linkchecker`_ which uses a the same idea to test its internals. .. _monkeypatching: http://pytest.org/latest/monkeypatch.html .. _pytest: http://pytest.org/ .. _funcargs feature: http://pytest.org/latest/funcargs.html .. _linkchecker: http://linkchecker.sourceforge.net/ .. _WSGI application: http://www.python.org/dev/peps/pep-0333/ .. _PyPI: http://pypi.python.org/pypi/pytest-localserver/ .. _bleeding-edge archive: https://bitbucket.org/basti/pytest-localserver/get/tip.tar.gz .. _report: https://bitbucket.org/basti/pytest-localserver/issues/ .. _tox: http://testrun.org/tox/ .. _a piece of software: http://pypi.python.org/pypi/python-amazon-product-api/ .. _Mailsink recipe: http://code.activestate.com/recipes/440690/ .. _Sebastien Martini: http://code.activestate.com/recipes/442473/ pytest-localserver-0.3.4/LICENSE0000666000000000000000000000204412312374770015102 0ustar rootrootCopyright (c) 2011, Sebastian Rahlf 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. pytest-localserver-0.3.4/pytest_localserver/0000755000000000000000000000000012552474553020030 5ustar rootrootpytest-localserver-0.3.4/pytest_localserver/__init__.py0000666000000000000000000000002212463452562022134 0ustar rootrootVERSION = '0.3.4' pytest-localserver-0.3.4/pytest_localserver/plugin.py0000666000000000000000000000465312312374770021706 0ustar rootroot#!/usr/bin/env python # -*- coding: utf8 -*- # # Copyright (C) 2011 Sebastian Rahlf # # This program is release under the MIT license. You can find the full text of # the license in the LICENSE file. def pytest_funcarg__httpserver(request): """The returned ``httpserver`` provides a threaded HTTP server instance running on a randomly assigned port on localhost. It can be taught which content (i.e. string) to serve with which response code and comes with following attributes: * ``code`` - HTTP response code (int) * ``content`` - content of next response (str) * ``headers`` - response headers (dict) Once these attribute are set, all subsequent requests will be answered with these values until they are changed or the server is stopped. A more convenient way to change these is :: httpserver.serve_content( content='My content', code=200, headers={'content-type': 'text/plain'}) The server address can be found in property * ``url`` which is the string representation of tuple ``server_address`` (host as str, port as int). Example:: import requests def scrape(url): html = requests.get(url).text # some parsing happens here # ... return result def test_retrieve_some_content(httpserver): httpserver.serve_content(open('cached-content.html').read()) assert scrape(httpserver.url) == 'Found it!' """ from pytest_localserver import http server = http.ContentServer() server.start() request.addfinalizer(server.stop) return server def pytest_funcarg__httpsserver(request): """The returned ``httpsserver`` (note the additional S!) provides a threaded HTTP server instance similar to funcarg ``httpserver`` but with SSL encryption. """ from pytest_localserver import https server = https.SecureContentServer() server.start() request.addfinalizer(server.stop) return server def pytest_funcarg__smtpserver(request): """The returned ``smtpserver`` provides a threaded instance of ``smtpd.SMTPServer`` running on localhost. It has the following attributes: * ``addr`` - server address as tuple (host as str, port as int) """ from pytest_localserver import smtp server = smtp.Server() server.start() request.addfinalizer(server.stop) return server pytest-localserver-0.3.4/pytest_localserver/https.py0000666000000000000000000001046412463451364021551 0ustar rootroot# Copyright (C) 2010-2013 Sebastian Rahlf and others (see AUTHORS). # # This program is release under the MIT license. You can find the full text of # the license in the LICENSE file. import os.path from pytest_localserver.http import ContentServer #: default server certificate DEFAULT_CERTIFICATE = os.path.join( os.path.abspath(os.path.dirname(__file__)), 'server.pem') class SecureContentServer (ContentServer): """ Small test server which works just like :class:`http.Server` over HTTP:: server = SecureContentServer( port=8080, key='/srv/my.key', cert='my.certificate') server.start() print 'Test server running at %s' % server.url server.serve_content(open('/path/to/some.file').read()) # any call to https://localhost:8080 will get the contents of # /path/to/some.file as a response. To avoid *ssl handshake failures* you can import the `pytest-localserver CA`_ into your browser of choice. How to create a self-signed certificate --------------------------------------- If you want to create your own server certificate, you need `OpenSSL`_ installed on your machine. A self-signed certificate consists of a certificate and a private key for your server. It can be created with the following command:: openssl req -new -x509 -keyout server.pem -out server.pem -nodes Note that both key and certificate are in a single file now named ``server.pem``. How to create your own Certificate Authority -------------------------------------------- Generate a server key and request for signing (csr). Make sure that the common name (CN) is your IP address/domain name (e.g. ``localhost``). :: openssl genrsa -des3 -out server.key 4096 openssl req -new -key server.key -out server.csr Generate your own CA. Make sure that this time the CN is *not* your IP address/domain name (e.g. ``localhost CA``). :: openssl genrsa -des3 -out ca.key 4096 openssl req -new -x509 -key ca.key -out ca.crt Sign the certificate signing request (csr) with the self-created CA that you made earlier. If you issue subsequent certificates and your browser already knows about previous ones simply increment the serial number. :: openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -set_serial 01 -out server.crt Make a server.key which doesn't cause HTTPSServer to prompt for a password:: openssl rsa -in server.key -out server.key.insecure mv server.key server.key.secure mv server.key.insecure server.key Create a single file for both key and certificate:: cat server.key server.crt > server.pem Now you only need to import ``ca.crt`` as a CA in your browser. Want to know more? ------------------ This information was compiled from the following sources, which you might find helpful if you want to dig deeper into `pyOpenSSH`_, certificates and CAs: - http://code.activestate.com/recipes/442473/ - http://www.tc.umn.edu/~brams006/selfsign.html - A more advanced tutorial can be found `here`_. .. _pytest-localserver CA: https://bitbucket.org/basti/pytest-localserver/src/tip/pytest_localserver/ca.crt .. _pyOpenSSH: https://launchpad.net/pyopenssl """ def __init__(self, host='localhost', port=0, key=DEFAULT_CERTIFICATE, cert=DEFAULT_CERTIFICATE): """ :param key: location of file containing the server private key. :param cert: location of file containing server certificate. """ super(SecureContentServer, self).__init__(host, port, ssl_context=(key, cert)) if __name__ == '__main__': # pragma: no cover import sys import time print('Using certificate %s.' % DEFAULT_CERTIFICATE) server = SecureContentServer() server.start() server.logging = True print('HTTPS server is running at %s' % server.url) print('Type to stop') try: path = sys.argv[1] except IndexError: path = os.path.join( os.path.dirname(os.path.abspath(__file__)), '..', 'README') server.serve_content(open(path).read(), 302) try: while True: time.sleep(1) except KeyboardInterrupt: print('\rstopping...') server.stop() pytest-localserver-0.3.4/pytest_localserver/smtp.py0000666000000000000000000000505312312374770021366 0ustar rootroot#!/usr/bin/env python # -*- coding: utf8 -*- # # Copyright (C) 2011 Sebastian Rahlf # with some ideas from http://code.activestate.com/recipes/440690/ # SmtpMailsink Copyright 2005 Aviarc Corporation # Written by Adam Feuer, Matt Branthwaite, and Troy Frever # which is Licensed under the PSF License import asyncore import email import smtpd import threading class Server (smtpd.SMTPServer, threading.Thread): """ Small SMTP test server. Try the following snippet for sending mail:: server = Server(port=8080) server.start() print 'SMTP server is running on %s:%i' % server.addr # any e-mail sent to localhost:8080 will end up in server.outbox # ... server.stop() """ WAIT_BETWEEN_CHECKS = 0.001 def __init__(self, host='localhost', port=0): smtpd.SMTPServer.__init__(self, (host, port), None) if self._localaddr[1] == 0: self.addr = self.getsockname() self.outbox = [] # initialise thread self._stopevent = threading.Event() self.threadName = self.__class__.__name__ threading.Thread.__init__(self, name=self.threadName) def process_message(self, peer, mailfrom, rcpttos, data): """ Adds message to outbox. """ self.outbox += [email.message_from_string(data)] def run(self): """ Threads run method. """ while not self._stopevent.is_set(): asyncore.loop(timeout=self.WAIT_BETWEEN_CHECKS, count=1) def stop(self, timeout=None): """ Stops test server. :param timeout: When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). """ self._stopevent.set() threading.Thread.join(self, timeout) self.close() def __del__(self): self.stop() def __repr__(self): # pragma: no cover return '' % self.addr if __name__ == "__main__": # pragma: no cover import time server = Server() server.start() print('SMTP server is running on %s:%i' % server.addr) print('Type to stop') try: try: while True: time.sleep(1) finally: print('\rstopping...') server.stop() except KeyboardInterrupt: # support for Python 2.4 dictates that try ... finally is not used # together with any except statements pass pytest-localserver-0.3.4/pytest_localserver/server.pem0000666000000000000000000001177112312374770022046 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- 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 -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIFODCCAyACAQEwDQYJKoZIhvcNAQEFBQAwezEbMBkGA1UEChMScHl0ZXN0LWxv Y2Fsc2VydmVyMSQwIgYDVQQLExtDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgRGVwdC4x FTATBgNVBAMTDGxvY2FsaG9zdCBDQTEfMB0GCSqGSIb3DQEJARYQYmFzdGlAcmVk dG9hZC5kZTAeFw0xMTA1MDcxNjEzMzFaFw0xMTA2MDYxNjEzMzFaMEkxGzAZBgNV BAoTEnB5dGVzdC1sb2NhbHNlcnZlcjEWMBQGA1UECxMNVGVzdGluZyBEZXB0LjES MBAGA1UEAxMJMTI3LjAuMC4xMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC AgEA0phLUETPqoTk32D857Cg1HHtpp07IR34uz5/Vnw+5HNG+51uLQCrRktVySyf DGhJTVTv+Ysx3+g4Fq0uU/sYvaZSoZSziH7LetoWpYuU34QAMFTg8Pk7Tli1axUJ z0G5pO6ClUf9i25pFS8/lbk335d4MGc6w//+pthpuhzan8PaufP1DOVQPIcvrsDj ZI1/yb0zBKTYKPaRGoTrM2lgX3BKVApSAq1fL8NYzBx9vR3Fwo/5sRPRWl9BpQNx v6zDYV2CtrfK9D1opxZulTi45r0aPcVqY1tY79ZdXoO+0dVVJSnEWH3RmCDKS229 Sg6wTZp+qIHw8WTkAXxX7Wg7wHp5l7Ml7gnoM00pzEvk1ZwPnzyXai0R3ADhhmJx 3nDUNx+3QmQ+miFD1brn1W+oUf//Wylx2zP5numWVrXuwvPoHUezdJwHXpIAYxBy G8RhER/OB7J45lFgwQPEYpkATP3LMZNc19K6jqp70HTgW+3ICTWCIX+5zyOGDLPJ nxmmGM5pNuy/s5k4Ojf7+k39fxGwUEZvQ2otlj7lKddBYwjcyQegQ9d25ubiHPGa FlAJG1PdUPumulsPto3Kf74d88SXDIjXq9DemVETlUk/hCB0ohNxr3tKFNNS0rOd 5VPzqPowbIszJmnPQ7FWNFm8BVNE7bIMq0e3KFhfqB/SgCsCAwEAATANBgkqhkiG 9w0BAQUFAAOCAgEAp3xwZXaNh0tfik23CJ41joRp4Nr8Ud5qaGTZlwseHrxl6Fhr eDI2BN4MrFC+IHcF7D98F/sYowWSO0zibp8YZL4IlN5sEFe8XV5jBuyptijinyHX ZZ7gMGpAb1F/9OdiLFaQrCsxAg4GyMVWUX1cpptI+jd3cSUZOQ/kHXZ3ls2NmFYk dTWPYt/6zTLvDEL2yCdG8qGCnTGmMnykt00F85/vEnWrz5Gpcwg1vJMDlXfNrice 6IRLE5LI3UZX+8gkTOnz4SSL0Ih97nfXXqDzWXhSvGSeCQ5zW4LCbYF1CZG6wjUE tgktNELrDECRtimxozXX9/7hNWQWiCPdddBEBOgfXer0SW2B26BHVsh2PSYeReSj 6OOXao15BLjLORScn0k5mqpoqUDULpg/MOLPQjq3ZZJMcbzhOBRJ4YhQ8bZ73Amz u+MugAve3CqHXFXs5ygCXSIrgcPdYWIKUe/QJOkFyZHA0W3A6fmvpNh8Brm38L8M N0//cSZHlJHuFmykDXBqNQPDdUPYMiT3dkVRm7Az+enMpnRs4yfeSSlCyOPJqh7b Yrxe6C9CQF5DFatrpF5/bvvxqV/jiwdXr415ikdwHf+LmrORHw3y5dr42++5Cpmm wn1T6rOBZEfA8szd+ccp1B3J1VYeEOL3faOTeBni6uDpRiLlxOeed+gDy80= -----END CERTIFICATE----- pytest-localserver-0.3.4/pytest_localserver/ca.crt0000666000000000000000000000433012312374770021123 0ustar rootroot-----BEGIN CERTIFICATE----- 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 -----END CERTIFICATE----- pytest-localserver-0.3.4/pytest_localserver/http.py0000666000000000000000000000727712463451076021376 0ustar rootroot# Copyright (C) 2010-2013 Sebastian Rahlf and others (see AUTHORS). # # This program is release under the MIT license. You can find the full text of # the license in the LICENSE file. import json import sys import threading from werkzeug.serving import make_server from werkzeug.wrappers import Response, Request class WSGIServer(threading.Thread): """ HTTP server running a WSGI application in its own thread. """ def __init__(self, host='127.0.0.1', port=0, application=None, **kwargs): self.app = application self._server = make_server(host, port, self.app, **kwargs) self.server_address = self._server.server_address super(WSGIServer, self).__init__( name=self.__class__, target=self._server.serve_forever) def __del__(self): self.stop() def stop(self): self._server.shutdown() @property def url(self): host, port = self.server_address proto = 'http' if self._server.ssl_context is None else 'https' return '%s://%s:%i' % (proto, host, port) class ContentServer(WSGIServer): """ Small test server which can be taught which content (i.e. string) to serve with which response code. Try the following snippet for testing API calls:: server = ContentServer(port=8080) server.start() print 'Test server running at http://%s:%i' % server.server_address # any request to http://localhost:8080 will get a 503 response. server.content = 'Hello World!' server.code = 503 # ... # we're done server.stop() """ def __init__(self, host='127.0.0.1', port=0, ssl_context=None): super(ContentServer, self).__init__(host, port, self, ssl_context=ssl_context) self.content, self.code = ('', 204) # HTTP 204: No Content self.headers = {} self.show_post_vars = False self.compress = None def __call__(self, environ, start_response): """ This is the WSGI application. """ request = Request(environ) if (request.content_type == 'application/x-www-form-urlencoded' and request.method == 'POST' and self.show_post_vars): content = json.dumps(request.form) else: content = self.content response = Response(status=self.code) response.headers.clear() response.headers.extend(self.headers) # FIXME get compression working! # if self.compress == 'gzip': # content = gzip.compress(content.encode('utf-8')) # response.content_encoding = 'gzip' response.data = content return response(environ, start_response) def serve_content(self, content, code=200, headers=None): """ Serves string content (with specified HTTP error code) as response to all subsequent request. :param content: content to be displayed :param code: HTTP status code :param headers: HTTP headers to be returned """ self.content, self.code = (content, code) if headers: self.headers = headers if __name__ == '__main__': # pragma: no cover import os.path import time app = ContentServer() server = WSGIServer(application=app) server.start() print('HTTP server is running at %s' % server.url) print('Type to stop') try: path = sys.argv[1] except IndexError: path = os.path.join( os.path.dirname(os.path.abspath(__file__)), '..', 'README') app.serve_content(open(path).read(), 302) try: while True: time.sleep(1) except KeyboardInterrupt: print('\rstopping...') server.stop() pytest-localserver-0.3.4/setup.py0000666000000000000000000000405112463451602015604 0ustar rootrootfrom setuptools import setup, Command import sys VERSION = '0.3.4' def read(fname): # makes sure that setup can be executed from a different location import os.path _here = os.path.abspath(os.path.dirname(__file__)) return open(os.path.join(_here, fname)).read() # make sure that versions match before uploading anything to the cheeseshop if 'upload' in sys.argv or 'register' in sys.argv: import pytest_localserver assert pytest_localserver.VERSION == VERSION class PyTest(Command): user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): import sys, subprocess errno = subprocess.call([sys.executable, 'runtests.py']) raise SystemExit(errno) setup( name='pytest-localserver', version=VERSION, author='Sebastian Rahlf', author_email='basti AT redtoad DOT de', license='MIT License', description='py.test plugin to test server connections locally.', long_description=read('README'), url='http://bitbucket.org/basti/pytest-localserver/', download_url='http://bitbucket.org/basti/pytest-localserver/downloads/', packages=['pytest_localserver'], install_requires=[ 'werkzeug>=0.10' ], cmdclass={'test': PyTest}, tests_require=[ 'pytest>=2.0.0', 'six', 'requests' ], entry_points={ 'pytest11': ['localserver = pytest_localserver.plugin'] }, zip_safe=False, include_package_data=True, keywords='py.test pytest server localhost http smtp', classifiers=[ 'Operating System :: OS Independent', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Topic :: Software Development :: Testing' ] ) pytest-localserver-0.3.4/tox.ini0000666000000000000000000000042012312374770015404 0ustar rootroot[tox] envlist = py26,py27,py33 recreate = True [tox:hudson] downloadcache = {toxworkdir}/_download [testenv] deps = pytest>=2.0.0 six requests commands = py.test -v \ --junitxml=junit-{envname}.xml \ [] # substitute with tox' positional arguments pytest-localserver-0.3.4/PKG-INFO0000666000000000000000000002327612463453400015177 0ustar rootrootMetadata-Version: 1.1 Name: pytest-localserver Version: 0.3.4 Summary: py.test plugin to test server connections locally. Home-page: http://bitbucket.org/basti/pytest-localserver/ Author: Sebastian Rahlf Author-email: basti AT redtoad DOT de License: MIT License Download-URL: http://bitbucket.org/basti/pytest-localserver/downloads/ Description: ================== pytest-localserver ================== pytest-localserver is a plugin for the `pytest`_ testing framework which enables you to test server connections locally. Sometimes `monkeypatching`_ ``urllib2.urlopen()`` just does not cut it, for instance if you work with ``urllib2.Request``, define your own openers/handlers or work with ``httplib``. In these cases it may come in handy to have an HTTP server running locally which behaves just like the real thing [1]_. Well, look no further! Quickstart ========== Let's say you have a function to scrape HTML which only required to be pointed at a URL :: import requests def scrape(url): html = requests.get(url).text # some parsing happens here # ... return result You want to test this function in its entirety without having to rely on a remote server whose content you cannot control, neither do you want to waste time setting up a complex mechanism to mock or patch the underlying Python modules dealing with the actual HTTP request (of which there are more than one BTW). So what do you do? You simply use pytest's `funcargs feature`_ and simulate an entire server locally! :: def test_retrieve_some_content(httpserver): httpserver.serve_content(open('cached-content.html').read()) assert scrape(httpserver.url) == 'Found it!' What happened here is that for the duration of your tests an HTTP server is started on a random port on localhost which will serve the content you tell it to and behaves just like the real thing. The added bonus is that you can test whether your code behaves gracefully if there is a network problem:: def test_content_retrieval_fails_graciously(httpserver): httpserver.serve_content('File not found!', 404) pytest.raises(ContentNotFoundException, scrape, httpserver.url) The same thing works for SMTP servers, too:: def test_sending_some_message(smtpserver): mailer = MyMailer(host=smtpserver.addr[0], port=smtpserver.addr[1]) mailer.send(to='bob@example.com', from_='alice@example.com', subject='MyMailer v1.0', body='Check out my mailer!') assert len(smtpserver.outbox)==1 Here an SMTP server is started which accepts e-mails being sent to it. The nice feature here is that you can actually check if the message was received and what was sent by looking into the smtpserver's ``outbox``. It is really that easy! Available funcargs ================== Here is a short overview of the available funcargs. For more details I suggest poking around in the code itself. ``httpserver`` provides a threaded HTTP server instance running on localhost. It has the following attributes: * ``code`` - HTTP response code (int) * ``content`` - content of next response (str) * ``headers`` - response headers (dict) Once these attribute are set, all subsequent requests will be answered with these values until they are changed or the server is stopped. A more convenient way to change these is :: httpserver.serve_content(content=None, code=200, headers=None) The server address can be found in property * ``url`` which is the string representation of tuple ``server_address`` (host as str, port as int). If you want to check which form fields have been POSTed, Try :: httpserver.serve_content(..., show_post_vars=True) which will display them as parsable text. ``httpsserver`` is the same as ``httpserver`` only with SSL encryption. ``smtpserver`` provides a threaded instance of ``smtpd.SMTPServer`` runnning on localhost. It has the following attributes: * ``addr`` - server address as tuple (host as str, port as int) * ``outbox`` - list of ``email.message.Message`` instances received. Using your a WSGI application as test server ============================================ As of version 0.3 you can now use a `WSGI application`_ to run on the test server :: from pytest_localserver.http import WSGIServer def simple_app(environ, start_response): """Simplest possible WSGI application""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['Hello world!\n'] def pytest_funcarg__testserver(request): """Defines the testserver funcarg""" server = WSGIServer(application=simple_app) server.start() request.addfinalizer(server.stop) return server def test_retrieve_some_content(testserver): assert scrape(testserver.url) == 'Hello world!\n' Have a look at the following page for more information on WSGI: http://wsgi.readthedocs.org/en/latest/learn.html Download and Installation ========================= You can install the plugin by running :: pip install pytest-localserver Alternatively, get the latest stable version from `PyPI`_ or the latest `bleeding-edge archive`_ from bitbucket.org. License and Credits =================== This plugin is released under the MIT license. You can find the full text of the license in the LICENSE file. Copyright (C) 2010-2013 Sebastian Rahlf and others (see AUTHORS). Some parts of this package is based on ideas or code from other people: - I borrowed some implementation ideas for the httpserver from `linkchecker`_. - The implementation for the SMTP server is based on the `Mailsink recipe`_ by Adam Feuer, Matt Branthwaite and Troy Frever. - The HTTPS implementation is based on work by `Sebastien Martini`_. Thanks guys! Development and future plans ============================ Feel free to clone the repository and add your own changes. Pull requests are always welcome!:: hg clone https://bitbucket.org/basti/pytest-localserver If you find any bugs, please file a `report`_. Test can be run with tox. Note that you need virtualenv<1.8 to run tests for Python 2.4. I already have a couple of ideas for future versions: * support for FTP, SSH (maybe base all on twisted?) * making the SMTP outbox as convenient to use as ``django.core.mail.outbox`` * add your own here! ---- .. [1] The idea for this project was born when I needed to check that `a piece of software`_ behaved itself when receiving HTTP error codes 404 and 500. Having unsuccessfully tried to mock a server, I stumbled across `linkchecker`_ which uses a the same idea to test its internals. .. _monkeypatching: http://pytest.org/latest/monkeypatch.html .. _pytest: http://pytest.org/ .. _funcargs feature: http://pytest.org/latest/funcargs.html .. _linkchecker: http://linkchecker.sourceforge.net/ .. _WSGI application: http://www.python.org/dev/peps/pep-0333/ .. _PyPI: http://pypi.python.org/pypi/pytest-localserver/ .. _bleeding-edge archive: https://bitbucket.org/basti/pytest-localserver/get/tip.tar.gz .. _report: https://bitbucket.org/basti/pytest-localserver/issues/ .. _tox: http://testrun.org/tox/ .. _a piece of software: http://pypi.python.org/pypi/python-amazon-product-api/ .. _Mailsink recipe: http://code.activestate.com/recipes/440690/ .. _Sebastien Martini: http://code.activestate.com/recipes/442473/ Keywords: py.test pytest server localhost http smtp Platform: UNKNOWN Classifier: Operating System :: OS Independent Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Software Development :: Testing pytest-localserver-0.3.4/MANIFEST.in0000666000000000000000000000024412312374770015633 0ustar rootrootinclude LICENSE include MANIFEST.in include README include pytest_localserver/server.* include pytest_localserver/ca.* recursive-include tests *.py include tox.ini pytest-localserver-0.3.4/setup.cfg0000666000000000000000000000010012463453400015700 0ustar rootroot[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0