pytest-httpbin-0.3.0/000077500000000000000000000000001317517426100145175ustar00rootroot00000000000000pytest-httpbin-0.3.0/.travis.yml000066400000000000000000000001711317517426100166270ustar00rootroot00000000000000language: python sudo: false python: - 2.6 - 2.7 - 3.4 - 3.5 - 3.6 - pypy install: pip install -U tox-travis script: tox pytest-httpbin-0.3.0/DESCRIPTION.rst000066400000000000000000000022221317517426100170320ustar00rootroot00000000000000pytest-httpbin ============== httpbin is an amazing web service for testing HTTP libraries. It has several great endpoints that can test pretty much everything you need in a HTTP library. The only problem is: maybe you don't want to wait for your tests to travel across the Internet and back to make assertions against a remote web service. Enter pytest-httpbin. Pytest-httpbin creates a pytest "fixture" that is dependency-injected into your tests. It automatically starts up a HTTP server in a separate thread running httpbin and provides your test with the URL in the fixture. Check out this example: .. code-block:: python def test_that_my_library_works_kinda_ok(httpbin): assert requests.get(httpbin.url + '/get/').status_code == 200 This replaces a test that might have looked like this before: .. code-block:: python def test_that_my_library_works_kinda_ok(): assert requests.get('http://httpbin.org/get').status_code == 200 pytest-httpbin also supports https and includes its own CA cert you can use. Check out `the full documentation`_ on the github page. .. _the full documentation: https://github.com/kevin1024/pytest-httpbin pytest-httpbin-0.3.0/MANIFEST.in000066400000000000000000000002511317517426100162530ustar00rootroot00000000000000# If using Python 2.6 or less, then have to include package data, even though # it's already declared in setup.py include pytest_httpbin/certs/* include DESCRIPTION.rst pytest-httpbin-0.3.0/README.md000066400000000000000000000202701317517426100157770ustar00rootroot00000000000000# pytest-httpbin [![Build Status](https://travis-ci.org/kevin1024/pytest-httpbin.svg?branch=master)](https://travis-ci.org/kevin1024/pytest-httpbin) [httpbin](https://httpbin.org/) is an amazing web service for testing HTTP libraries. It has several great endpoints that can test pretty much everything you need in a HTTP library. The only problem is: maybe you don't want to wait for your tests to travel across the Internet and back to make assertions against a remote web service (speed), and maybe you want to work offline (convenience). Enter **pytest-httpbin**. Pytest-httpbin creates a [pytest fixture](http://pytest.org/latest/fixture.html) that is dependency-injected into your tests. It automatically starts up a HTTP server in a separate thread running httpbin and provides your test with the URL in the fixture. Check out this example: ```python def test_that_my_library_works_kinda_ok(httpbin): assert requests.get(httpbin.url + '/get').status_code == 200 ``` This replaces a test that might have looked like this before: ```python def test_that_my_library_works_kinda_ok(): assert requests.get('http://httpbin.org/get').status_code == 200 ``` If you're making a lot of requests to httpbin, it can radically speed up your tests. ![demo](http://i.imgur.com/heNOQLP.gif) # HTTPS support pytest-httpbin also supports HTTPS: ```python def test_that_my_library_works_kinda_ok(httpbin_secure): assert requests.get(httpbin_secure.url + '/get/').status_code == 200 ``` It's actually starting 2 web servers in separate threads in the background: one HTTP and one HTTPS. The servers are started on a random port (see below for fixed port support), on the loopback interface on your machine. Pytest-httpbin includes a self-signed certificate. If your library verifies certificates against a CA (and it should), you'll have to add the CA from pytest-httpbin. The path to the pytest-httpbin CA bundle can by found like this `python -m pytest_httpbin.certs`. For example in requests, you can set the `REQUESTS_CA_BUNDLE` python path. You can run your tests like this: ```bash REQUESTS_CA_BUNDLE=`python -m pytest_httpbin.certs` py.test tests/ ``` # API of the injected object The injected object has the following attributes: * url * port * host and the following methods: * join(string): Returns the results of calling `urlparse.urljoin` with the url from the injected server automatically applied as the first argument. You supply the second argument Also, I defined `__add__` on the object to append to `httpbin.url`. This means you can do stuff like `httpbin + '/get'` instead of `httpbin.url + '/get'`. ## Testing both HTTP and HTTPS endpoints with one test If you ever find yourself needing to test both the http and https version of and endpoint, you can use the `httpbin_both` funcarg like this: ```python def test_that_my_library_works_kinda_ok(httpbin_both): assert requests.get(httpbin_both.url + '/get/').status_code == 200 ``` Through the magic of pytest parametrization, this function will actually execute twice: once with an http url and once with an https url. ## Using pytest-httpbin with unittest-style test cases I have provided 2 additional fixtures to make testing with class-based tests easier. I have also provided a couple decorators that provide some syntactic sugar around the pytest method of adding the fixtures to class-based tests. Just add the `use_class_based_httpbin` and/or `use_class_based_httpbin_secure` class decorators to your class, and then you can access httpbin using self.httpbin and self.httpbin_secure. ```python import pytest_httpbin @pytest_httpbin.use_class_based_httpbin @pytest_httpbin.use_class_based_httpbin_secure class TestClassBassedTests(unittest.TestCase): def test_http(self): assert requests.get(self.httpbin.url + '/get').response def test_http_secure(self): assert requests.get(self.httpbin_secure.url + '/get').response ``` ## Running the server on fixed port Sometimes a randomized port can be a problem. Worry not, you can fix the port number to a desired value with the `HTTPBIN_HTTP_PORT` and `HTTPBIN_HTTPS_PORT` environment variables. If those are defined during pytest plugins are loaded, `httbin` and `httpbin_secure` fixtures will run on given ports. You can run your tests like this: ```bash HTTPBIN_HTTP_PORT=8080 HTTPBIN_HTTPS_PORT=8443 py.test tests/ ``` ## Installation All you need to do is this: ```bash pip install pytest-httpbin ``` and your tests executed by pytest all will have access to the `httpbin` and `httpbin_secure` funcargs. Cool right? ## Support and dependencies pytest-httpbin suports Python 2.6, 2.7, 3.4, and pypy. It will automatically install httpbin and flask when you install it from pypi. [httpbin](https://github.com/kennethreitz/httpbin) itself does not support python 2.6 as of version 0.6.0, when the Flask-common dependency was added. If you need python 2.6 support pin the httpbin version to 0.5.0 ## Running the pytest-httpbin test suite If you want to run pytest-httpbin's test suite, you'll need to install requests and pytest, and then use the ./runtests.sh script. ```bash pip install pytest /.runtests.sh ``` Also, you can use tox to run the tests on all supported python versions: ```bash pip install tox tox ``` ## Changelog * 0.3.0 * Allow to run httpbin on fixed port using environment variables (thanks @hroncok) * Allow server to be thread.join()ed (thanks @graingert) * Add support for Python 3.6 (thanks @graingert) * 0.2.3: * Another attempt to fix #32 (Rare bug, only happens on Travis) * 0.2.2: * Fix bug with python3 * 0.2.1: * Attempt to fix strange, impossible-to-reproduce bug with broken SSL certs that only happens on Travis (#32) [Bad release, breaks py3] * 0.2.0: * Remove threaded HTTP server. I built it for Requests, but they deleted their threaded test since it didn't really work very well. The threaded server seems to cause some strange problems with HTTP chunking, so I'll just remove it since nobody is using it (I hope) * 0.1.1: * Fix weird hang with SSL on pypy (again) * 0.1.0: * Update server to use multithreaded werkzeug server * 0.0.7: * Update the certificates (they expired) * 0.0.6: * Fix an issue where pypy was hanging when a request was made with an invalid certificate * 0.0.5: * Fix broken version parsing in 0.0.4 * 0.0.4: * **Bad release: Broken version parsing** * Fix `BadStatusLine` error that occurs when sending multiple requests in a single session (PR #16). Thanks @msabramo! * Fix #9 ("Can't be installed at the same time than pytest?") (PR #14). Thanks @msabramo! * Add `httpbin_ca_bundle` pytest fixture. With this fixture there is no need to specify the bundle on every request, as it will automatically set `REQUESTS_CA_BUNDLE` if using [requests](http://docs.python-requests.org/). And you don't have to care about where it is located (PR #8). Thanks @t-8ch! * 0.0.3: Add a couple test fixtures to make testing old class-based test suites easier * 0.0.2: Fixed a couple bugs with the wsgiref server to bring behavior in line with httpbin.org, thanks @jakubroztocil for the bug reports * 0.0.1: Initial release ## License The MIT License (MIT) Copyright (c) 2014-2015 Kevin McCarthy 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-httpbin-0.3.0/pytest_httpbin/000077500000000000000000000000001317517426100175775ustar00rootroot00000000000000pytest-httpbin-0.3.0/pytest_httpbin/__init__.py000066400000000000000000000005561317517426100217160ustar00rootroot00000000000000import os import pytest here = os.path.dirname(__file__) version_file = os.path.join(here, "version.py") with open(version_file) as f: code = compile(f.read(), version_file, 'exec') exec(code) use_class_based_httpbin = pytest.mark.usefixtures("class_based_httpbin") use_class_based_httpbin_secure = pytest.mark.usefixtures("class_based_httpbin_secure") pytest-httpbin-0.3.0/pytest_httpbin/certs.py000066400000000000000000000010001317517426100212600ustar00rootroot00000000000000""" certs.py ~~~~~~~~ This module returns the preferred default CA certificate bundle. If you are packaging pytest-httpbin, e.g., for a Linux distribution or a managed environment, you can change the definition of where() to return a separately packaged CA bundle. """ import os.path def where(): """Return the preferred certificate bundle.""" # vendored bundle inside Requests return os.path.join(os.path.dirname(__file__), 'certs', 'cacert.pem') if __name__ == '__main__': print(where()) pytest-httpbin-0.3.0/pytest_httpbin/certs/000077500000000000000000000000001317517426100207175ustar00rootroot00000000000000pytest-httpbin-0.3.0/pytest_httpbin/certs/cacert.pem000066400000000000000000000063561317517426100226750ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: dd:39:30:16:60:55:90:7c Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, ST=Hawaii, O=kevin1024, CN=pytest-httpbin Certificate Authority Validity Not Before: Jun 26 18:16:59 2015 GMT Not After : Jun 18 18:16:59 2045 GMT Subject: C=US, ST=Hawaii, O=kevin1024, CN=pytest-httpbin Certificate Authority Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:bd:80:fd:e4:96:0e:3b:5e:35:9b:83:00:34:88: 64:5a:50:53:0e:1d:94:76:c9:dc:e7:b5:59:1e:d4: 82:55:36:a6:b4:41:2c:60:ad:76:f0:cd:42:a0:0f: 4a:1c:0d:d7:29:da:c3:d9:c0:ea:f1:48:e0:66:4d: 4b:7c:ff:d6:5e:e0:73:89:53:8b:6e:6c:57:7d:bd: e9:d0:46:39:5d:85:a5:f1:3a:d4:3d:83:19:03:44: 93:71:2c:5e:d7:61:8e:db:cc:80:d0:f1:c0:47:bf: 98:8f:06:40:e1:f7:41:ee:ed:a7:57:0d:a6:4c:26: 75:8e:f1:78:d3:80:ad:9c:e9 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: AE:05:EF:BD:A9:CE:BB:A6:D8:0E:EB:C4:8C:72:2F:13:E5:CD:AA:CA X509v3 Authority Key Identifier: keyid:AE:05:EF:BD:A9:CE:BB:A6:D8:0E:EB:C4:8C:72:2F:13:E5:CD:AA:CA DirName:/C=US/ST=Hawaii/O=kevin1024/CN=pytest-httpbin Certificate Authority serial:DD:39:30:16:60:55:90:7C X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption bc:0c:b4:21:03:bf:35:bf:88:9f:de:06:23:f4:e3:8f:bc:34: b5:8b:af:bf:31:5d:17:44:2c:72:c9:88:25:d1:c7:d0:1c:70: 06:82:a5:fa:fa:d7:b9:16:64:c2:08:54:1e:4c:93:9f:22:4e: e5:4f:a7:71:e5:6e:14:31:e9:41:e2:33:23:8b:c8:01:c3:2a: 66:a8:d8:df:ef:ee:7b:bb:84:f4:78:a6:ca:8f:29:aa:d5:fa: 8a:73:94:0c:32:53:c8:93:bd:fc:c4:60:4d:9a:80:4f:c6:d4: 27:44:a2:37:63:6c:97:04:ce:e3:6a:6f:d3:84:0d:b4:74:1f: 49:eb -----BEGIN CERTIFICATE----- MIIDBzCCAnCgAwIBAgIJAN05MBZgVZB8MA0GCSqGSIb3DQEBBQUAMGExCzAJBgNV BAYTAlVTMQ8wDQYDVQQIEwZIYXdhaWkxEjAQBgNVBAoTCWtldmluMTAyNDEtMCsG A1UEAxMkcHl0ZXN0LWh0dHBiaW4gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTE1 MDYyNjE4MTY1OVoXDTQ1MDYxODE4MTY1OVowYTELMAkGA1UEBhMCVVMxDzANBgNV BAgTBkhhd2FpaTESMBAGA1UEChMJa2V2aW4xMDI0MS0wKwYDVQQDEyRweXRlc3Qt aHR0cGJpbiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcNAQEBBQAD gY0AMIGJAoGBAL2A/eSWDjteNZuDADSIZFpQUw4dlHbJ3Oe1WR7UglU2prRBLGCt dvDNQqAPShwN1ynaw9nA6vFI4GZNS3z/1l7gc4lTi25sV3296dBGOV2FpfE61D2D GQNEk3EsXtdhjtvMgNDxwEe/mI8GQOH3Qe7tp1cNpkwmdY7xeNOArZzpAgMBAAGj gcYwgcMwHQYDVR0OBBYEFK4F772pzrum2A7rxIxyLxPlzarKMIGTBgNVHSMEgYsw gYiAFK4F772pzrum2A7rxIxyLxPlzarKoWWkYzBhMQswCQYDVQQGEwJVUzEPMA0G A1UECBMGSGF3YWlpMRIwEAYDVQQKEwlrZXZpbjEwMjQxLTArBgNVBAMTJHB5dGVz dC1odHRwYmluIENlcnRpZmljYXRlIEF1dGhvcml0eYIJAN05MBZgVZB8MAwGA1Ud EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAvAy0IQO/Nb+In94GI/Tjj7w0tYuv vzFdF0QscsmIJdHH0BxwBoKl+vrXuRZkwghUHkyTnyJO5U+nceVuFDHpQeIzI4vI AcMqZqjY3+/ue7uE9Himyo8pqtX6inOUDDJTyJO9/MRgTZqAT8bUJ0SiN2NslwTO 42pv04QNtHQfSes= -----END CERTIFICATE----- pytest-httpbin-0.3.0/pytest_httpbin/certs/cert.pem000066400000000000000000000074421317517426100223660ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: dd:39:30:16:60:55:90:7e Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, ST=Hawaii, O=kevin1024, CN=pytest-httpbin Certificate Authority Validity Not Before: Jun 26 18:20:35 2015 GMT Not After : Jun 23 18:20:35 2025 GMT Subject: C=US, ST=Hawaii, O=kevin1024, OU=kevin1024, CN=127.0.0.1 Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:ce:b4:0f:0b:86:17:68:24:6f:7c:25:32:73:81: bd:55:38:05:ae:09:29:00:c0:f1:99:30:5a:7f:05: 9f:e7:e9:d3:ce:d0:dd:4f:73:c8:bf:65:04:94:e5: 11:8e:1d:91:f0:88:85:3e:48:d3:09:5b:3f:8f:97: 95:34:bf:8d:00:cb:70:d2:c1:2b:34:dd:99:1d:86: 9b:90:54:a5:de:18:c4:03:3d:53:f0:dd:cc:6d:ec: fb:b9:93:ab:19:85:05:63:2d:34:a6:47:42:71:3b: e4:1e:4a:4c:d9:60:d4:6b:d6:51:a8:4a:30:70:2e: 6c:62:a2:34:da:cf:30:34:97:a4:9d:17:72:0b:b2: 37:69:e2:ca:b6:d5:9f:46:c5:eb:cf:dc:46:b0:fe: ef:37:5e:4f:eb:f3:50:4d:2c:4e:c2:0c:e4:0c:63: c2:d8:ab:a3:d6:a0:12:bf:d6:fc:3f:b6:4c:dc:2b: 9b:c5:ae:83:4d:3b:3c:19:85:50:88:82:a2:5f:ff: de:98:60:fc:12:3a:55:c3:4f:0a:e9:1f:aa:12:cb: f8:ce:14:d6:ed:89:ff:c7:ea:3b:fe:97:87:54:eb: 62:de:cd:ef:6b:e2:9e:47:82:77:55:59:4f:b8:ad: 1b:e0:9d:1a:28:16:9f:6a:cb:b2:44:f9:65:c3:c4: 03:09 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 1E:28:41:6B:12:03:41:29:64:0D:E5:C3:E3:F7:9E:82:0C:66:1E:B9 X509v3 Authority Key Identifier: keyid:AE:05:EF:BD:A9:CE:BB:A6:D8:0E:EB:C4:8C:72:2F:13:E5:CD:AA:CA Signature Algorithm: sha1WithRSAEncryption 67:8c:6d:a1:2f:b3:35:87:a3:c0:04:92:5d:8a:8b:f8:51:6e: 94:88:59:ed:66:b2:54:b0:a2:3d:7a:05:ee:19:17:a6:0b:3b: 20:f7:d2:73:2c:f0:b9:ad:2e:5d:45:11:5d:8d:33:5c:69:7f: 4a:c5:8c:10:3e:35:b4:39:d7:52:66:bc:02:d8:4d:d0:ba:a1: ae:55:f5:36:01:17:97:40:1a:9d:6a:e0:b8:33:be:2d:98:b7: 5b:92:6a:77:a7:d9:f5:5b:a4:5f:fa:aa:5b:c1:6b:4d:0c:b7: 5a:4c:47:b2:f7:90:a3:ff:6f:8c:fd:f2:60:38:53:29:71:48: d7:69 -----BEGIN CERTIFICATE----- MIIDODCCAqGgAwIBAgIJAN05MBZgVZB+MA0GCSqGSIb3DQEBBQUAMGExCzAJBgNV BAYTAlVTMQ8wDQYDVQQIEwZIYXdhaWkxEjAQBgNVBAoTCWtldmluMTAyNDEtMCsG A1UEAxMkcHl0ZXN0LWh0dHBiaW4gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTE1 MDYyNjE4MjAzNVoXDTI1MDYyMzE4MjAzNVowWjELMAkGA1UEBhMCVVMxDzANBgNV BAgTBkhhd2FpaTESMBAGA1UEChMJa2V2aW4xMDI0MRIwEAYDVQQLEwlrZXZpbjEw MjQxEjAQBgNVBAMTCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBAM60DwuGF2gkb3wlMnOBvVU4Ba4JKQDA8ZkwWn8Fn+fp087Q3U9zyL9l BJTlEY4dkfCIhT5I0wlbP4+XlTS/jQDLcNLBKzTdmR2Gm5BUpd4YxAM9U/DdzG3s +7mTqxmFBWMtNKZHQnE75B5KTNlg1GvWUahKMHAubGKiNNrPMDSXpJ0XcguyN2ni yrbVn0bF68/cRrD+7zdeT+vzUE0sTsIM5Axjwtiro9agEr/W/D+2TNwrm8Wug007 PBmFUIiCol//3phg/BI6VcNPCukfqhLL+M4U1u2J/8fqO/6Xh1TrYt7N72vinkeC d1VZT7itG+CdGigWn2rLskT5ZcPEAwkCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O BBYEFB4oQWsSA0EpZA3lw+P3noIMZh65MB8GA1UdIwQYMBaAFK4F772pzrum2A7r xIxyLxPlzarKMA0GCSqGSIb3DQEBBQUAA4GBAGeMbaEvszWHo8AEkl2Ki/hRbpSI We1mslSwoj16Be4ZF6YLOyD30nMs8LmtLl1FEV2NM1xpf0rFjBA+NbQ511JmvALY TdC6oa5V9TYBF5dAGp1q4Lgzvi2Yt1uSanen2fVbpF/6qlvBa00Mt1pMR7L3kKP/ b4z98mA4UylxSNdp -----END CERTIFICATE----- pytest-httpbin-0.3.0/pytest_httpbin/certs/key.pem000066400000000000000000000032201317517426100222070ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAzrQPC4YXaCRvfCUyc4G9VTgFrgkpAMDxmTBafwWf5+nTztDd T3PIv2UElOURjh2R8IiFPkjTCVs/j5eVNL+NAMtw0sErNN2ZHYabkFSl3hjEAz1T 8N3Mbez7uZOrGYUFYy00pkdCcTvkHkpM2WDUa9ZRqEowcC5sYqI02s8wNJeknRdy C7I3aeLKttWfRsXrz9xGsP7vN15P6/NQTSxOwgzkDGPC2Kuj1qASv9b8P7ZM3Cub xa6DTTs8GYVQiIKiX//emGD8EjpVw08K6R+qEsv4zhTW7Yn/x+o7/peHVOti3s3v a+KeR4J3VVlPuK0b4J0aKBafasuyRPllw8QDCQIDAQABAoIBAQCJ//iTbwCtjLXJ omPebd3jyTUxjfgMAsTJy1h/uVea06ePSi6W3uxFq8G1ToG76c4HUn3yqVgLxRnY WhFJWCFhSHGYo1KfRtr0tWuinoDmmI40w3sJMmtLcI5WxVnT/dUs839VC/o18xBH kL9h2Z24KSv3OSDBpJzD9Rtogi7izK8DSQoANBMDEmPPJ5UJBLPjdZn04i6BYZCM U/+ZADHKXbq6I+7RAcbPJbkvrbBEP234KZvIdw1eIAIZufQBQuDhnwS0Fi9iY/EP awoYa9HLgFjh+iprhwh+2SDyIp8DA+4HrY1tXAyzCqjgLn/X8wifOUrZECYj1i65 EOiryxMBAoGBAPjmvIwBRxnr1OsKX3gCFoZr+Zu5RjACD9IOSV17cv7glZQVfXBR REDBoL7CmZrhsW4zBK0YWz30Dx7TGBniTFJ3e8IZJ7Th8PSOhIRYWqqFQ78YBHFi VcpPOBswy1i8BM9FE0GyF1zusmz8Ak2hFr/IHVkIqHwWvkTI6gGhbJ2RAoGBANSZ OqEWJKbRX9nuRqSdROqLOtUgWXZ78yvcQaaifyZHEFSKZZjc5MXT96lVd1PyGGAY uyjAqdd5LiwsS9Rw1cuC5fix2ihH5KFq7EnEJA/zdy91YdO6xmAyBOtjuTHsNj93 if4ilib290/mRKXeI1zpzzWHsvL9Az5spqlkljH5AoGAfln7ewMnCfSbCJoibrR4 pNJpSvEZvUM+rr6L5cXGUbbGl/70x7CpekoRBOWavnI19SA3Dnvfzap4hohYosMr RW3cSGMmsf9Ep5E1mk2T8R5njrltf/WQYXwnmj4B7FC+DE4fgWkbzRRrRUIFFU1i VAcNRuZLSXruKdLoX92HWtECgYEAhpTlf3n0A8JBKkVjZOvF56/xs19CIvY+LsLE sIbndMTBurLNs+IJ1I3llsVqv7Je6d5eBGNKYQPuTbpQ2o//V1Bq4m88CgnQ2rpE EEJhDdPy3BEzt4Ph9p1Tbet4HflJMg4rRbyBTvNCBctgI5wmyLeeG2Xmy1mNhyPi sRLi3YkCgYEAiHMsniJc1gZBevjtnqGTPdUo0syAnkZ7RUk/Piur/c0Altkgu5vK I7p3DbkHBAMDjpAZs1kpfmR4sTYKke+IQDxj2pOZEyYnmQxlGdy8xxoE9dWQeDeg Le+R83OAKjU4LHpH7hhJMR8X60MJaWC1BDACFO35kqIzvtCYxgEoOiI= -----END RSA PRIVATE KEY----- pytest-httpbin-0.3.0/pytest_httpbin/plugin.py000066400000000000000000000021251317517426100214470ustar00rootroot00000000000000from __future__ import absolute_import import pytest from httpbin import app as httpbin_app from . import serve, certs @pytest.fixture(scope='session') def httpbin(request): server = serve.Server(application=httpbin_app) server.start() request.addfinalizer(server.stop) return server @pytest.fixture(scope='session') def httpbin_secure(request): server = serve.SecureServer(application=httpbin_app) server.start() request.addfinalizer(server.stop) return server @pytest.fixture(scope='session', params=['http', 'https']) def httpbin_both(request, httpbin, httpbin_secure): if request.param == 'http': return httpbin elif request.param == 'https': return httpbin_secure @pytest.fixture(scope='class') def class_based_httpbin(request, httpbin): request.cls.httpbin = httpbin @pytest.fixture(scope='class') def class_based_httpbin_secure(request, httpbin_secure): request.cls.httpbin_secure = httpbin_secure @pytest.fixture(scope='function') def httpbin_ca_bundle(monkeypatch): monkeypatch.setenv('REQUESTS_CA_BUNDLE', certs.where()) pytest-httpbin-0.3.0/pytest_httpbin/serve.py000066400000000000000000000077431317517426100213100ustar00rootroot00000000000000import os import threading import ssl from wsgiref.simple_server import WSGIServer, make_server, WSGIRequestHandler from wsgiref.handlers import SimpleHandler from six.moves.urllib.parse import urljoin CERT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'certs') class ServerHandler(SimpleHandler): server_software = 'Pytest-HTTPBIN/0.1.0' http_version = '1.1' def cleanup_headers(self): SimpleHandler.cleanup_headers(self) self.headers['Connection'] = 'Close' def close(self): try: self.request_handler.log_request( self.status.split(' ', 1)[0], self.bytes_sent ) finally: SimpleHandler.close(self) class Handler(WSGIRequestHandler): def handle(self): """Handle a single HTTP request""" self.raw_requestline = self.rfile.readline() if not self.parse_request(): # An error code has been sent, just exit return handler = ServerHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) handler.request_handler = self # backpointer for logging handler.run(self.server.get_app()) def get_environ(self): """ wsgiref simple server adds content-type text/plain to everything, this removes it if it's not actually in the headers. """ # Note: Can't use super since this is an oldstyle class in python 2.x environ = WSGIRequestHandler.get_environ(self).copy() if self.headers.get('content-type') is None: del environ['CONTENT_TYPE'] return environ class SecureWSGIServer(WSGIServer): def finish_request(self, request, client_address): """ Negotiates SSL and then mimics BaseServer behavior. """ request.settimeout(1.0) try: ssock = ssl.wrap_socket( request, keyfile=os.path.join(CERT_DIR, 'key.pem'), certfile=os.path.join(CERT_DIR, 'cert.pem'), server_side=True, suppress_ragged_eofs=False, ) self.RequestHandlerClass(ssock, client_address, self) except Exception as e: print("pytest-httpbin server hit an exception serving request: %s" % e) print("attempting to ignore so the rest of the tests can run") # WSGIRequestHandler seems to close the socket for us. # Thanks, WSGIRequestHandler!! class Server(object): """ HTTP server running a WSGI application in its own thread. """ port_envvar = 'HTTPBIN_HTTP_PORT' def __init__(self, host='127.0.0.1', port=0, application=None, **kwargs): self.app = application if self.port_envvar in os.environ: port = int(os.environ[self.port_envvar]) self._server = make_server( host, port, self.app, handler_class=Handler, **kwargs ) self.host = self._server.server_address[0] self.port = self._server.server_address[1] self.protocol = 'http' self._thread = threading.Thread( name=self.__class__, target=self._server.serve_forever, ) def __del__(self): if hasattr(self, '_server'): self.stop() def start(self): self._thread.start() def __add__(self, other): return self.url + other def stop(self): self._server.shutdown() @property def url(self): return '{0}://{1}:{2}'.format(self.protocol, self.host, self.port) def join(self, url, allow_fragments=True): return urljoin(self.url, url, allow_fragments=allow_fragments) class SecureServer(Server): port_envvar = 'HTTPBIN_HTTPS_PORT' def __init__(self, host='127.0.0.1', port=0, application=None, **kwargs): kwargs['server_class'] = SecureWSGIServer super(SecureServer, self).__init__(host, port, application, **kwargs) self.protocol = 'https' pytest-httpbin-0.3.0/pytest_httpbin/version.py000066400000000000000000000000261317517426100216340ustar00rootroot00000000000000__version__ = '0.3.0' pytest-httpbin-0.3.0/runtests.sh000077500000000000000000000000361317517426100167440ustar00rootroot00000000000000#!/bin/bash py.test $1 -v -s pytest-httpbin-0.3.0/setup.cfg000066400000000000000000000003751317517426100163450ustar00rootroot00000000000000[bdist_wheel] # This flag says that the code is written to work on both Python 2 and Python # 3. If at all possible, it is good practice to do this. If you cannot, you # will need to generate wheels for each Python version that you support. universal=1 pytest-httpbin-0.3.0/setup.py000066400000000000000000000037401317517426100162350ustar00rootroot00000000000000from setuptools import setup, find_packages import codecs import os import re with open("pytest_httpbin/version.py") as f: code = compile(f.read(), "pytest_httpbin/version.py", 'exec') exec(code) here = os.path.abspath(os.path.dirname(__file__)) # Get the long description from the relevant file with codecs.open(os.path.join(here, 'DESCRIPTION.rst'), encoding='utf-8') as f: long_description = f.read() setup( name="pytest-httpbin", # There are various approaches to referencing the version. For a discussion, # see http://packaging.python.org/en/latest/tutorial.html#version version=__version__, description="Easily test your HTTP library against a local copy of httpbin", long_description=long_description, # The project URL. url='https://github.com/kevin1024/pytest-httpbin', # Author details author='Kevin McCarthy', author_email='me@kevinmccarthy.org', # Choose your license license='MIT', classifiers=[ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'Topic :: Software Development :: Testing', 'Topic :: Software Development :: Libraries', '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.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ], # What does your project relate to? keywords='pytest-httpbin testing pytest httpbin', packages=find_packages(exclude=["contrib", "docs", "tests*"]), include_package_data = True, # include files listed in MANIFEST.in install_requires = ['httpbin','six'], # the following makes a plugin available to pytest entry_points = { 'pytest11': [ 'httpbin = pytest_httpbin.plugin', ] }, ) pytest-httpbin-0.3.0/tests/000077500000000000000000000000001317517426100156615ustar00rootroot00000000000000pytest-httpbin-0.3.0/tests/conftest.py000066400000000000000000000001651317517426100200620ustar00rootroot00000000000000import pytest from pytest_httpbin.plugin import httpbin_ca_bundle pytest.fixture(autouse=True)(httpbin_ca_bundle) pytest-httpbin-0.3.0/tests/test_httpbin.py000066400000000000000000000030611317517426100207420ustar00rootroot00000000000000import unittest import requests import pytest_httpbin def test_httpbin_gets_injected(httpbin): assert httpbin.url def test_httpbin_accepts_get_requests(httpbin): assert requests.get(httpbin.url + '/get').status_code == 200 def test_httpbin_secure_accepts_get_requests(httpbin_secure): assert requests.get(httpbin_secure.url + '/get').status_code == 200 def test_httpbin_secure_accepts_lots_of_get_requests(httpbin_secure): for i in range(10): assert requests.get(httpbin_secure.url + '/get').status_code == 200 def test_httpbin_accepts_lots_of_get_requests_in_single_session(httpbin): session = requests.Session() for i in range(10): assert session.get(httpbin.url + '/get').status_code == 200 def test_httpbin_both(httpbin_both): # this test will get called twice, once with an http url, once with an # https url assert requests.get(httpbin_both.url + '/get').status_code == 200 def test_httpbin_join(httpbin): assert httpbin.join('foo') == httpbin.url + '/foo' def test_httpbin_str(httpbin): assert httpbin + '/foo' == httpbin.url + '/foo' def test_chunked_encoding(httpbin_both): assert requests.get(httpbin_both.url + '/stream/20').status_code == 200 @pytest_httpbin.use_class_based_httpbin @pytest_httpbin.use_class_based_httpbin_secure class TestClassBassedTests(unittest.TestCase): def test_http(self): assert requests.get(self.httpbin.url + '/get').status_code == 200 def test_http_secure(self): assert requests.get(self.httpbin_secure.url + '/get').status_code == 200 pytest-httpbin-0.3.0/tests/test_server.py000066400000000000000000000053621317517426100206060ustar00rootroot00000000000000# coding=utf8 # -*- coding: utf8 -*- # vim: set fileencoding=utf8 : import os import requests import pytest from util import get_raw_http_response from pytest_httpbin import serve from httpbin import app as httpbin_app def test_content_type_header_not_automatically_added(httpbin): """ The server was automatically adding this for some reason, see issue #5 """ resp = requests.get(httpbin + '/headers').json()['headers'] assert 'Content-Type' not in resp def test_unicode_data(httpbin): """ UTF-8 was not getting recognized for what it was and being encoded as if it was binary, see issue #7 """ resp = requests.post( httpbin + '/post', data=u'оживлённым'.encode('utf-8'), headers={ 'content-type': 'text/html; charset=utf-8', } ) assert resp.json()['data'] == u'оживлённым' def test_server_should_be_http_1_1(httpbin): """ The server should speak HTTP/1.1 since we live in the future, see issue #6 """ resp = get_raw_http_response(httpbin.host, httpbin.port, '/get') assert resp.startswith(b'HTTP/1.1') def test_dont_crash_on_certificate_problems(httpbin_secure): with pytest.raises(Exception): # this request used to hang requests.get( httpbin_secure + '/get', verify = True, cert=__file__ ) # and this request would never happen requests.get( httpbin_secure + '/get', verify = True, ) @pytest.mark.parametrize('protocol', ('http', 'https')) def test_fixed_port_environment_variables(protocol): """ Note that we cannot test the fixture here because it is session scoped and was already started. Thus, let's just test a new Server instance. """ if protocol == 'http': server_cls = serve.Server envvar = 'HTTPBIN_HTTP_PORT' elif protocol == 'https': server_cls = serve.SecureServer envvar = 'HTTPBIN_HTTPS_PORT' else: raise RuntimeError('Unexpected protocol param: {0}'.format(protocol)) # just have different port to avoid adrress already in use # if the second test run too fast after the first one (happens on pypy) port = 12345 + len(protocol) try: envvar_original = os.environ.get(envvar, None) os.environ[envvar] = str(port) server = server_cls(application=httpbin_app) assert server.port == port finally: # if we don't do this, it blocks: try: server.start() server.stop() except UnboundLocalError: pass # restore the original environ: if envvar_original is None: del os.environ[envvar] else: os.environ[envvar] = envvar_original pytest-httpbin-0.3.0/tests/util.py000066400000000000000000000011231317517426100172050ustar00rootroot00000000000000import socket def get_raw_http_response(host, port, path): CRLF = b"\r\n" request = [ b"GET " + path.encode('ascii') + b" HTTP/1.1", b"Host: " + host.encode('ascii'), b"Connection: Close", b"", b"", ] # Connect to the server s = socket.socket() s.connect((host, port)) # Send an HTTP request s.send(CRLF.join(request)) # Get the response (in several parts, if necessary) response = b'' buffer = s.recv(4096) while buffer: response += buffer buffer = s.recv(4096) return response pytest-httpbin-0.3.0/tox.ini000066400000000000000000000003441317517426100160330ustar00rootroot00000000000000# content of: tox.ini , put in same dir as setup.py [tox] envlist = py26, py27, py33, py34, py35, py36, pypy, pypy3 [testenv] deps = pytest requests py26: httpbin==0.5.0 commands = ./runtests.sh {posargs:tests/}