2012-07-21 03:22:47 +02:00
|
|
|
# coding=utf8
|
|
|
|
"""
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
Many of the test cases here use httpbin.org.
|
|
|
|
|
|
|
|
To make it run faster and offline you can::
|
|
|
|
|
|
|
|
# Install `httpbin` locally
|
|
|
|
pip install httpbin
|
|
|
|
|
|
|
|
# Run it
|
|
|
|
httpbin
|
|
|
|
|
|
|
|
# Run the tests against it
|
|
|
|
HTTPBIN_URL=http://localhost:5000 python setup.py test
|
|
|
|
|
|
|
|
"""
|
2012-06-24 03:43:08 +02:00
|
|
|
import os
|
|
|
|
import sys
|
2012-07-14 23:59:57 +02:00
|
|
|
import json
|
2012-07-21 13:28:59 +02:00
|
|
|
import tempfile
|
|
|
|
import unittest
|
|
|
|
import argparse
|
2012-07-14 23:59:57 +02:00
|
|
|
from requests import Response
|
2012-07-21 13:28:59 +02:00
|
|
|
from requests.compat import is_py26, is_py3
|
2012-06-24 03:43:08 +02:00
|
|
|
|
2012-03-15 00:11:49 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
#################################################################
|
|
|
|
# Utils/setup
|
|
|
|
#################################################################
|
2012-03-14 12:17:39 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
# HACK: Prepend ../ to PYTHONPATH so that we can import httpie form there.
|
2012-03-14 12:17:39 +01:00
|
|
|
TESTS_ROOT = os.path.dirname(__file__)
|
2012-03-14 19:21:47 +01:00
|
|
|
sys.path.insert(0, os.path.realpath(os.path.join(TESTS_ROOT, '..')))
|
2012-06-24 01:25:30 +02:00
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
from httpie import cliparse
|
|
|
|
from httpie.models import Environment
|
|
|
|
from httpie.core import main, get_output
|
|
|
|
|
2012-03-02 01:39:22 +01:00
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
HTTPBIN_URL = os.environ.get('HTTPBIN_URL',
|
|
|
|
'http://httpbin.org')
|
2012-03-02 01:39:22 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
TEST_FILE_PATH = os.path.join(TESTS_ROOT, 'file.txt')
|
2012-06-29 00:45:31 +02:00
|
|
|
TEST_FILE2_PATH = os.path.join(TESTS_ROOT, 'file2.txt')
|
2012-06-24 04:20:45 +02:00
|
|
|
TEST_FILE_CONTENT = open(TEST_FILE_PATH).read().strip()
|
2012-03-15 00:11:49 +01:00
|
|
|
TERMINAL_COLOR_PRESENCE_CHECK = '\x1b['
|
2012-03-04 01:36:39 +01:00
|
|
|
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
def httpbin(path):
|
|
|
|
return HTTPBIN_URL + path
|
|
|
|
|
|
|
|
|
2012-03-02 01:40:40 +01:00
|
|
|
def http(*args, **kwargs):
|
2012-06-24 03:43:08 +02:00
|
|
|
"""
|
|
|
|
Invoke `httpie.__main__.main` with `args` and `kwargs`,
|
|
|
|
and return a unicode response.
|
|
|
|
|
|
|
|
"""
|
2012-07-21 02:59:43 +02:00
|
|
|
|
|
|
|
if 'env' not in kwargs:
|
|
|
|
# Ensure that we have terminal by default
|
|
|
|
# (needed for Travis).
|
|
|
|
kwargs['env'] = Environment(
|
|
|
|
colors=0,
|
|
|
|
stdin_isatty=True,
|
|
|
|
stdout_isatty=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
stdout = kwargs['env'].stdout = tempfile.TemporaryFile()
|
|
|
|
main(args=args, **kwargs)
|
2012-04-25 00:08:40 +02:00
|
|
|
stdout.seek(0)
|
|
|
|
response = stdout.read().decode('utf8')
|
|
|
|
stdout.close()
|
|
|
|
return response
|
2012-03-02 01:40:40 +01:00
|
|
|
|
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
class BaseTestCase(unittest.TestCase):
|
2012-03-04 11:22:21 +01:00
|
|
|
|
2012-03-15 00:11:49 +01:00
|
|
|
if is_py26:
|
2012-03-04 12:02:30 +01:00
|
|
|
def assertIn(self, member, container, msg=None):
|
2012-03-04 11:22:21 +01:00
|
|
|
self.assert_(member in container, msg)
|
|
|
|
|
2012-03-04 12:02:30 +01:00
|
|
|
def assertNotIn(self, member, container, msg=None):
|
2012-03-04 11:22:21 +01:00
|
|
|
self.assert_(member not in container, msg)
|
|
|
|
|
2012-03-04 12:02:30 +01:00
|
|
|
def assertDictEqual(self, d1, d2, msg=None):
|
2012-03-04 11:58:23 +01:00
|
|
|
self.assertEqual(set(d1.keys()), set(d2.keys()), msg)
|
|
|
|
self.assertEqual(sorted(d1.values()), sorted(d2.values()), msg)
|
2012-03-04 11:22:21 +01:00
|
|
|
|
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
#################################################################
|
2012-07-21 02:59:43 +02:00
|
|
|
# High-level tests using httpbin.
|
2012-06-24 03:43:08 +02:00
|
|
|
#################################################################
|
2012-03-04 10:50:23 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
class HTTPieTest(BaseTestCase):
|
2012-03-04 10:50:23 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
def test_GET(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'GET',
|
|
|
|
httpbin('/get')
|
|
|
|
)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-03-02 01:40:40 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
def test_DELETE(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'DELETE',
|
|
|
|
httpbin('/delete')
|
|
|
|
)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-03-02 01:40:40 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
def test_PUT(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'PUT',
|
|
|
|
httpbin('/put'),
|
|
|
|
'foo=bar'
|
|
|
|
)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"foo": "bar"', r)
|
2012-03-02 01:40:40 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
def test_POST_JSON_data(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'foo=bar'
|
|
|
|
)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"foo": "bar"', r)
|
2012-03-15 00:11:49 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
def test_POST_form(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--form',
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'foo=bar'
|
|
|
|
)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"foo": "bar"', r)
|
2012-03-02 01:40:40 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
def test_POST_stdin(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
|
|
|
|
env = Environment(
|
|
|
|
stdin=open(TEST_FILE_PATH),
|
|
|
|
stdin_isatty=False,
|
|
|
|
colors=0,
|
|
|
|
)
|
|
|
|
|
|
|
|
r = http(
|
|
|
|
'--form',
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
env=env
|
|
|
|
)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn(TEST_FILE_CONTENT, r)
|
2012-06-24 03:43:08 +02:00
|
|
|
|
2012-03-02 01:40:40 +01:00
|
|
|
def test_headers(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'GET',
|
|
|
|
httpbin('/headers'),
|
|
|
|
'Foo:bar'
|
|
|
|
)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"User-Agent": "HTTPie', r)
|
|
|
|
self.assertIn('"Foo": "bar"', r)
|
2012-03-02 01:40:40 +01:00
|
|
|
|
2012-06-17 19:46:56 +02:00
|
|
|
|
2012-07-04 01:39:21 +02:00
|
|
|
class AutoContentTypeAndAcceptHeadersTest(BaseTestCase):
|
|
|
|
"""
|
|
|
|
Test that Accept and Content-Type correctly defaults to JSON,
|
|
|
|
but can still be overridden. The same with Content-Type when --form
|
|
|
|
-f is used.
|
|
|
|
|
|
|
|
"""
|
|
|
|
def test_GET_no_data_no_auto_headers(self):
|
|
|
|
# https://github.com/jkbr/httpie/issues/62
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'GET',
|
|
|
|
httpbin('/headers')
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"Accept": "*/*"', r)
|
2012-07-21 02:59:43 +02:00
|
|
|
self.assertNotIn('"Content-Type": "application/json', r)
|
2012-07-04 01:39:21 +02:00
|
|
|
|
|
|
|
def test_POST_no_data_no_auto_headers(self):
|
|
|
|
# JSON headers shouldn't be automatically set for POST with no data.
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'POST',
|
|
|
|
httpbin('/post')
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"Accept": "*/*"', r)
|
2012-07-21 02:59:43 +02:00
|
|
|
self.assertNotIn('"Content-Type": "application/json', r)
|
2012-07-04 01:39:21 +02:00
|
|
|
|
|
|
|
def test_POST_with_data_auto_JSON_headers(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'a=b'
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"Accept": "application/json"', r)
|
|
|
|
self.assertIn('"Content-Type": "application/json; charset=utf-8', r)
|
|
|
|
|
|
|
|
def test_GET_with_data_auto_JSON_headers(self):
|
|
|
|
# JSON headers should automatically be set also for GET with data.
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'a=b'
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"Accept": "application/json"', r)
|
|
|
|
self.assertIn('"Content-Type": "application/json; charset=utf-8', r)
|
|
|
|
|
|
|
|
def test_POST_explicit_JSON_auto_JSON_headers(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--json',
|
|
|
|
'POST',
|
|
|
|
httpbin('/post')
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"Accept": "application/json"', r)
|
|
|
|
self.assertIn('"Content-Type": "application/json; charset=utf-8', r)
|
|
|
|
|
|
|
|
def test_GET_explicit_JSON_explicit_headers(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--json',
|
|
|
|
'GET',
|
|
|
|
httpbin('/headers'),
|
|
|
|
'Accept:application/xml',
|
|
|
|
'Content-Type:application/xml'
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"Accept": "application/xml"', r)
|
|
|
|
self.assertIn('"Content-Type": "application/xml"', r)
|
|
|
|
|
|
|
|
def test_POST_form_auto_Content_Type(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--form',
|
|
|
|
'POST',
|
|
|
|
httpbin('/post')
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-07-17 07:01:30 +02:00
|
|
|
self.assertIn(
|
|
|
|
'"Content-Type":'
|
|
|
|
' "application/x-www-form-urlencoded; charset=utf-8"',
|
|
|
|
r
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
|
|
|
|
def test_POST_form_Content_Type_override(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--form',
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'Content-Type:application/xml'
|
|
|
|
)
|
2012-07-04 01:39:21 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"Content-Type": "application/xml"', r)
|
|
|
|
|
2012-07-20 23:43:04 +02:00
|
|
|
def test_print_only_body_when_stdout_redirected_by_default(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
|
|
|
|
r = http(
|
|
|
|
'GET',
|
2012-07-21 13:28:59 +02:00
|
|
|
httpbin('/get'),
|
2012-07-21 02:59:43 +02:00
|
|
|
env=Environment(stdout_isatty=False)
|
|
|
|
)
|
2012-07-20 23:43:04 +02:00
|
|
|
self.assertNotIn('HTTP/', r)
|
|
|
|
|
|
|
|
def test_print_overridable_when_stdout_redirected(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
|
|
|
|
r = http(
|
|
|
|
'--print=h',
|
|
|
|
'GET',
|
2012-07-21 13:28:59 +02:00
|
|
|
httpbin('/get'),
|
2012-07-21 02:59:43 +02:00
|
|
|
env=Environment(stdout_isatty=False)
|
|
|
|
)
|
2012-07-20 23:43:04 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
|
2012-07-04 01:39:21 +02:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
class ImplicitHTTPMethodTest(BaseTestCase):
|
2012-06-17 19:46:56 +02:00
|
|
|
|
2012-06-24 01:25:30 +02:00
|
|
|
def test_implicit_GET(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(httpbin('/get'))
|
2012-06-24 01:25:30 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
|
|
|
|
def test_implicit_GET_with_headers(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
httpbin('/headers'),
|
|
|
|
'Foo:bar'
|
|
|
|
)
|
2012-06-24 01:25:30 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('"Foo": "bar"', r)
|
2012-06-16 18:08:31 +02:00
|
|
|
|
2012-06-24 01:25:30 +02:00
|
|
|
def test_implicit_POST_json(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
httpbin('/post'),
|
|
|
|
'hello=world'
|
|
|
|
)
|
2012-06-24 01:25:30 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('"hello": "world"', r)
|
2012-06-17 19:46:56 +02:00
|
|
|
|
2012-06-24 01:25:30 +02:00
|
|
|
def test_implicit_POST_form(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--form',
|
|
|
|
httpbin('/post'),
|
|
|
|
'foo=bar'
|
|
|
|
)
|
2012-06-24 01:25:30 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('"foo": "bar"', r)
|
2012-06-17 19:46:56 +02:00
|
|
|
|
2012-06-24 01:25:30 +02:00
|
|
|
def test_implicit_POST_stdin(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
env = Environment(
|
|
|
|
stdin_isatty=False,
|
|
|
|
stdin=open(TEST_FILE_PATH),
|
|
|
|
colors=0,
|
|
|
|
)
|
|
|
|
r = http(
|
|
|
|
'--form',
|
|
|
|
httpbin('/post'),
|
|
|
|
env=env
|
|
|
|
)
|
2012-06-24 01:25:30 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-16 18:08:31 +02:00
|
|
|
|
2012-03-02 01:40:40 +01:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
class PrettyFlagTest(BaseTestCase):
|
2012-03-04 01:36:39 +01:00
|
|
|
"""Test the --pretty / --ugly flag handling."""
|
|
|
|
|
|
|
|
def test_pretty_enabled_by_default(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'GET',
|
|
|
|
httpbin('/get'),
|
|
|
|
env=Environment(stdout_isatty=True),
|
|
|
|
)
|
2012-03-15 00:11:49 +01:00
|
|
|
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
2012-03-04 01:36:39 +01:00
|
|
|
|
2012-07-20 23:43:04 +02:00
|
|
|
def test_pretty_enabled_by_default_unless_stdout_redirected(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'GET',
|
|
|
|
httpbin('/get')
|
|
|
|
)
|
2012-03-15 00:11:49 +01:00
|
|
|
self.assertNotIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
2012-03-04 01:36:39 +01:00
|
|
|
|
|
|
|
def test_force_pretty(self):
|
2012-07-17 07:01:30 +02:00
|
|
|
r = http(
|
|
|
|
'--pretty',
|
|
|
|
'GET',
|
2012-07-21 02:59:43 +02:00
|
|
|
httpbin('/get'),
|
|
|
|
env=Environment(stdout_isatty=False),
|
2012-07-17 07:01:30 +02:00
|
|
|
)
|
2012-03-15 00:11:49 +01:00
|
|
|
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
2012-03-04 01:36:39 +01:00
|
|
|
|
|
|
|
def test_force_ugly(self):
|
2012-07-17 07:01:30 +02:00
|
|
|
r = http(
|
|
|
|
'--ugly',
|
|
|
|
'GET',
|
2012-07-21 02:59:43 +02:00
|
|
|
httpbin('/get'),
|
2012-07-17 07:01:30 +02:00
|
|
|
)
|
2012-03-15 00:11:49 +01:00
|
|
|
self.assertNotIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
2012-03-04 01:36:39 +01:00
|
|
|
|
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
class VerboseFlagTest(BaseTestCase):
|
|
|
|
|
|
|
|
def test_verbose(self):
|
2012-07-17 07:01:30 +02:00
|
|
|
r = http(
|
|
|
|
'--verbose',
|
|
|
|
'GET',
|
2012-07-21 02:59:43 +02:00
|
|
|
httpbin('/get'),
|
2012-07-17 07:01:30 +02:00
|
|
|
'test-header:__test__'
|
|
|
|
)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertEqual(r.count('__test__'), 2)
|
|
|
|
|
|
|
|
def test_verbose_form(self):
|
|
|
|
# https://github.com/jkbr/httpie/issues/53
|
2012-07-17 07:01:30 +02:00
|
|
|
r = http(
|
|
|
|
'--verbose',
|
|
|
|
'--form',
|
|
|
|
'POST',
|
2012-07-21 02:59:43 +02:00
|
|
|
httpbin('/post'),
|
2012-07-17 07:01:30 +02:00
|
|
|
'foo=bar',
|
|
|
|
'baz=bar'
|
|
|
|
)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('foo=bar&baz=bar', r)
|
|
|
|
|
|
|
|
|
|
|
|
class MultipartFormDataFileUploadTest(BaseTestCase):
|
2012-03-14 12:17:39 +01:00
|
|
|
|
|
|
|
def test_non_existent_file_raises_parse_error(self):
|
2012-04-25 01:32:53 +02:00
|
|
|
self.assertRaises(cliparse.ParseError, http,
|
2012-07-21 02:59:43 +02:00
|
|
|
'--form',
|
|
|
|
'--traceback',
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'foo@/__does_not_exist__'
|
|
|
|
)
|
2012-03-14 12:17:39 +01:00
|
|
|
|
|
|
|
def test_upload_ok(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--form',
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'test-file@%s' % TEST_FILE_PATH,
|
|
|
|
'foo=bar'
|
|
|
|
)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-06-29 00:45:31 +02:00
|
|
|
self.assertIn('"test-file": "%s' % TEST_FILE_CONTENT, r)
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('"foo": "bar"', r)
|
2012-03-14 12:17:39 +01:00
|
|
|
|
|
|
|
|
2012-06-29 00:45:31 +02:00
|
|
|
class RequestBodyFromFilePathTest(BaseTestCase):
|
|
|
|
"""
|
|
|
|
`http URL @file'
|
|
|
|
|
|
|
|
"""
|
|
|
|
def test_request_body_from_file_by_path(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'POST',
|
|
|
|
httpbin('/post'),
|
|
|
|
'@' + TEST_FILE_PATH
|
|
|
|
)
|
2012-06-29 00:45:31 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn(TEST_FILE_CONTENT, r)
|
|
|
|
self.assertIn('"Content-Type": "text/plain"', r)
|
|
|
|
|
|
|
|
def test_request_body_from_file_by_path_with_explicit_content_type(self):
|
2012-07-17 07:01:30 +02:00
|
|
|
r = http(
|
|
|
|
'POST',
|
2012-07-21 02:59:43 +02:00
|
|
|
httpbin('/post'),
|
2012-07-17 07:01:30 +02:00
|
|
|
'@' + TEST_FILE_PATH,
|
|
|
|
'Content-Type:x-foo/bar'
|
|
|
|
)
|
2012-06-29 00:45:31 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn(TEST_FILE_CONTENT, r)
|
|
|
|
self.assertIn('"Content-Type": "x-foo/bar"', r)
|
|
|
|
|
|
|
|
def test_request_body_from_file_by_path_only_one_file_allowed(self):
|
|
|
|
self.assertRaises(SystemExit, lambda: http(
|
|
|
|
'POST',
|
2012-07-21 02:59:43 +02:00
|
|
|
httpbin('/post'),
|
|
|
|
'@' + TEST_FILE_PATH,
|
|
|
|
'@' + TEST_FILE2_PATH)
|
|
|
|
)
|
2012-06-29 00:45:31 +02:00
|
|
|
|
2012-07-04 01:39:21 +02:00
|
|
|
def test_request_body_from_file_by_path_no_data_items_allowed(self):
|
2012-06-29 00:45:31 +02:00
|
|
|
self.assertRaises(SystemExit, lambda: http(
|
|
|
|
'POST',
|
2012-07-21 02:59:43 +02:00
|
|
|
httpbin('/post'),
|
|
|
|
'@' + TEST_FILE_PATH,
|
|
|
|
'foo=bar')
|
|
|
|
)
|
2012-06-29 00:45:31 +02:00
|
|
|
|
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
class AuthTest(BaseTestCase):
|
2012-04-11 13:47:47 +02:00
|
|
|
|
|
|
|
def test_basic_auth(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--auth',
|
|
|
|
'user:password',
|
|
|
|
'GET',
|
2012-07-21 13:28:59 +02:00
|
|
|
httpbin('/basic-auth/user/password')
|
2012-07-21 02:59:43 +02:00
|
|
|
)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-04-11 13:47:47 +02:00
|
|
|
self.assertIn('"authenticated": true', r)
|
|
|
|
self.assertIn('"user": "user"', r)
|
|
|
|
|
|
|
|
def test_digest_auth(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--auth-type=digest',
|
|
|
|
'--auth',
|
|
|
|
'user:password',
|
|
|
|
'GET',
|
2012-07-21 13:28:59 +02:00
|
|
|
httpbin('/digest-auth/auth/user/password')
|
2012-07-21 02:59:43 +02:00
|
|
|
)
|
2012-06-24 04:20:45 +02:00
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
2012-04-11 13:47:47 +02:00
|
|
|
self.assertIn('"authenticated": true', r)
|
|
|
|
self.assertIn('"user": "user"', r)
|
|
|
|
|
2012-07-16 23:41:27 +02:00
|
|
|
def test_password_prompt(self):
|
2012-07-21 02:59:43 +02:00
|
|
|
|
2012-07-16 23:41:27 +02:00
|
|
|
cliparse.AuthCredentials._getpass = lambda self, prompt: 'password'
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
r = http(
|
|
|
|
'--auth',
|
|
|
|
'user',
|
|
|
|
'GET',
|
2012-07-21 13:28:59 +02:00
|
|
|
httpbin('/basic-auth/user/password')
|
2012-07-21 02:59:43 +02:00
|
|
|
)
|
2012-07-16 23:41:27 +02:00
|
|
|
|
|
|
|
self.assertIn('HTTP/1.1 200', r)
|
|
|
|
self.assertIn('"authenticated": true', r)
|
|
|
|
self.assertIn('"user": "user"', r)
|
|
|
|
|
2012-04-11 13:47:47 +02:00
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
#################################################################
|
|
|
|
# CLI argument parsing related tests.
|
|
|
|
#################################################################
|
|
|
|
|
|
|
|
class ItemParsingTest(BaseTestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.key_value_type = cliparse.KeyValueType(
|
|
|
|
cliparse.SEP_HEADERS,
|
2012-07-19 04:16:08 +02:00
|
|
|
cliparse.SEP_QUERY,
|
2012-06-24 03:43:08 +02:00
|
|
|
cliparse.SEP_DATA,
|
|
|
|
cliparse.SEP_DATA_RAW_JSON,
|
|
|
|
cliparse.SEP_FILES,
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_invalid_items(self):
|
|
|
|
items = ['no-separator']
|
|
|
|
for item in items:
|
|
|
|
self.assertRaises(argparse.ArgumentTypeError,
|
|
|
|
lambda: self.key_value_type(item))
|
|
|
|
|
|
|
|
def test_escape(self):
|
2012-07-19 04:16:08 +02:00
|
|
|
headers, data, files, queries = cliparse.parse_items([
|
2012-06-24 03:43:08 +02:00
|
|
|
# headers
|
|
|
|
self.key_value_type('foo\\:bar:baz'),
|
|
|
|
self.key_value_type('jack\\@jill:hill'),
|
|
|
|
# data
|
|
|
|
self.key_value_type('baz\\=bar=foo'),
|
|
|
|
# files
|
|
|
|
self.key_value_type('bar\\@baz@%s' % TEST_FILE_PATH)
|
|
|
|
])
|
|
|
|
self.assertDictEqual(headers, {
|
|
|
|
'foo:bar': 'baz',
|
|
|
|
'jack@jill': 'hill',
|
|
|
|
})
|
|
|
|
self.assertDictEqual(data, {
|
|
|
|
'baz=bar': 'foo',
|
|
|
|
})
|
|
|
|
self.assertIn('bar@baz', files)
|
|
|
|
|
|
|
|
def test_escape_longsep(self):
|
2012-07-19 04:16:08 +02:00
|
|
|
headers, data, files, queries = cliparse.parse_items([
|
2012-06-24 03:43:08 +02:00
|
|
|
self.key_value_type('bob\\:==foo'),
|
|
|
|
])
|
|
|
|
self.assertDictEqual(data, {
|
|
|
|
'bob:=': 'foo',
|
|
|
|
})
|
|
|
|
|
|
|
|
def test_valid_items(self):
|
2012-07-19 04:16:08 +02:00
|
|
|
headers, data, files, queries = cliparse.parse_items([
|
2012-06-24 03:43:08 +02:00
|
|
|
self.key_value_type('string=value'),
|
|
|
|
self.key_value_type('header:value'),
|
|
|
|
self.key_value_type('list:=["a", 1, {}, false]'),
|
|
|
|
self.key_value_type('obj:={"a": "b"}'),
|
|
|
|
self.key_value_type('eh:'),
|
|
|
|
self.key_value_type('ed='),
|
|
|
|
self.key_value_type('bool:=true'),
|
|
|
|
self.key_value_type('test-file@%s' % TEST_FILE_PATH),
|
2012-07-19 04:16:08 +02:00
|
|
|
self.key_value_type('query=:value'),
|
2012-06-24 03:43:08 +02:00
|
|
|
])
|
|
|
|
self.assertDictEqual(headers, {
|
|
|
|
'header': 'value',
|
|
|
|
'eh': ''
|
|
|
|
})
|
|
|
|
self.assertDictEqual(data, {
|
|
|
|
"ed": "",
|
|
|
|
"string": "value",
|
|
|
|
"bool": True,
|
|
|
|
"list": ["a", 1, {}, False],
|
|
|
|
"obj": {"a": "b"}
|
|
|
|
})
|
2012-07-19 04:16:08 +02:00
|
|
|
self.assertDictEqual(queries, {
|
|
|
|
'query': 'value',
|
|
|
|
})
|
2012-06-24 03:43:08 +02:00
|
|
|
self.assertIn('test-file', files)
|
|
|
|
|
2012-07-19 04:16:08 +02:00
|
|
|
def test_query_string(self):
|
|
|
|
headers, data, files, queries = cliparse.parse_items([
|
|
|
|
self.key_value_type('query=:value'),
|
|
|
|
])
|
|
|
|
self.assertDictEqual(queries, {
|
|
|
|
'query': 'value',
|
|
|
|
})
|
|
|
|
|
2012-06-24 03:43:08 +02:00
|
|
|
|
2012-06-29 00:45:31 +02:00
|
|
|
class ArgumentParserTestCase(unittest.TestCase):
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.parser = cliparse.Parser()
|
|
|
|
|
|
|
|
def test_guess_when_method_set_and_valid(self):
|
|
|
|
args = argparse.Namespace()
|
|
|
|
args.method = 'GET'
|
|
|
|
args.url = 'http://example.com/'
|
|
|
|
args.items = []
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
self.parser._guess_method(args, Environment())
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
self.assertEquals(args.method, 'GET')
|
|
|
|
self.assertEquals(args.url, 'http://example.com/')
|
|
|
|
self.assertEquals(args.items, [])
|
|
|
|
|
|
|
|
def test_guess_when_method_not_set(self):
|
|
|
|
args = argparse.Namespace()
|
|
|
|
args.method = None
|
|
|
|
args.url = 'http://example.com/'
|
|
|
|
args.items = []
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
self.parser._guess_method(args, Environment())
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
self.assertEquals(args.method, 'GET')
|
|
|
|
self.assertEquals(args.url, 'http://example.com/')
|
|
|
|
self.assertEquals(args.items, [])
|
|
|
|
|
|
|
|
def test_guess_when_method_set_but_invalid_and_data_field(self):
|
|
|
|
args = argparse.Namespace()
|
|
|
|
args.method = 'http://example.com/'
|
|
|
|
args.url = 'data=field'
|
|
|
|
args.items = []
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
self.parser._guess_method(args, Environment())
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
self.assertEquals(args.method, 'POST')
|
|
|
|
self.assertEquals(args.url, 'http://example.com/')
|
|
|
|
self.assertEquals(
|
|
|
|
args.items,
|
2012-07-17 07:01:30 +02:00
|
|
|
[cliparse.KeyValue(
|
|
|
|
key='data', value='field', sep='=', orig='data=field')])
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
def test_guess_when_method_set_but_invalid_and_header_field(self):
|
|
|
|
args = argparse.Namespace()
|
|
|
|
args.method = 'http://example.com/'
|
|
|
|
args.url = 'test:header'
|
|
|
|
args.items = []
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
self.parser._guess_method(args, Environment())
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
self.assertEquals(args.method, 'GET')
|
|
|
|
self.assertEquals(args.url, 'http://example.com/')
|
|
|
|
self.assertEquals(
|
|
|
|
args.items,
|
2012-07-17 07:01:30 +02:00
|
|
|
[cliparse.KeyValue(
|
|
|
|
key='test', value='header', sep=':', orig='test:header')])
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
def test_guess_when_method_set_but_invalid_and_item_exists(self):
|
|
|
|
args = argparse.Namespace()
|
|
|
|
args.method = 'http://example.com/'
|
|
|
|
args.url = 'new_item=a'
|
|
|
|
args.items = [
|
2012-07-17 07:01:30 +02:00
|
|
|
cliparse.KeyValue(
|
|
|
|
key='old_item', value='b', sep='=', orig='old_item=b')
|
2012-06-24 03:43:08 +02:00
|
|
|
]
|
|
|
|
|
2012-07-21 02:59:43 +02:00
|
|
|
self.parser._guess_method(args, Environment())
|
2012-06-24 03:43:08 +02:00
|
|
|
|
|
|
|
self.assertEquals(args.items, [
|
2012-07-17 07:01:30 +02:00
|
|
|
cliparse.KeyValue(
|
|
|
|
key='new_item', value='a', sep='=', orig='new_item=a'),
|
|
|
|
cliparse.KeyValue(key
|
|
|
|
='old_item', value='b', sep='=', orig='old_item=b'),
|
2012-06-24 03:43:08 +02:00
|
|
|
])
|
|
|
|
|
|
|
|
|
2012-07-14 23:59:57 +02:00
|
|
|
class FakeResponse(Response):
|
|
|
|
|
|
|
|
class Mock(object):
|
|
|
|
|
|
|
|
def __getattr__(self, item):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __repr__(self):
|
2012-07-17 01:26:21 +02:00
|
|
|
return 'Mock string'
|
2012-07-14 23:59:57 +02:00
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return self.__repr__()
|
|
|
|
|
|
|
|
def __init__(self, content=None, encoding='utf-8'):
|
|
|
|
super(FakeResponse, self).__init__()
|
|
|
|
self.headers['Content-Type'] = 'application/json'
|
|
|
|
self.encoding = encoding
|
|
|
|
self._content = content.encode(encoding)
|
|
|
|
self.raw = self.Mock()
|
|
|
|
|
|
|
|
|
|
|
|
class UnicodeOutputTestCase(BaseTestCase):
|
|
|
|
|
|
|
|
def test_unicode_output(self):
|
|
|
|
# some cyrillic and simplified chinese symbols
|
2012-07-17 01:26:21 +02:00
|
|
|
response_dict = {'Привет': 'Мир!',
|
|
|
|
'Hello': '世界'}
|
|
|
|
if not is_py3:
|
|
|
|
response_dict = dict(
|
|
|
|
(k.decode('utf8'), v.decode('utf8'))
|
|
|
|
for k, v in response_dict.items()
|
|
|
|
)
|
2012-07-14 23:59:57 +02:00
|
|
|
response_body = json.dumps(response_dict)
|
|
|
|
# emulate response
|
|
|
|
response = FakeResponse(response_body)
|
|
|
|
|
|
|
|
# emulate cli arguments
|
|
|
|
args = argparse.Namespace()
|
|
|
|
args.prettify = True
|
|
|
|
args.output_options = 'b'
|
|
|
|
args.forced_content_type = None
|
|
|
|
args.style = 'default'
|
|
|
|
|
|
|
|
# colorized output contains escape sequences
|
2012-07-21 02:59:43 +02:00
|
|
|
output = get_output(args, Environment(), response)
|
2012-07-14 23:59:57 +02:00
|
|
|
|
2012-07-17 01:26:21 +02:00
|
|
|
for key, value in response_dict.items():
|
2012-07-14 23:59:57 +02:00
|
|
|
self.assertIn(key, output)
|
|
|
|
self.assertIn(value, output)
|
|
|
|
|
|
|
|
|
2012-03-02 01:40:40 +01:00
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|