revamping docs + tests
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,3 +11,5 @@ dist/
|
||||
.tox/
|
||||
_public/
|
||||
tests/functional/fixtures/recording-*.json
|
||||
#*
|
||||
*#*
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
language: python
|
||||
sudo: false
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
@@ -14,7 +15,7 @@ env:
|
||||
- TEST_TYPE=functional
|
||||
|
||||
install:
|
||||
- pip install -r requirements.txt -r test-requirements.txt
|
||||
- pip install -r development.txt
|
||||
|
||||
script:
|
||||
- make $TEST_TYPE
|
||||
|
||||
35
Makefile
35
Makefile
@@ -1,4 +1,14 @@
|
||||
all: check_dependencies unit functional doctests
|
||||
# Config
|
||||
OSNAME := $(shell uname)
|
||||
|
||||
ifeq ($(OSNAME), Linux)
|
||||
OPEN_COMMAND := gnome-open
|
||||
else
|
||||
OPEN_COMMAND := open
|
||||
endif
|
||||
|
||||
|
||||
all: check_dependencies unit functional acceptance
|
||||
|
||||
filename=httpretty-`python -c 'import httpretty;print httpretty.version'`.tar.gz
|
||||
|
||||
@@ -11,17 +21,21 @@ check_dependencies:
|
||||
python -c "import $$dependency" 2>/dev/null || (echo "You must install $$dependency in order to run httpretty's tests" && exit 3) ; \
|
||||
done
|
||||
|
||||
test: unit functional doctests
|
||||
test: unit functional acceptance
|
||||
|
||||
unit: prepare
|
||||
lint:
|
||||
@echo "Checking code style ..."
|
||||
@flake8 httpretty
|
||||
|
||||
unit: prepare lint
|
||||
@echo "Running unit tests ..."
|
||||
@nosetests -s tests/unit
|
||||
@nosetests --rednose -x --with-randomly --with-coverage --cover-package=httpretty -s tests/unit
|
||||
|
||||
functional: prepare
|
||||
@echo "Running functional tests ..."
|
||||
@nosetests -s tests/functional
|
||||
@nosetests --rednose -x --with-randomly --with-coverage --cover-package=httpretty -s tests/functional
|
||||
|
||||
doctests: prepare
|
||||
acceptance: prepare
|
||||
@echo "Running documentation tests tests ..."
|
||||
@steadymark README.md
|
||||
|
||||
@@ -35,9 +49,9 @@ release: clean unit functional
|
||||
@./.release
|
||||
@python setup.py sdist bdist_wheel register upload
|
||||
|
||||
docs: doctests
|
||||
@pandoc -o README.rst README.md
|
||||
@markment -o . -t ./theme --sitemap-for="http://falcao.it/HTTPretty" docs
|
||||
docs: acceptance
|
||||
@cd docs && make html
|
||||
$(OPEN_COMMAND) docs/build/html/index.html
|
||||
|
||||
deploy-docs:
|
||||
@git co master && \
|
||||
@@ -51,3 +65,6 @@ deploy-docs:
|
||||
|
||||
prepare:
|
||||
@reset
|
||||
|
||||
|
||||
.PHONY: docs
|
||||
|
||||
@@ -508,7 +508,7 @@ make unit functional
|
||||
# License
|
||||
|
||||
<HTTPretty - HTTP client mock for Python>
|
||||
Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -551,7 +551,7 @@ License
|
||||
::
|
||||
|
||||
<HTTPretty - HTTP client mock for Python>
|
||||
Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
-r requirements.txt
|
||||
coverage
|
||||
tox
|
||||
markment
|
||||
steadymark
|
||||
nose
|
||||
rednose
|
||||
mock
|
||||
sure
|
||||
17
development.txt
Normal file
17
development.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
# Test dependencies, both for local environment and CI server
|
||||
flake8==2.5.1
|
||||
httplib2==0.9.2
|
||||
ipdb==0.8.1
|
||||
misaka==2.0.0
|
||||
mock==1.3.0
|
||||
nose==1.3.7
|
||||
nose-randomly==1.2.0
|
||||
rednose==0.4.3
|
||||
requests==2.8.1
|
||||
steadymark==0.7.1
|
||||
sure==1.2.24
|
||||
urllib3==1.12
|
||||
tornado==4.3
|
||||
coverage==4.0.3
|
||||
Sphinx==1.3.3
|
||||
sphinx-rtd-theme==0.1.9
|
||||
192
docs/Makefile
Normal file
192
docs/Makefile
Normal file
@@ -0,0 +1,192 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " applehelp to make an Apple Help Book"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/HTTPretty.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/HTTPretty.qhc"
|
||||
|
||||
applehelp:
|
||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||
@echo
|
||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||
"~/Library/Documentation/Help or install it in your application" \
|
||||
"bundle."
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/HTTPretty"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/HTTPretty"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
coverage:
|
||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||
@echo "Testing of coverage in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/coverage/python.txt."
|
||||
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
@@ -28,7 +28,7 @@ make unit functional
|
||||
# License
|
||||
|
||||
<HTTPretty - HTTP client mock for Python>
|
||||
Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
21
docs/source/acks.rst
Normal file
21
docs/source/acks.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
Acknowledgements
|
||||
###############
|
||||
|
||||
caveats
|
||||
=======
|
||||
|
||||
``forcing_headers`` + ``Content-Length``
|
||||
----------------------------------------
|
||||
if you use the ``forcing_headers`` options make sure to add the header
|
||||
``Content-Length`` otherwise the
|
||||
[requests](http://docs.python-requests.org/en/latest/) will try to
|
||||
load the response endlessly
|
||||
|
||||
supported libraries
|
||||
-------------------
|
||||
|
||||
Because HTTPretty works in the socket level it should work with any HTTP client libraries, although it is `battle tested <https://github.com/gabrielfalcao/HTTPretty/tree/master/tests/functional>`_ against:
|
||||
|
||||
* `requests <http://docs.python-requests.org/en/latest/>`_
|
||||
* `httplib2 <http://code.google.com/p/httplib2/>`_
|
||||
* `urllib2 <http://docs.python.org/2/library/urllib2.html>`_
|
||||
20
docs/source/api-reference.rst
Normal file
20
docs/source/api-reference.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
.. _API Reference:
|
||||
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. automodule:: httpretty.core
|
||||
:members:
|
||||
|
||||
.. automodule:: httpretty.http
|
||||
:members:
|
||||
|
||||
.. automodule:: httpretty.utils
|
||||
:members:
|
||||
|
||||
.. automodule:: httpretty.errors
|
||||
:members:
|
||||
|
||||
.. automodule:: httpretty.compat
|
||||
:members:
|
||||
368
docs/source/conf.py
Normal file
368
docs/source/conf.py
Normal file
@@ -0,0 +1,368 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# HTTPretty documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun Dec 13 07:25:00 2015.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shlex
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.ifconfig',
|
||||
'sphinx.ext.viewcode',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'HTTPretty'
|
||||
copyright = u'2015, Gabriel Falcão'
|
||||
author = u'Gabriel Falcão'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0.8.10'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0.8.10'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
import sphinx_rtd_theme
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Language to be used for generating the HTML full-text search index.
|
||||
# Sphinx supports the following languages:
|
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
|
||||
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
|
||||
#html_search_language = 'en'
|
||||
|
||||
# A dictionary with options for the search language support, empty by default.
|
||||
# Now only 'ja' uses this config value
|
||||
#html_search_options = {'type': 'default'}
|
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
#html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'HTTPrettydoc'
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'HTTPretty.tex', u'HTTPretty Documentation',
|
||||
u'Gabriel Falcão', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'httpretty', u'HTTPretty Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'HTTPretty', u'HTTPretty Documentation',
|
||||
author, 'HTTPretty', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
|
||||
|
||||
# -- Options for Epub output ----------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = project
|
||||
epub_author = author
|
||||
epub_publisher = author
|
||||
epub_copyright = copyright
|
||||
|
||||
# The basename for the epub file. It defaults to the project name.
|
||||
#epub_basename = project
|
||||
|
||||
# The HTML theme for the epub output. Since the default themes are not optimized
|
||||
# for small screen space, using the same theme for HTML and epub output is
|
||||
# usually not wise. This defaults to 'epub', a theme designed to save visual
|
||||
# space.
|
||||
#epub_theme = 'epub'
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or 'en' if the language is not set.
|
||||
#epub_language = ''
|
||||
|
||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
#epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#epub_uid = ''
|
||||
|
||||
# A tuple containing the cover image and cover page html template filenames.
|
||||
#epub_cover = ()
|
||||
|
||||
# A sequence of (type, uri, title) tuples for the guide element of content.opf.
|
||||
#epub_guide = ()
|
||||
|
||||
# HTML files that should be inserted before the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
#epub_pre_files = []
|
||||
|
||||
# HTML files shat should be inserted after the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
#epub_post_files = []
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
|
||||
# The depth of the table of contents in toc.ncx.
|
||||
#epub_tocdepth = 3
|
||||
|
||||
# Allow duplicate toc entries.
|
||||
#epub_tocdup = True
|
||||
|
||||
# Choose between 'default' and 'includehidden'.
|
||||
#epub_tocscope = 'default'
|
||||
|
||||
# Fix unsupported image types using the Pillow.
|
||||
#epub_fix_images = False
|
||||
|
||||
# Scale large images.
|
||||
#epub_max_image_width = 0
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#epub_show_urls = 'inline'
|
||||
|
||||
# If false, no index is generated.
|
||||
#epub_use_index = True
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
23
docs/source/index.rst
Normal file
23
docs/source/index.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
.. HTTPretty documentation master file, created by
|
||||
sphinx-quickstart on Sun Dec 13 07:25:00 2015.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to HTTPretty's documentation!
|
||||
=====================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
tutorial
|
||||
acks
|
||||
api-reference
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
100
docs/source/tutorial.rst
Normal file
100
docs/source/tutorial.rst
Normal file
@@ -0,0 +1,100 @@
|
||||
What is HTTPretty ?
|
||||
###################
|
||||
|
||||
.. highlight:: python
|
||||
|
||||
Once upon a time a python developer wanted to use a RESTful api,
|
||||
everything was fine but until the day he needed to test the code that
|
||||
hits the RESTful API: what if the API server is down? What if its
|
||||
content has changed ?
|
||||
|
||||
Don't worry, HTTPretty is here for you:
|
||||
|
||||
::
|
||||
|
||||
import requests
|
||||
from sure import expect
|
||||
import httpretty
|
||||
|
||||
|
||||
@httpretty.activate
|
||||
def test_yipit_api_returning_deals():
|
||||
httpretty.register_uri(httpretty.GET, "http://api.yipit.com/v1/deals/",
|
||||
body='[{"title": "Test Deal"}]',
|
||||
content_type="application/json")
|
||||
|
||||
response = requests.get('http://api.yipit.com/v1/deals/')
|
||||
|
||||
expect(response.json()).to.equal([{"title": "Test Deal"}])
|
||||
|
||||
|
||||
A more technical description
|
||||
============================
|
||||
|
||||
HTTPretty is a HTTP client mock library for Python 100% inspired on ruby's [FakeWeb](http://fakeweb.rubyforge.org/).
|
||||
If you come from ruby this would probably sound familiar :smiley:
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
||||
Installing httpretty is as easy as:
|
||||
|
||||
.. highlight:: bash
|
||||
|
||||
::
|
||||
|
||||
pip install HTTPretty
|
||||
|
||||
|
||||
Demo
|
||||
####
|
||||
|
||||
expecting a simple response body
|
||||
================================
|
||||
|
||||
.. highlight:: python
|
||||
|
||||
::
|
||||
|
||||
import requests
|
||||
import httpretty
|
||||
|
||||
def test_one():
|
||||
httpretty.enable() # enable HTTPretty so that it will monkey patch the socket module
|
||||
httpretty.register_uri(httpretty.GET, "http://yipit.com/",
|
||||
body="Find the best daily deals")
|
||||
|
||||
response = requests.get('http://yipit.com')
|
||||
|
||||
assert response.text == "Find the best daily deals"
|
||||
|
||||
httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module
|
||||
httpretty.reset() # reset HTTPretty state (clean up registered urls and request history)
|
||||
|
||||
|
||||
Motivation
|
||||
##########
|
||||
|
||||
When building systems that access external resources such as RESTful
|
||||
webservices, XMLRPC or even simple HTTP requests, we stumble in the
|
||||
problem:
|
||||
|
||||
*"I'm gonna need to mock all those requests"*
|
||||
|
||||
It brings a lot of hassle, you will need to use a generic mocking
|
||||
tool, mess with scope and so on.
|
||||
|
||||
The idea behind HTTPretty (how it works)
|
||||
========================================
|
||||
|
||||
|
||||
HTTPretty `monkey patches <http://en.wikipedia.org/wiki/Monkey_patch>`_
|
||||
Python's `socket <http://docs.python.org/library/socket.html>`_ core
|
||||
module, reimplementing the HTTP protocol, by mocking requests and
|
||||
responses.
|
||||
|
||||
As for how it works this way, you don't need to worry what http
|
||||
library you're gonna use.
|
||||
|
||||
HTTPretty will mock the response for you :) *(and also give you the
|
||||
latest requests so that you can check them)*
|
||||
@@ -1,7 +1,7 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
@@ -55,6 +55,33 @@ def last_request():
|
||||
"""returns the last request"""
|
||||
return httpretty.last_request
|
||||
|
||||
|
||||
def has_request():
|
||||
"""returns a boolean indicating whether any request has been made"""
|
||||
return not isinstance(httpretty.last_request.headers, EmptyRequestHeaders)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'last_request',
|
||||
'has_request',
|
||||
'GET',
|
||||
'PUT',
|
||||
'POST',
|
||||
'DELETE',
|
||||
'HEAD',
|
||||
'PATCH',
|
||||
'OPTIONS',
|
||||
'CONNECT',
|
||||
'register_uri',
|
||||
'enable',
|
||||
'disable',
|
||||
'is_enabled',
|
||||
'reset',
|
||||
'Response',
|
||||
'URIInfo',
|
||||
'UnmockedError',
|
||||
'HTTPrettyError',
|
||||
'httpretty',
|
||||
'httprettified',
|
||||
'EmptyRequestHeaders',
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
@@ -37,9 +37,6 @@ if PY3: # pragma: no cover
|
||||
StringIO = io.BytesIO
|
||||
basestring = (str, bytes)
|
||||
|
||||
class BaseClass(object):
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
else: # pragma: no cover
|
||||
text_type = unicode
|
||||
byte_type = str
|
||||
@@ -49,6 +46,7 @@ else: # pragma: no cover
|
||||
|
||||
|
||||
class BaseClass(object):
|
||||
|
||||
def __repr__(self):
|
||||
ret = self.__str__()
|
||||
if PY3: # pragma: no cover
|
||||
@@ -58,11 +56,17 @@ class BaseClass(object):
|
||||
|
||||
|
||||
try: # pragma: no cover
|
||||
from urllib.parse import urlsplit, urlunsplit, parse_qs, quote, quote_plus, unquote
|
||||
from urllib.parse import urlsplit
|
||||
from urllib.parse import urlunsplit
|
||||
from urllib.parse import parse_qs
|
||||
from urllib.parse import quote
|
||||
from urllib.parse import quote_plus
|
||||
from urllib.parse import unquote
|
||||
unquote_utf8 = unquote
|
||||
except ImportError: # pragma: no cover
|
||||
from urlparse import urlsplit, urlunsplit, parse_qs, unquote
|
||||
from urllib import quote, quote_plus
|
||||
|
||||
def unquote_utf8(qs):
|
||||
if isinstance(qs, text_type):
|
||||
qs = qs.encode('utf-8')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
@@ -32,7 +32,6 @@ import socket
|
||||
import functools
|
||||
import itertools
|
||||
import warnings
|
||||
import logging
|
||||
import traceback
|
||||
import json
|
||||
import contextlib
|
||||
@@ -43,6 +42,7 @@ from .compat import (
|
||||
PY3,
|
||||
StringIO,
|
||||
text_type,
|
||||
byte_type,
|
||||
BaseClass,
|
||||
BaseHTTPRequestHandler,
|
||||
quote,
|
||||
@@ -50,7 +50,6 @@ from .compat import (
|
||||
urlunsplit,
|
||||
urlsplit,
|
||||
parse_qs,
|
||||
unquote,
|
||||
unquote_utf8,
|
||||
ClassTypes,
|
||||
basestring
|
||||
@@ -84,8 +83,9 @@ old_ssl_wrap_socket = None
|
||||
old_sslwrap_simple = None
|
||||
old_sslsocket = None
|
||||
|
||||
if PY3: # pragma: no cover
|
||||
basestring = (bytes, str)
|
||||
MULTILINE_ANY_REGEX = re.compile(r'.*', re.M)
|
||||
|
||||
|
||||
try: # pragma: no cover
|
||||
import socks
|
||||
old_socksocket = socks.socksocket
|
||||
@@ -109,7 +109,7 @@ POTENTIAL_HTTPS_PORTS = set(DEFAULT_HTTPS_PORTS)
|
||||
|
||||
|
||||
class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
|
||||
"""Represents a HTTP request. It takes a valid multi-line, `\r\n`
|
||||
"""Represents a HTTP request. It takes a valid multi-line, ``\r\n``
|
||||
separated string with HTTP headers and parse them out using the
|
||||
internal `parse_request` method.
|
||||
|
||||
@@ -119,26 +119,26 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
|
||||
|
||||
It has some convenience attributes:
|
||||
|
||||
`headers` -> a mimetype object that can be cast into a dictionary,
|
||||
``headers`` -> a mimetype object that can be cast into a dictionary,
|
||||
contains all the request headers
|
||||
|
||||
`method` -> the HTTP method used in this request
|
||||
``method`` -> the HTTP method used in this request
|
||||
|
||||
`querystring` -> a dictionary containing lists with the
|
||||
``querystring`` -> a dictionary containing lists with the
|
||||
attributes. Please notice that if you need a single value from a
|
||||
query string you will need to get it manually like:
|
||||
|
||||
```python
|
||||
>>> request.querystring
|
||||
{'name': ['Gabriel Falcao']}
|
||||
>>> print request.querystring['name'][0]
|
||||
```
|
||||
::
|
||||
|
||||
`parsed_body` -> a dictionary containing parsed request body or
|
||||
>>> request.querystring
|
||||
{'name': ['Gabriel Falcao']}
|
||||
>>> print request.querystring['name'][0]
|
||||
|
||||
``parsed_body`` -> a dictionary containing parsed request body or
|
||||
None if HTTPrettyRequest doesn't know how to parse it. It
|
||||
currently supports parsing body data that was sent under the
|
||||
`content-type` headers values: 'application/json' or
|
||||
'application/x-www-form-urlencoded'
|
||||
``content`-type` headers values: ``application/json`` or
|
||||
``application/x-www-form-urlencoded``
|
||||
"""
|
||||
def __init__(self, headers, body=''):
|
||||
# first of all, lets make sure that if headers or body are
|
||||
@@ -150,9 +150,10 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
|
||||
# Now let's concatenate the headers with the body, and create
|
||||
# `rfile` based on it
|
||||
self.rfile = StringIO(b'\r\n\r\n'.join([self.raw_headers, self.body]))
|
||||
self.wfile = StringIO() # Creating `wfile` as an empty
|
||||
# StringIO, just to avoid any real
|
||||
# I/O calls
|
||||
|
||||
# Creating `wfile` as an empty StringIO, just to avoid any
|
||||
# real I/O calls
|
||||
self.wfile = StringIO()
|
||||
|
||||
# parsing the request line preemptively
|
||||
self.raw_requestline = self.rfile.readline()
|
||||
@@ -185,8 +186,12 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
|
||||
# `application/json` or `application/x-www-form-urlencoded`
|
||||
self.parsed_body = self.parse_request_body(self.body)
|
||||
|
||||
def __nonzero__(self):
|
||||
return bool(self.body) or bool(self.raw_headers)
|
||||
|
||||
def __str__(self):
|
||||
return '<HTTPrettyRequest("{0}", total_headers={1}, body_length={2})>'.format(
|
||||
tmpl = '<HTTPrettyRequest("{0}", total_headers={1}, body_length={2})>'
|
||||
return tmpl.format(
|
||||
self.headers.get('content-type', ''),
|
||||
len(self.headers),
|
||||
len(self.body),
|
||||
@@ -202,7 +207,8 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
|
||||
return result
|
||||
|
||||
def parse_request_body(self, body):
|
||||
""" Attempt to parse the post based on the content-type passed. Return the regular body if not """
|
||||
"""Attempt to parse the post based on the content-type passed.
|
||||
Return the regular body if not"""
|
||||
|
||||
PARSING_FUNCTIONS = {
|
||||
'application/json': json.loads,
|
||||
@@ -251,15 +257,15 @@ class fakesock(object):
|
||||
_sent_data = []
|
||||
|
||||
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM,
|
||||
protocol=0):
|
||||
protocol=0, _sock=None):
|
||||
self.truesock = (old_socket(family, type, protocol)
|
||||
if httpretty.allow_net_connect
|
||||
else None)
|
||||
self._closed = True
|
||||
self.fd = FakeSockFile()
|
||||
self.fd.socket = self
|
||||
self.fd.socket = _sock or self
|
||||
self.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
|
||||
self._sock = self
|
||||
self._sock = _sock or self
|
||||
self.is_http = False
|
||||
self._bufsize = 1024
|
||||
|
||||
@@ -306,7 +312,9 @@ class fakesock(object):
|
||||
# See issue #206
|
||||
self.is_http = False
|
||||
else:
|
||||
self.is_http = self._port in POTENTIAL_HTTP_PORTS | POTENTIAL_HTTPS_PORTS
|
||||
ports_to_check = (
|
||||
POTENTIAL_HTTP_PORTS.union(POTENTIAL_HTTPS_PORTS))
|
||||
self.is_http = self._port in ports_to_check
|
||||
|
||||
if not self.is_http:
|
||||
if self.truesock:
|
||||
@@ -371,7 +379,7 @@ class fakesock(object):
|
||||
try:
|
||||
received = self.truesock.recv(self._bufsize)
|
||||
self.fd.write(received)
|
||||
should_continue = len(received) == self._bufsize
|
||||
should_continue = bool(received.strip())
|
||||
|
||||
except socket.error as e:
|
||||
if e.errno == EAGAIN:
|
||||
@@ -386,14 +394,20 @@ class fakesock(object):
|
||||
self.fd.socket = self
|
||||
try:
|
||||
requestline, _ = data.split(b'\r\n', 1)
|
||||
method, path, version = parse_requestline(decode_utf8(requestline))
|
||||
method, path, version = parse_requestline(
|
||||
decode_utf8(requestline))
|
||||
is_parsing_headers = True
|
||||
except ValueError:
|
||||
path = ''
|
||||
is_parsing_headers = False
|
||||
|
||||
if not self._entry:
|
||||
# If the previous request wasn't mocked, don't mock the subsequent sending of data
|
||||
if self._entry is None:
|
||||
# If the previous request wasn't mocked, don't
|
||||
# mock the subsequent sending of data
|
||||
return self.real_sendall(data, *args, **kw)
|
||||
else:
|
||||
method = self._entry.method
|
||||
path = self._entry.info.path
|
||||
|
||||
self.fd.seek(0)
|
||||
|
||||
@@ -403,7 +417,11 @@ class fakesock(object):
|
||||
meta = self._entry.request.headers
|
||||
body = utf8(self._sent_data[-1])
|
||||
if meta.get('transfer-encoding', '') == 'chunked':
|
||||
if not body.isdigit() and body != b'\r\n' and body != b'0\r\n\r\n':
|
||||
if (
|
||||
not body.isdigit()
|
||||
and (body != b'\r\n')
|
||||
and (body != b'0\r\n\r\n')
|
||||
):
|
||||
self._entry.request.body += body
|
||||
else:
|
||||
self._entry.request.body += body
|
||||
@@ -414,14 +432,22 @@ class fakesock(object):
|
||||
# path might come with
|
||||
s = urlsplit(path)
|
||||
POTENTIAL_HTTP_PORTS.add(int(s.port or 80))
|
||||
headers, body = list(map(utf8, data.split(b'\r\n\r\n', 1)))
|
||||
parts = list(map(utf8, data.split(b'\r\n\r\n', 1)))
|
||||
if len(parts) == 2:
|
||||
headers, body = parts
|
||||
else:
|
||||
headers = ''
|
||||
body = data
|
||||
|
||||
request = httpretty.historify_request(headers, body)
|
||||
|
||||
info = URIInfo(hostname=self._host, port=self._port,
|
||||
path=s.path,
|
||||
query=s.query,
|
||||
last_request=request)
|
||||
info = URIInfo(
|
||||
hostname=self._host,
|
||||
port=self._port,
|
||||
path=s.path,
|
||||
query=s.query,
|
||||
last_request=request
|
||||
)
|
||||
|
||||
matcher, entries = httpretty.match_uriinfo(info)
|
||||
|
||||
@@ -480,7 +506,10 @@ def fake_wrap_socket(s, *args, **kw):
|
||||
return s
|
||||
|
||||
|
||||
def create_fake_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None):
|
||||
def create_fake_connection(
|
||||
address,
|
||||
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
source_address=None):
|
||||
s = fakesock.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
|
||||
if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
|
||||
s.settimeout(timeout)
|
||||
@@ -555,15 +584,15 @@ class Entry(BaseClass):
|
||||
igot = int(got)
|
||||
except (ValueError, TypeError):
|
||||
warnings.warn(
|
||||
'HTTPretty got to register the Content-Length header ' \
|
||||
'HTTPretty got to register the Content-Length header '
|
||||
'with "%r" which is not a number' % got)
|
||||
return
|
||||
|
||||
if igot > self.body_length:
|
||||
raise HTTPrettyError(
|
||||
'HTTPretty got inconsistent parameters. The header ' \
|
||||
'Content-Length you registered expects size "%d" but ' \
|
||||
'the body you registered for that has actually length ' \
|
||||
'HTTPretty got inconsistent parameters. The header '
|
||||
'Content-Length you registered expects size "%d" but '
|
||||
'the body you registered for that has actually length '
|
||||
'"%d".' % (
|
||||
igot, self.body_length,
|
||||
)
|
||||
@@ -595,12 +624,18 @@ class Entry(BaseClass):
|
||||
headers = self.forcing_headers
|
||||
|
||||
if self.adding_headers:
|
||||
headers.update(self.normalize_headers(self.adding_headers))
|
||||
headers.update(
|
||||
self.normalize_headers(
|
||||
self.adding_headers))
|
||||
|
||||
headers = self.normalize_headers(headers)
|
||||
status = headers.get('status', self.status)
|
||||
if self.body_is_callable:
|
||||
status, headers, self.body = self.callable_body(self.request, self.info.full_url(), headers)
|
||||
status, headers, self.body = self.callable_body(
|
||||
self.request,
|
||||
self.info.full_url(),
|
||||
headers
|
||||
)
|
||||
headers.update({
|
||||
'content-length': len(self.body)
|
||||
})
|
||||
@@ -616,7 +651,8 @@ class Entry(BaseClass):
|
||||
content_type = headers.pop('content-type',
|
||||
'text/plain; charset=utf-8')
|
||||
|
||||
content_length = headers.pop('content-length', self.body_length)
|
||||
content_length = headers.pop('content-length',
|
||||
byte_type(self.body_length))
|
||||
|
||||
string_list.append('content-type: %s' % content_type)
|
||||
if not self.streaming:
|
||||
@@ -778,7 +814,7 @@ class URIMatcher(object):
|
||||
self.entries = entries
|
||||
self.priority = priority
|
||||
|
||||
#hash of current_entry pointers, per method.
|
||||
# hash of current_entry pointers, per method.
|
||||
self.current_entries = {}
|
||||
|
||||
def matches(self, info):
|
||||
@@ -802,7 +838,8 @@ class URIMatcher(object):
|
||||
if method not in self.current_entries:
|
||||
self.current_entries[method] = 0
|
||||
|
||||
#restrict selection to entries that match the requested method
|
||||
# restrict selection to entries that match the requested
|
||||
# method
|
||||
entries_for_method = [e for e in self.entries if e.method == method]
|
||||
|
||||
if self.current_entries[method] >= len(entries_for_method):
|
||||
@@ -860,13 +897,17 @@ class httpretty(HttpBaseClass):
|
||||
try:
|
||||
import urllib3
|
||||
except ImportError:
|
||||
raise RuntimeError('HTTPretty requires urllib3 installed for recording actual requests.')
|
||||
|
||||
msg = (
|
||||
'HTTPretty requires urllib3 installed '
|
||||
'for recording actual requests.'
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
http = urllib3.PoolManager()
|
||||
|
||||
cls.enable()
|
||||
calls = []
|
||||
|
||||
def record_request(request, uri, headers):
|
||||
cls.disable()
|
||||
|
||||
@@ -889,7 +930,7 @@ class httpretty(HttpBaseClass):
|
||||
return response.status, response.headers, response.data
|
||||
|
||||
for method in cls.METHODS:
|
||||
cls.register_uri(method, re.compile(r'.*', re.M), body=record_request)
|
||||
cls.register_uri(method, MULTILINE_ANY_REGEX, body=record_request)
|
||||
|
||||
yield
|
||||
cls.disable()
|
||||
@@ -905,7 +946,9 @@ class httpretty(HttpBaseClass):
|
||||
for item in data:
|
||||
uri = item['request']['uri']
|
||||
method = item['request']['method']
|
||||
cls.register_uri(method, uri, body=item['response']['body'], forcing_headers=item['response']['headers'])
|
||||
body = item['response']['body']
|
||||
headers = item['response']['headers']
|
||||
cls.register_uri(method, uri, body=body, forcing_headers=headers)
|
||||
|
||||
yield
|
||||
cls.disable()
|
||||
@@ -969,8 +1012,15 @@ class httpretty(HttpBaseClass):
|
||||
return '<HTTPretty with %d URI entries>' % len(self._entries)
|
||||
|
||||
@classmethod
|
||||
def Response(cls, body, method=None, uri=None, adding_headers=None, forcing_headers=None,
|
||||
status=200, streaming=False, **headers):
|
||||
def Response(
|
||||
cls, body,
|
||||
method=None,
|
||||
uri=None,
|
||||
adding_headers=None,
|
||||
forcing_headers=None,
|
||||
status=200,
|
||||
streaming=False,
|
||||
**headers):
|
||||
|
||||
headers[str('body')] = body
|
||||
headers[str('adding_headers')] = adding_headers
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# HTTPretty doesn't have any requirements per se so far. yay!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
@@ -26,16 +26,28 @@
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
from functools import wraps
|
||||
from sure import scenario
|
||||
import json
|
||||
|
||||
from os.path import abspath, dirname, join
|
||||
from httpretty.core import POTENTIAL_HTTP_PORTS
|
||||
from httpretty.core import POTENTIAL_HTTP_PORTS, old_socket
|
||||
|
||||
|
||||
def get_free_tcp_port():
|
||||
"""returns a TCP port that can be used for listen in the host.
|
||||
"""
|
||||
tcp = old_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
tcp.bind(('', 0))
|
||||
host, port = tcp.getsockname()
|
||||
tcp.close()
|
||||
return port
|
||||
|
||||
|
||||
LOCAL_FILE = lambda *path: join(abspath(dirname(__file__)), *path)
|
||||
@@ -53,7 +65,7 @@ class JSONEchoHandler(tornado.web.RequestHandler):
|
||||
|
||||
|
||||
class JSONEchoServer(threading.Thread):
|
||||
def __init__(self, lock, port=8888, *args, **kw):
|
||||
def __init__(self, lock, port, *args, **kw):
|
||||
self.lock = lock
|
||||
self.port = int(port)
|
||||
self._stop = threading.Event()
|
||||
@@ -78,14 +90,16 @@ class JSONEchoServer(threading.Thread):
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
|
||||
|
||||
def use_tornado_server(callback):
|
||||
lock = threading.Lock()
|
||||
lock.acquire()
|
||||
|
||||
@wraps(callback)
|
||||
def func(*args, **kw):
|
||||
server = JSONEchoServer(lock, os.getenv('TEST_PORT', 8888))
|
||||
port = os.getenv('TEST_PORT', get_free_tcp_port())
|
||||
POTENTIAL_HTTP_PORTS.add(port)
|
||||
kw['port'] = port
|
||||
server = JSONEchoServer(lock, port)
|
||||
server.start()
|
||||
try:
|
||||
lock.acquire()
|
||||
@@ -93,6 +107,6 @@ def use_tornado_server(callback):
|
||||
finally:
|
||||
lock.release()
|
||||
server.stop()
|
||||
if 8888 in POTENTIAL_HTTP_PORTS:
|
||||
POTENTIAL_HTTP_PORTS.remove(8888)
|
||||
if port in POTENTIAL_HTTP_PORTS:
|
||||
POTENTIAL_HTTP_PORTS.remove(port)
|
||||
return func
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <httpretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
@@ -25,13 +25,15 @@
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import time
|
||||
import requests
|
||||
try:
|
||||
import urllib.request as urllib2
|
||||
except ImportError:
|
||||
import urllib2
|
||||
|
||||
from .testserver import TornadoServer, TCPServer, TCPClient
|
||||
from .base import get_free_tcp_port
|
||||
from sure import expect, that_with_context
|
||||
|
||||
import functools
|
||||
@@ -41,8 +43,23 @@ from httpretty import core, HTTPretty
|
||||
|
||||
|
||||
def start_http_server(context):
|
||||
context.server = TornadoServer(9999)
|
||||
httpretty.disable()
|
||||
context.http_port = get_free_tcp_port()
|
||||
context.server = TornadoServer(context.http_port)
|
||||
context.server.start()
|
||||
ready = False
|
||||
timeout = 2
|
||||
started_at = time.time()
|
||||
while not ready:
|
||||
httpretty.disable()
|
||||
time.sleep(.1)
|
||||
try:
|
||||
requests.get('http://localhost:{0}/'.format(context.http_port))
|
||||
ready = True
|
||||
except:
|
||||
if time.time() - started_at >= timeout:
|
||||
break
|
||||
|
||||
httpretty.enable()
|
||||
|
||||
|
||||
@@ -52,9 +69,10 @@ def stop_http_server(context):
|
||||
|
||||
|
||||
def start_tcp_server(context):
|
||||
context.server = TCPServer(8888)
|
||||
context.tcp_port = get_free_tcp_port()
|
||||
context.server = TCPServer(context.tcp_port)
|
||||
context.server.start()
|
||||
context.client = TCPClient(8888)
|
||||
context.client = TCPClient(context.tcp_port)
|
||||
httpretty.enable()
|
||||
|
||||
|
||||
@@ -70,19 +88,19 @@ def test_httpretty_bypasses_when_disabled(context):
|
||||
"httpretty should bypass all requests by disabling it"
|
||||
|
||||
httpretty.register_uri(
|
||||
httpretty.GET, "http://localhost:9999/go-for-bubbles/",
|
||||
httpretty.GET, "http://localhost:{0}/go-for-bubbles/".format(context.http_port),
|
||||
body="glub glub")
|
||||
|
||||
httpretty.disable()
|
||||
|
||||
fd = urllib2.urlopen('http://localhost:9999/go-for-bubbles/')
|
||||
fd = urllib2.urlopen('http://localhost:{0}/go-for-bubbles/'.format(context.http_port))
|
||||
got1 = fd.read()
|
||||
fd.close()
|
||||
|
||||
expect(got1).to.equal(
|
||||
b'. 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 .')
|
||||
|
||||
fd = urllib2.urlopen('http://localhost:9999/come-again/')
|
||||
fd = urllib2.urlopen('http://localhost:{0}/come-again/'.format(context.http_port))
|
||||
got2 = fd.read()
|
||||
fd.close()
|
||||
|
||||
@@ -90,12 +108,13 @@ def test_httpretty_bypasses_when_disabled(context):
|
||||
|
||||
httpretty.enable()
|
||||
|
||||
fd = urllib2.urlopen('http://localhost:9999/go-for-bubbles/')
|
||||
fd = urllib2.urlopen('http://localhost:{0}/go-for-bubbles/'.format(context.http_port))
|
||||
got3 = fd.read()
|
||||
fd.close()
|
||||
|
||||
expect(got3).to.equal(b'glub glub')
|
||||
core.POTENTIAL_HTTP_PORTS.remove(9999)
|
||||
core.POTENTIAL_HTTP_PORTS.remove(context.http_port)
|
||||
|
||||
|
||||
@httpretty.activate
|
||||
@that_with_context(start_http_server, stop_http_server)
|
||||
@@ -103,21 +122,21 @@ def test_httpretty_bypasses_a_unregistered_request(context):
|
||||
"httpretty should bypass a unregistered request by disabling it"
|
||||
|
||||
httpretty.register_uri(
|
||||
httpretty.GET, "http://localhost:9999/go-for-bubbles/",
|
||||
httpretty.GET, "http://localhost:{0}/go-for-bubbles/".format(context.http_port),
|
||||
body="glub glub")
|
||||
|
||||
fd = urllib2.urlopen('http://localhost:9999/go-for-bubbles/')
|
||||
fd = urllib2.urlopen('http://localhost:{0}/go-for-bubbles/'.format(context.http_port))
|
||||
got1 = fd.read()
|
||||
fd.close()
|
||||
|
||||
expect(got1).to.equal(b'glub glub')
|
||||
|
||||
fd = urllib2.urlopen('http://localhost:9999/come-again/')
|
||||
fd = urllib2.urlopen('http://localhost:{0}/come-again/'.format(context.http_port))
|
||||
got2 = fd.read()
|
||||
fd.close()
|
||||
|
||||
expect(got2).to.equal(b'<- HELLO WORLD ->')
|
||||
core.POTENTIAL_HTTP_PORTS.remove(9999)
|
||||
core.POTENTIAL_HTTP_PORTS.remove(context.http_port)
|
||||
|
||||
|
||||
@httpretty.activate
|
||||
@@ -163,7 +182,7 @@ def test_disallow_net_connect_1(context):
|
||||
def foo():
|
||||
fd = None
|
||||
try:
|
||||
fd = urllib2.urlopen('http://localhost:9999/go-for-bubbles/')
|
||||
fd = urllib2.urlopen('http://localhost:{0}/go-for-bubbles/'.format(context.http_port))
|
||||
finally:
|
||||
if fd:
|
||||
fd.close()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
@@ -52,8 +52,7 @@ except NameError:
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
PORT = int(os.getenv('TEST_PORT') or 8888)
|
||||
server_url = lambda path: "http://localhost:{0}/{1}".format(PORT, path.lstrip('/'))
|
||||
server_url = lambda path, port: "http://localhost:{0}/{1}".format(port, path.lstrip('/'))
|
||||
|
||||
|
||||
@httprettified
|
||||
@@ -701,15 +700,15 @@ def test_unicode_querystrings():
|
||||
|
||||
|
||||
@use_tornado_server
|
||||
def test_recording_calls():
|
||||
def test_recording_calls(port):
|
||||
("HTTPretty should be able to record calls")
|
||||
# Given a destination path:
|
||||
destination = FIXTURE_FILE("recording-.json")
|
||||
destination = FIXTURE_FILE("recording-1.json")
|
||||
|
||||
# When I record some calls
|
||||
with HTTPretty.record(destination):
|
||||
requests.get(server_url("/foobar?name=Gabriel&age=25"))
|
||||
requests.post(server_url("/foobar"), data=json.dumps({'test': '123'}))
|
||||
requests.get(server_url("/foobar?name=Gabriel&age=25", port))
|
||||
requests.post(server_url("/foobar", port), data=json.dumps({'test': '123'}))
|
||||
|
||||
# Then the destination path should exist
|
||||
os.path.exists(destination).should.be.true
|
||||
@@ -741,19 +740,13 @@ def test_recording_calls():
|
||||
response['response'].should.have.key("status").being.equal(200)
|
||||
response['response'].should.have.key("body").being.an(text_type)
|
||||
response['response'].should.have.key("headers").being.a(dict)
|
||||
response['response']["headers"].should.have.key("server").being.equal("TornadoServer/" + tornado_version)
|
||||
response['response']["headers"].should.have.key("Server").being.equal("TornadoServer/" + tornado_version)
|
||||
|
||||
|
||||
def test_playing_calls():
|
||||
("HTTPretty should be able to record calls")
|
||||
# Given a destination path:
|
||||
destination = FIXTURE_FILE("playback-1.json")
|
||||
|
||||
# When I playback some previously recorded calls
|
||||
# And When I playback the previously recorded calls
|
||||
with HTTPretty.playback(destination):
|
||||
# And make the expected requests
|
||||
response1 = requests.get(server_url("/foobar?name=Gabriel&age=25"))
|
||||
response2 = requests.post(server_url("/foobar"), data=json.dumps({'test': '123'}))
|
||||
response1 = requests.get(server_url("/foobar?name=Gabriel&age=25", port))
|
||||
response2 = requests.post(server_url("/foobar", port), data=json.dumps({'test': '123'}))
|
||||
|
||||
# Then the responses should be the expected
|
||||
response1.json().should.equal({"foobar": {"age": "25", "name": "Gabriel"}})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -441,7 +441,7 @@ def test_fakesock_socket_real_sendall_when_http(POTENTIAL_HTTP_PORTS, old_socket
|
||||
|
||||
# And the potential http port is 4000
|
||||
POTENTIAL_HTTP_PORTS.__contains__.side_effect = lambda other: int(other) == 4000
|
||||
POTENTIAL_HTTP_PORTS.__or__.side_effect = lambda other: POTENTIAL_HTTP_PORTS
|
||||
POTENTIAL_HTTP_PORTS.union.side_effect = lambda other: POTENTIAL_HTTP_PORTS
|
||||
|
||||
# Given a fake socket
|
||||
socket = fakesock.socket()
|
||||
@@ -472,8 +472,8 @@ def test_fakesock_socket_real_sendall_when_http(POTENTIAL_HTTP_PORTS, old_socket
|
||||
@patch('httpretty.core.POTENTIAL_HTTP_PORTS')
|
||||
def test_fakesock_socket_sendall_with_valid_requestline(POTENTIAL_HTTP_PORTS, httpretty, old_socket):
|
||||
("fakesock.socket#sendall should create an entry if it's given a valid request line")
|
||||
matcher = Mock()
|
||||
info = Mock()
|
||||
matcher = Mock(name='matcher')
|
||||
info = Mock(name='info')
|
||||
httpretty.match_uriinfo.return_value = (matcher, info)
|
||||
httpretty.register_uri(httpretty.GET, 'http://foo.com/foobar')
|
||||
|
||||
@@ -496,10 +496,10 @@ def test_fakesock_socket_sendall_with_valid_requestline(POTENTIAL_HTTP_PORTS, ht
|
||||
@patch('httpretty.core.old_socket')
|
||||
@patch('httpretty.core.httpretty')
|
||||
@patch('httpretty.core.POTENTIAL_HTTP_PORTS')
|
||||
def test_fakesock_socket_sendall_with_valid_requestline(POTENTIAL_HTTP_PORTS, httpretty, old_socket):
|
||||
def test_fakesock_socket_sendall_with_valid_requestline_2(POTENTIAL_HTTP_PORTS, httpretty, old_socket):
|
||||
("fakesock.socket#sendall should create an entry if it's given a valid request line")
|
||||
matcher = Mock()
|
||||
info = Mock()
|
||||
matcher = Mock(name='matcher')
|
||||
info = Mock(name='info')
|
||||
httpretty.match_uriinfo.return_value = (matcher, info)
|
||||
httpretty.register_uri(httpretty.GET, 'http://foo.com/foobar')
|
||||
|
||||
@@ -525,6 +525,7 @@ def test_fakesock_socket_sendall_with_body_data_no_entry(POTENTIAL_HTTP_PORTS, o
|
||||
("fakesock.socket#sendall should call real_sendall when not parsing headers and there is no entry")
|
||||
# Background:
|
||||
# Using a subclass of socket that mocks out real_sendall
|
||||
|
||||
class MySocket(fakesock.socket):
|
||||
def real_sendall(self, data):
|
||||
data.should.equal(b'BLABLABLABLA')
|
||||
@@ -547,21 +548,17 @@ def test_fakesock_socket_sendall_with_body_data_no_entry(POTENTIAL_HTTP_PORTS, o
|
||||
@patch('httpretty.core.old_socket')
|
||||
@patch('httpretty.core.POTENTIAL_HTTP_PORTS')
|
||||
def test_fakesock_socket_sendall_with_body_data_with_entry(POTENTIAL_HTTP_PORTS, old_socket):
|
||||
("fakesock.socket#sendall should call real_sendall when not ")
|
||||
("fakesock.socket#sendall should call real_sendall when there is no entry")
|
||||
# Background:
|
||||
# Using a subclass of socket that mocks out real_sendall
|
||||
data_sent = []
|
||||
|
||||
class MySocket(fakesock.socket):
|
||||
def real_sendall(self, data):
|
||||
raise AssertionError('should have never been called')
|
||||
# Using a mocked entry
|
||||
entry = Mock()
|
||||
entry.request.headers = {}
|
||||
entry.request.body = b''
|
||||
data_sent.append(data)
|
||||
|
||||
# Given an instance of that socket
|
||||
socket = MySocket()
|
||||
socket._entry = entry
|
||||
|
||||
|
||||
# And that is is considered http
|
||||
socket.connect(('foo.com', 80))
|
||||
@@ -569,21 +566,31 @@ def test_fakesock_socket_sendall_with_body_data_with_entry(POTENTIAL_HTTP_PORTS,
|
||||
# When I try to send data
|
||||
socket.sendall(b"BLABLABLABLA")
|
||||
|
||||
# Then the entry should have that body
|
||||
entry.request.body.should.equal(b'BLABLABLABLA')
|
||||
# Then it shoud have called real_sendall
|
||||
data_sent.should.equal(['BLABLABLABLA'])
|
||||
|
||||
|
||||
@patch('httpretty.core.httpretty.match_uriinfo')
|
||||
@patch('httpretty.core.old_socket')
|
||||
@patch('httpretty.core.POTENTIAL_HTTP_PORTS')
|
||||
def test_fakesock_socket_sendall_with_body_data_with_chunked_entry(POTENTIAL_HTTP_PORTS, old_socket):
|
||||
def test_fakesock_socket_sendall_with_body_data_with_chunked_entry(POTENTIAL_HTTP_PORTS, old_socket, match_uriinfo):
|
||||
("fakesock.socket#sendall should call real_sendall when not ")
|
||||
# Background:
|
||||
# Using a subclass of socket that mocks out real_sendall
|
||||
|
||||
class MySocket(fakesock.socket):
|
||||
def real_sendall(self, data):
|
||||
raise AssertionError('should have never been called')
|
||||
|
||||
matcher = Mock(name='matcher')
|
||||
info = Mock(name='info')
|
||||
httpretty.match_uriinfo.return_value = (matcher, info)
|
||||
|
||||
# Using a mocked entry
|
||||
entry = Mock()
|
||||
entry.method = 'GET'
|
||||
entry.info.path = '/foo'
|
||||
|
||||
entry.request.headers = {
|
||||
'transfer-encoding': 'chunked',
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011-2013> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
# Copyright (C) <2011-2015> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
@@ -238,7 +238,9 @@ def test_uri_info_eq_ignores_case():
|
||||
)
|
||||
expect(uri_info_uppercase).to.equal(uri_info_lowercase)
|
||||
|
||||
|
||||
def test_global_boolean_enabled():
|
||||
HTTPretty.disable()
|
||||
expect(HTTPretty.is_enabled()).to.be.falsy
|
||||
HTTPretty.enable()
|
||||
expect(HTTPretty.is_enabled()).to.be.truthy
|
||||
|
||||
@@ -12,8 +12,11 @@ def test_last_request(original):
|
||||
|
||||
httpretty.last_request().should.equal(original.last_request)
|
||||
|
||||
|
||||
def test_has_request():
|
||||
"""httpretty.has_request() correctly detects whether or not a request has been made"""
|
||||
("httpretty.has_request() correctly detects "
|
||||
"whether or not a request has been made")
|
||||
httpretty.reset()
|
||||
httpretty.has_request().should.be.false
|
||||
with patch('httpretty.httpretty.last_request', return_value=HTTPrettyRequest('')):
|
||||
httpretty.has_request().should.be.true
|
||||
|
||||
Reference in New Issue
Block a user