"Fossies" - the Fresh Open Source Software Archive

Member "http-prompt-2.1.0/tests/test_execution.py" (5 Mar 2021, 56325 Bytes) of package /linux/www/http-prompt-2.1.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_execution.py": 1.0.0_vs_2.1.0.

    1 # -*- coding: utf-8 -*-
    2 from __future__ import unicode_literals
    3 
    4 import hashlib
    5 import io
    6 import json
    7 import shutil
    8 import os
    9 import sys
   10 
   11 import pytest
   12 import six
   13 
   14 from collections import namedtuple
   15 
   16 from mock import patch
   17 
   18 from http_prompt.context import Context
   19 from http_prompt.execution import execute, HTTPIE_PROGRAM_NAME
   20 
   21 from .base import TempAppDirTestCase
   22 
   23 
   24 class ExecutionTestCase(TempAppDirTestCase):
   25 
   26     def setUp(self):
   27         super(ExecutionTestCase, self).setUp()
   28         self.patchers = [
   29             ('httpie_main', patch('http_prompt.execution.httpie_main')),
   30             ('echo_via_pager',
   31              patch('http_prompt.output.click.echo_via_pager')),
   32             ('secho', patch('http_prompt.execution.click.secho')),
   33             ('get_terminal_size', patch('http_prompt.utils.get_terminal_size'))
   34         ]
   35         for attr_name, patcher in self.patchers:
   36             setattr(self, attr_name, patcher.start())
   37 
   38         self.context = Context('http://localhost', spec={
   39             'paths': {
   40                 '/users': {},
   41                 '/users/{username}': {},
   42                 '/users/{username}/events': {},
   43                 '/users/{username}/orgs': {},
   44                 '/orgs': {},
   45                 '/orgs/{org}': {},
   46                 '/orgs/{org}/events': {},
   47                 '/orgs/{org}/members': {}
   48             }
   49         })
   50 
   51         # pytest mocks to capture stdout so we can't really get_terminal_size()
   52         Size = namedtuple('Size', ['columns', 'rows'])
   53         self.get_terminal_size.return_value = Size(80, 30)
   54 
   55     def tearDown(self):
   56         super(ExecutionTestCase, self).tearDown()
   57         for _, patcher in self.patchers:
   58             patcher.stop()
   59 
   60     def assert_httpie_main_called_with(self, args):
   61         self.assertEqual(self.httpie_main.call_args[0][0], [HTTPIE_PROGRAM_NAME, *args])
   62 
   63     def assert_stdout(self, expected_msg):
   64         # Append '\n' to simulate behavior of click.echo_via_pager(),
   65         # which we use whenever we want to output anything to stdout
   66         printed_msg = self.echo_via_pager.call_args[0][0] + '\n'
   67         self.assertEqual(printed_msg, expected_msg)
   68 
   69     def assert_stdout_startswith(self, expected_prefix):
   70         printed_msg = self.echo_via_pager.call_args[0][0]
   71         self.assertTrue(printed_msg.startswith(expected_prefix))
   72 
   73     def get_stdout(self):
   74         return self.echo_via_pager.call_args[0][0]
   75 
   76     def assert_stderr(self, expected_msg):
   77         printed_msg = self.secho.call_args[0][0]
   78         print_options = self.secho.call_args[1]
   79         self.assertEqual(printed_msg, expected_msg)
   80         self.assertEqual(print_options, {'err': True, 'fg': 'red'})
   81 
   82 
   83 class TestExecution_noop(ExecutionTestCase):
   84 
   85     def test_empty_string(self):
   86         execute('', self.context)
   87         self.assertEqual(self.context.url, 'http://localhost')
   88         self.assertFalse(self.context.options)
   89         self.assertFalse(self.context.headers)
   90         self.assertFalse(self.context.querystring_params)
   91         self.assertFalse(self.context.body_params)
   92         self.assertFalse(self.context.should_exit)
   93 
   94     def test_spaces(self):
   95         execute('  \t \t  ', self.context)
   96         self.assertEqual(self.context.url, 'http://localhost')
   97         self.assertFalse(self.context.options)
   98         self.assertFalse(self.context.headers)
   99         self.assertFalse(self.context.querystring_params)
  100         self.assertFalse(self.context.body_params)
  101         self.assertFalse(self.context.should_exit)
  102 
  103 
  104 class TestExecution_env(ExecutionTestCase):
  105 
  106     def setUp(self):
  107         super(TestExecution_env, self).setUp()
  108 
  109         self.context.url = 'http://localhost:8000/api'
  110         self.context.headers.update({
  111             'Accept': 'text/csv',
  112             'Authorization': 'ApiKey 1234'
  113         })
  114         self.context.querystring_params.update({
  115             'page': ['1'],
  116             'limit': ['50']
  117         })
  118         self.context.body_params.update({
  119             'name': 'John Doe'
  120         })
  121         self.context.options.update({
  122             '--verify': 'no',
  123             '--form': None
  124         })
  125 
  126     def test_env(self):
  127         execute('env', self.context)
  128         self.assert_stdout("--form\n--verify=no\n"
  129                            "cd http://localhost:8000/api\n"
  130                            "limit==50\npage==1\n"
  131                            "'name=John Doe'\n"
  132                            "Accept:text/csv\n"
  133                            "'Authorization:ApiKey 1234'\n")
  134 
  135     def test_env_with_spaces(self):
  136         execute('  env   ', self.context)
  137         self.assert_stdout("--form\n--verify=no\n"
  138                            "cd http://localhost:8000/api\n"
  139                            "limit==50\npage==1\n"
  140                            "'name=John Doe'\n"
  141                            "Accept:text/csv\n"
  142                            "'Authorization:ApiKey 1234'\n")
  143 
  144     def test_env_non_ascii(self):
  145         self.context.body_params['name'] = '許 功蓋'
  146         execute('env', self.context)
  147         self.assert_stdout("--form\n--verify=no\n"
  148                            "cd http://localhost:8000/api\n"
  149                            "limit==50\npage==1\n"
  150                            "'name=許 功蓋'\n"
  151                            "Accept:text/csv\n"
  152                            "'Authorization:ApiKey 1234'\n")
  153 
  154     def test_env_write_to_file(self):
  155         filename = self.make_tempfile()
  156 
  157         # write something first to make sure it's a full overwrite
  158         with open(filename, 'w') as f:
  159             f.write('hello world\n')
  160 
  161         execute('env > %s' % filename, self.context)
  162 
  163         with open(filename) as f:
  164             content = f.read()
  165 
  166         self.assertEqual(content,
  167                          "--form\n--verify=no\n"
  168                          "cd http://localhost:8000/api\n"
  169                          "limit==50\npage==1\n"
  170                          "'name=John Doe'\n"
  171                          "Accept:text/csv\n"
  172                          "'Authorization:ApiKey 1234'\n")
  173 
  174     def test_env_write_to_file_with_env_vars(self):
  175         filename = self.make_tempfile('hello world\n', 'testenvvar')
  176         filename_with_var = filename.replace("testenvvar", "${MYPRIVATEVAR}")
  177 
  178         os.environ['MYPRIVATEVAR'] = 'testenvvar'
  179         execute('env > %s' % filename_with_var, self.context)
  180         os.environ['MYPRIVATEVAR'] = ''
  181 
  182         with open(filename) as f:
  183             content = f.read()
  184 
  185         self.assertEqual(content,
  186                          "--form\n--verify=no\n"
  187                          "cd http://localhost:8000/api\n"
  188                          "limit==50\npage==1\n"
  189                          "'name=John Doe'\n"
  190                          "Accept:text/csv\n"
  191                          "'Authorization:ApiKey 1234'\n")
  192 
  193 
  194     def test_env_non_ascii_and_write_to_file(self):
  195         filename = self.make_tempfile()
  196 
  197         # write something first to make sure it's a full overwrite
  198         with open(filename, 'w') as f:
  199             f.write('hello world\n')
  200 
  201         self.context.body_params['name'] = '許 功蓋'
  202         execute('env > %s' % filename, self.context)
  203 
  204         with io.open(filename, encoding='utf-8') as f:
  205             content = f.read()
  206 
  207         self.assertEqual(content,
  208                          "--form\n--verify=no\n"
  209                          "cd http://localhost:8000/api\n"
  210                          "limit==50\npage==1\n"
  211                          "'name=許 功蓋'\n"
  212                          "Accept:text/csv\n"
  213                          "'Authorization:ApiKey 1234'\n")
  214 
  215     def test_env_write_to_quoted_filename(self):
  216         filename = self.make_tempfile()
  217 
  218         # Write something first to make sure it's a full overwrite
  219         with open(filename, 'w') as f:
  220             f.write('hello world\n')
  221 
  222         execute("env > '%s'" % filename, self.context)
  223 
  224         with open(filename) as f:
  225             content = f.read()
  226 
  227         self.assertEqual(content,
  228                          "--form\n--verify=no\n"
  229                          "cd http://localhost:8000/api\n"
  230                          "limit==50\npage==1\n"
  231                          "'name=John Doe'\n"
  232                          "Accept:text/csv\n"
  233                          "'Authorization:ApiKey 1234'\n")
  234 
  235     def test_env_append_to_file(self):
  236         filename = self.make_tempfile()
  237 
  238         # Write something first to make sure it's an append
  239         with open(filename, 'w') as f:
  240             f.write('hello world\n')
  241 
  242         execute('env >> %s' % filename, self.context)
  243 
  244         with open(filename) as f:
  245             content = f.read()
  246 
  247         self.assertEqual(content,
  248                          "hello world\n"
  249                          "--form\n--verify=no\n"
  250                          "cd http://localhost:8000/api\n"
  251                          "limit==50\npage==1\n"
  252                          "'name=John Doe'\n"
  253                          "Accept:text/csv\n"
  254                          "'Authorization:ApiKey 1234'\n")
  255 
  256 
  257 class TestExecution_source_and_exec(ExecutionTestCase):
  258 
  259     def setUp(self):
  260         super(TestExecution_source_and_exec, self).setUp()
  261 
  262         self.context.url = 'http://localhost:8000/api'
  263         self.context.headers.update({
  264             'Accept': 'text/csv',
  265             'Authorization': 'ApiKey 1234'
  266         })
  267         self.context.querystring_params.update({
  268             'page': ['1'],
  269             'limit': ['50']
  270         })
  271         self.context.body_params.update({
  272             'name': 'John Doe'
  273         })
  274         self.context.options.update({
  275             '--verify': 'no',
  276             '--form': None
  277         })
  278 
  279         # The file that is about to be sourced/exec'd
  280         self.filename = self.make_tempfile(
  281             "Language:en Authorization:'ApiKey 5678'\n"
  282             "name='Jane Doe'  username=jane   limit==25\n"
  283             "rm -o --form\n"
  284             "cd v2/user\n")
  285 
  286     def test_source(self):
  287         execute('source %s' % self.filename, self.context)
  288 
  289         self.assertEqual(self.context.url,
  290                          'http://localhost:8000/api/v2/user')
  291         self.assertEqual(self.context.headers, {
  292             'Accept': 'text/csv',
  293             'Authorization': 'ApiKey 5678',
  294             'Language': 'en'
  295         })
  296         self.assertEqual(self.context.querystring_params, {
  297             'page': ['1'],
  298             'limit': ['25']
  299         })
  300         self.assertEqual(self.context.body_params, {
  301             'name': 'Jane Doe',
  302             'username': 'jane'
  303         })
  304         self.assertEqual(self.context.options, {
  305             '--verify': 'no'
  306         })
  307 
  308     def test_source_with_spaces(self):
  309         execute(' source       %s   ' % self.filename, self.context)
  310 
  311         self.assertEqual(self.context.url,
  312                          'http://localhost:8000/api/v2/user')
  313         self.assertEqual(self.context.headers, {
  314             'Accept': 'text/csv',
  315             'Authorization': 'ApiKey 5678',
  316             'Language': 'en'
  317         })
  318         self.assertEqual(self.context.querystring_params, {
  319             'page': ['1'],
  320             'limit': ['25']
  321         })
  322         self.assertEqual(self.context.body_params, {
  323             'name': 'Jane Doe',
  324             'username': 'jane'
  325         })
  326         self.assertEqual(self.context.options, {
  327             '--verify': 'no'
  328         })
  329 
  330     def test_source_non_existing_file(self):
  331         c = self.context.copy()
  332         execute('source no_such_file.txt', self.context)
  333         self.assertEqual(self.context, c)
  334 
  335         # Expect the error message would be the same as when we open the
  336         # non-existing file
  337         try:
  338             with io.open('no_such_file.txt'):
  339                 pass
  340         except IOError as err:
  341             err_msg = str(err)
  342         else:
  343             assert False, 'what?! no_such_file.txt exists!'
  344 
  345         self.assert_stderr(err_msg)
  346 
  347     def test_source_quoted_filename(self):
  348         execute('source "%s"' % self.filename, self.context)
  349 
  350         self.assertEqual(self.context.url,
  351                          'http://localhost:8000/api/v2/user')
  352         self.assertEqual(self.context.headers, {
  353             'Accept': 'text/csv',
  354             'Authorization': 'ApiKey 5678',
  355             'Language': 'en'
  356         })
  357         self.assertEqual(self.context.querystring_params, {
  358             'page': ['1'],
  359             'limit': ['25']
  360         })
  361         self.assertEqual(self.context.body_params, {
  362             'name': 'Jane Doe',
  363             'username': 'jane'
  364         })
  365         self.assertEqual(self.context.options, {
  366             '--verify': 'no'
  367         })
  368 
  369     @pytest.mark.skipif(sys.platform == 'win32',
  370                         reason="Windows doesn't use backslashes to escape")
  371     def test_source_escaped_filename(self):
  372         new_filename = self.filename + r' copy'
  373         shutil.copyfile(self.filename, new_filename)
  374 
  375         new_filename = new_filename.replace(' ', r'\ ')
  376 
  377         execute('source %s' % new_filename, self.context)
  378 
  379         self.assertEqual(self.context.url,
  380                          'http://localhost:8000/api/v2/user')
  381         self.assertEqual(self.context.headers, {
  382             'Accept': 'text/csv',
  383             'Authorization': 'ApiKey 5678',
  384             'Language': 'en'
  385         })
  386         self.assertEqual(self.context.querystring_params, {
  387             'page': ['1'],
  388             'limit': ['25']
  389         })
  390         self.assertEqual(self.context.body_params, {
  391             'name': 'Jane Doe',
  392             'username': 'jane'
  393         })
  394         self.assertEqual(self.context.options, {
  395             '--verify': 'no'
  396         })
  397 
  398     def test_exec(self):
  399         execute('exec %s' % self.filename, self.context)
  400 
  401         self.assertEqual(self.context.url,
  402                          'http://localhost:8000/api/v2/user')
  403         self.assertEqual(self.context.headers, {
  404             'Authorization': 'ApiKey 5678',
  405             'Language': 'en'
  406         })
  407         self.assertEqual(self.context.querystring_params, {
  408             'limit': ['25']
  409         })
  410         self.assertEqual(self.context.body_params, {
  411             'name': 'Jane Doe',
  412             'username': 'jane'
  413         })
  414 
  415     def test_exec_with_spaces(self):
  416         execute('  exec    %s   ' % self.filename, self.context)
  417 
  418         self.assertEqual(self.context.url,
  419                          'http://localhost:8000/api/v2/user')
  420         self.assertEqual(self.context.headers, {
  421             'Authorization': 'ApiKey 5678',
  422             'Language': 'en'
  423         })
  424         self.assertEqual(self.context.querystring_params, {
  425             'limit': ['25']
  426         })
  427         self.assertEqual(self.context.body_params, {
  428             'name': 'Jane Doe',
  429             'username': 'jane'
  430         })
  431 
  432     def test_exec_non_existing_file(self):
  433         c = self.context.copy()
  434         execute('exec no_such_file.txt', self.context)
  435         self.assertEqual(self.context, c)
  436 
  437         # Try to get the error message when opening a non-existing file
  438         try:
  439             with io.open('no_such_file.txt'):
  440                 pass
  441         except IOError as err:
  442             err_msg = str(err)
  443         else:
  444             assert False, 'what?! no_such_file.txt exists!'
  445 
  446         self.assert_stderr(err_msg)
  447 
  448     def test_exec_quoted_filename(self):
  449         execute("exec '%s'" % self.filename, self.context)
  450 
  451         self.assertEqual(self.context.url,
  452                          'http://localhost:8000/api/v2/user')
  453         self.assertEqual(self.context.headers, {
  454             'Authorization': 'ApiKey 5678',
  455             'Language': 'en'
  456         })
  457         self.assertEqual(self.context.querystring_params, {
  458             'limit': ['25']
  459         })
  460         self.assertEqual(self.context.body_params, {
  461             'name': 'Jane Doe',
  462             'username': 'jane'
  463         })
  464 
  465     @pytest.mark.skipif(sys.platform == 'win32',
  466                         reason="Windows doesn't use backslashes to escape")
  467     def test_exec_escaped_filename(self):
  468         new_filename = self.filename + r' copy'
  469         shutil.copyfile(self.filename, new_filename)
  470 
  471         new_filename = new_filename.replace(' ', r'\ ')
  472 
  473         execute('exec %s' % new_filename, self.context)
  474         self.assertEqual(self.context.url,
  475                          'http://localhost:8000/api/v2/user')
  476         self.assertEqual(self.context.headers, {
  477             'Authorization': 'ApiKey 5678',
  478             'Language': 'en'
  479         })
  480         self.assertEqual(self.context.querystring_params, {
  481             'limit': ['25']
  482         })
  483         self.assertEqual(self.context.body_params, {
  484             'name': 'Jane Doe',
  485             'username': 'jane'
  486         })
  487 
  488 
  489 class TestExecution_env_and_source(ExecutionTestCase):
  490 
  491     def test_env_and_source(self):
  492         c = Context()
  493         c.url = 'http://localhost:8000/api'
  494         c.headers.update({
  495             'Accept': 'text/csv',
  496             'Authorization': 'ApiKey 1234'
  497         })
  498         c.querystring_params.update({
  499             'page': ['1'],
  500             'limit': ['50']
  501         })
  502         c.body_params.update({
  503             'name': 'John Doe'
  504         })
  505         c.options.update({
  506             '--verify': 'no',
  507             '--form': None
  508         })
  509 
  510         c2 = c.copy()
  511 
  512         filename = self.make_tempfile()
  513         execute('env > %s' % filename, c)
  514         execute('rm *', c)
  515 
  516         self.assertFalse(c.headers)
  517         self.assertFalse(c.querystring_params)
  518         self.assertFalse(c.body_params)
  519         self.assertFalse(c.options)
  520 
  521         execute('source %s' % filename, c)
  522 
  523         self.assertEqual(c, c2)
  524 
  525     def test_env_and_source_non_ascii(self):
  526         c = Context()
  527         c.url = 'http://localhost:8000/api'
  528         c.headers.update({
  529             'Accept': 'text/csv',
  530             'Authorization': 'ApiKey 1234'
  531         })
  532         c.querystring_params.update({
  533             'page': ['1'],
  534             'limit': ['50']
  535         })
  536         c.body_params.update({
  537             'name': '許 功蓋'
  538         })
  539         c.options.update({
  540             '--verify': 'no',
  541             '--form': None
  542         })
  543 
  544         c2 = c.copy()
  545 
  546         filename = self.make_tempfile()
  547         execute('env > %s' % filename, c)
  548         execute('rm *', c)
  549 
  550         self.assertFalse(c.headers)
  551         self.assertFalse(c.querystring_params)
  552         self.assertFalse(c.body_params)
  553         self.assertFalse(c.options)
  554 
  555         execute('source %s' % filename, c)
  556 
  557         self.assertEqual(c, c2)
  558 
  559 
  560 class TestExecution_help(ExecutionTestCase):
  561 
  562     def test_help(self):
  563         execute('help', self.context)
  564         self.assert_stdout_startswith('Commands:\n\tcd')
  565 
  566     def test_help_with_spaces(self):
  567         execute('  help   ', self.context)
  568         self.assert_stdout_startswith('Commands:\n\tcd')
  569 
  570 
  571 class TestExecution_exit(ExecutionTestCase):
  572 
  573     def test_exit(self):
  574         execute('exit', self.context)
  575         self.assertTrue(self.context.should_exit)
  576 
  577     def test_exit_with_spaces(self):
  578         execute('   exit  ', self.context)
  579         self.assertTrue(self.context.should_exit)
  580 
  581 
  582 class TestExecution_cd(ExecutionTestCase):
  583 
  584     def test_single_level(self):
  585         execute('cd api', self.context)
  586         self.assertEqual(self.context.url, 'http://localhost/api')
  587 
  588     def test_many_levels(self):
  589         execute('cd api/v2/movie/50', self.context)
  590         self.assertEqual(self.context.url, 'http://localhost/api/v2/movie/50')
  591 
  592     def test_change_base(self):
  593         execute('cd //example.com/api', self.context)
  594         self.assertEqual(self.context.url, 'http://example.com/api')
  595 
  596     def test_root(self):
  597         execute('cd /api/v2', self.context)
  598         self.assertEqual(self.context.url, 'http://localhost/api/v2')
  599 
  600         execute('cd /index.html', self.context)
  601         self.assertEqual(self.context.url, 'http://localhost/index.html')
  602 
  603     def test_dot_dot(self):
  604         execute('cd api/v1', self.context)
  605         self.assertEqual(self.context.url, 'http://localhost/api/v1')
  606 
  607         execute('cd ..', self.context)
  608         self.assertEqual(self.context.url, 'http://localhost/api')
  609 
  610         # If dot-dot has a trailing slash, the resulting URL should have a
  611         # trailing slash
  612         execute('cd ../rest/api/', self.context)
  613         self.assertEqual(self.context.url, 'http://localhost/rest/api/')
  614 
  615     def test_url_with_trailing_slash(self):
  616         self.context.url = 'http://localhost/'
  617         execute('cd api', self.context)
  618         self.assertEqual(self.context.url, 'http://localhost/api')
  619 
  620         execute('cd v2/', self.context)
  621         self.assertEqual(self.context.url, 'http://localhost/api/v2/')
  622 
  623         execute('cd /objects/', self.context)
  624         self.assertEqual(self.context.url, 'http://localhost/objects/')
  625 
  626     def test_path_with_trailing_slash(self):
  627         execute('cd api/', self.context)
  628         self.assertEqual(self.context.url, 'http://localhost/api/')
  629 
  630         execute('cd movie/1/', self.context)
  631         self.assertEqual(self.context.url, 'http://localhost/api/movie/1/')
  632 
  633     def test_without_url(self):
  634         execute('cd api/', self.context)
  635         self.assertEqual(self.context.url, 'http://localhost/api/')
  636 
  637         execute('cd', self.context)
  638         self.assertEqual(self.context.url, 'http://localhost')
  639 
  640 
  641 class TestExecution_rm(ExecutionTestCase):
  642 
  643     def test_header(self):
  644         self.context.headers['Content-Type'] = 'text/html'
  645         execute('rm -h Content-Type', self.context)
  646         self.assertFalse(self.context.headers)
  647 
  648     def test_option(self):
  649         self.context.options['--form'] = None
  650         execute('rm -o --form', self.context)
  651         self.assertFalse(self.context.options)
  652 
  653     def test_querystring(self):
  654         self.context.querystring_params['page'] = '1'
  655         execute('rm -q page', self.context)
  656         self.assertFalse(self.context.querystring_params)
  657 
  658     def test_body_param(self):
  659         self.context.body_params['name'] = 'alice'
  660         execute('rm -b name', self.context)
  661         self.assertFalse(self.context.body_params)
  662 
  663     def test_body_json_param(self):
  664         self.context.body_json_params['name'] = 'bob'
  665         execute('rm -b name', self.context)
  666         self.assertFalse(self.context.body_json_params)
  667 
  668     def test_header_single_quoted(self):
  669         self.context.headers['Content-Type'] = 'text/html'
  670         execute("rm -h 'Content-Type'", self.context)
  671         self.assertFalse(self.context.headers)
  672 
  673     def test_option_double_quoted(self):
  674         self.context.options['--form'] = None
  675         execute('rm -o "--form"', self.context)
  676         self.assertFalse(self.context.options)
  677 
  678     def test_querystring_double_quoted(self):
  679         self.context.querystring_params['page size'] = '10'
  680         execute('rm -q "page size"', self.context)
  681         self.assertFalse(self.context.querystring_params)
  682 
  683     def test_body_param_double_quoted(self):
  684         self.context.body_params['family name'] = 'Doe Doe'
  685         execute('rm -b "family name"', self.context)
  686         self.assertFalse(self.context.body_params)
  687 
  688     def test_body_param_escaped(self):
  689         self.context.body_params['family name'] = 'Doe Doe'
  690         execute('rm -b family\ name', self.context)
  691         self.assertFalse(self.context.body_params)
  692 
  693     def test_body_json_param_escaped_colon(self):
  694         self.context.body_json_params[r'where[id\:gt]'] = 2
  695         execute(r'rm -b where[id\:gt]', self.context)
  696         self.assertFalse(self.context.body_json_params)
  697 
  698     def test_body_param_escaped_equal(self):
  699         self.context.body_params[r'foo\=bar'] = 'hello'
  700         execute(r'rm -b foo\=bar', self.context)
  701         self.assertFalse(self.context.body_params)
  702 
  703     def test_non_existing_key(self):
  704         execute('rm -q abcd', self.context)
  705         self.assert_stderr("Key 'abcd' not found")
  706 
  707     @pytest.mark.skipif(not six.PY2, reason='a bug on Python 2')
  708     def test_non_existing_key_unicode(self):  # See #25
  709         execute(u'rm -q abcd', self.context)
  710         self.assert_stderr("Key 'abcd' not found")
  711 
  712     def test_body_reset(self):
  713         self.context.body_params.update({
  714             'first_name': 'alice',
  715             'last_name': 'bryne'
  716         })
  717         execute('rm -b *', self.context)
  718         self.assertFalse(self.context.body_params)
  719 
  720     def test_querystring_reset(self):
  721         self.context.querystring_params.update({
  722             'first_name': 'alice',
  723             'last_name': 'bryne'
  724         })
  725         execute('rm -q *', self.context)
  726         self.assertFalse(self.context.querystring_params)
  727 
  728     def test_headers_reset(self):
  729         self.context.headers.update({
  730             'Content-Type': 'text/html',
  731             'Accept': 'application/json'
  732         })
  733         execute('rm -h *', self.context)
  734         self.assertFalse(self.context.headers)
  735 
  736     def test_options_reset(self):
  737         self.context.options.update({
  738             '--form': None,
  739             '--body': None
  740         })
  741         execute('rm -o *', self.context)
  742         self.assertFalse(self.context.options)
  743 
  744     def test_reset(self):
  745         self.context.options.update({
  746             '--form': None,
  747             '--verify': 'no'
  748         })
  749         self.context.headers.update({
  750             'Accept': 'dontcare',
  751             'Content-Type': 'dontcare'
  752         })
  753         self.context.querystring_params.update({
  754             'name': 'dontcare',
  755             'email': 'dontcare'
  756         })
  757         self.context.body_params.update({
  758             'name': 'dontcare',
  759             'email': 'dontcare'
  760         })
  761         self.context.body_json_params.update({
  762             'name': 'dontcare'
  763         })
  764 
  765         execute('rm *', self.context)
  766 
  767         self.assertFalse(self.context.options)
  768         self.assertFalse(self.context.headers)
  769         self.assertFalse(self.context.querystring_params)
  770         self.assertFalse(self.context.body_params)
  771         self.assertFalse(self.context.body_json_params)
  772 
  773 
  774 class TestExecution_ls(ExecutionTestCase):
  775 
  776     def test_root(self):
  777         execute('ls', self.context)
  778         self.assert_stdout('orgs  users\n')
  779 
  780     def test_relative_path(self):
  781         self.context.url = 'http://localhost/users'
  782         execute('ls 101', self.context)
  783         self.assert_stdout('events orgs\n')
  784 
  785     def test_absolute_path(self):
  786         self.context.url = 'http://localhost/users'
  787         execute('ls /orgs/1', self.context)
  788         self.assert_stdout('events  members\n')
  789 
  790     def test_redirect_write(self):
  791         filename = self.make_tempfile()
  792 
  793         # Write something first to make sure it's a full overwrite
  794         with open(filename, 'w') as f:
  795             f.write('hello world\n')
  796 
  797         execute('ls > %s' % filename, self.context)
  798 
  799         with open(filename) as f:
  800             content = f.read()
  801         self.assertEqual(content, 'orgs\nusers')
  802 
  803     def test_redirect_append(self):
  804         filename = self.make_tempfile()
  805 
  806         # Write something first to make sure it's an append
  807         with open(filename, 'w') as f:
  808             f.write('hello world\n')
  809 
  810         execute('ls >> %s' % filename, self.context)
  811 
  812         with open(filename) as f:
  813             content = f.read()
  814         self.assertEqual(content, 'hello world\norgs\nusers')
  815 
  816     def test_grep(self):
  817         execute('ls | grep users', self.context)
  818         self.assert_stdout('users\n')
  819 
  820 
  821 class TestMutation(ExecutionTestCase):
  822 
  823     def test_simple_headers(self):
  824         execute('Accept:text/html User-Agent:HttpPrompt', self.context)
  825         self.assertEqual(self.context.headers, {
  826             'Accept': 'text/html',
  827             'User-Agent': 'HttpPrompt'
  828         })
  829 
  830     def test_header_value_with_double_quotes(self):
  831         execute('Accept:text/html User-Agent:"HTTP Prompt"', self.context)
  832         self.assertEqual(self.context.headers, {
  833             'Accept': 'text/html',
  834             'User-Agent': 'HTTP Prompt'
  835         })
  836 
  837     def test_header_value_with_single_quotes(self):
  838         execute("Accept:text/html User-Agent:'HTTP Prompt'", self.context)
  839         self.assertEqual(self.context.headers, {
  840             'Accept': 'text/html',
  841             'User-Agent': 'HTTP Prompt'
  842         })
  843 
  844     def test_header_with_double_quotes(self):
  845         execute('Accept:text/html "User-Agent:HTTP Prompt"', self.context)
  846         self.assertEqual(self.context.headers, {
  847             'Accept': 'text/html',
  848             'User-Agent': 'HTTP Prompt'
  849         })
  850 
  851     def test_header_with_single_quotes(self):
  852         execute("Accept:text/html 'User-Agent:HTTP Prompt'", self.context)
  853         self.assertEqual(self.context.headers, {
  854             'Accept': 'text/html',
  855             'User-Agent': 'HTTP Prompt'
  856         })
  857 
  858     def test_header_escaped_chars(self):
  859         execute(r'X-Name:John\'s\ Doe', self.context)
  860         self.assertEqual(self.context.headers, {
  861             'X-Name': "John's Doe"
  862         })
  863 
  864     def test_header_value_escaped_quote(self):
  865         execute(r"'X-Name:John\'s Doe'", self.context)
  866         self.assertEqual(self.context.headers, {
  867             'X-Name': "John's Doe"
  868         })
  869 
  870     def test_simple_querystring(self):
  871         execute('page==1 limit==20', self.context)
  872         self.assertEqual(self.context.querystring_params, {
  873             'page': ['1'],
  874             'limit': ['20']
  875         })
  876 
  877     def test_querystring_with_double_quotes(self):
  878         execute('page==1 name=="John Doe"', self.context)
  879         self.assertEqual(self.context.querystring_params, {
  880             'page': ['1'],
  881             'name': ['John Doe']
  882         })
  883 
  884     def test_querystring_with_single_quotes(self):
  885         execute("page==1 name=='John Doe'", self.context)
  886         self.assertEqual(self.context.querystring_params, {
  887             'page': ['1'],
  888             'name': ['John Doe']
  889         })
  890 
  891     def test_querystring_with_chinese(self):
  892         execute("name==王小明", self.context)
  893         self.assertEqual(self.context.querystring_params, {
  894             'name': ['王小明']
  895         })
  896 
  897     def test_querystring_escaped_chars(self):
  898         execute(r'name==John\'s\ Doe', self.context)
  899         self.assertEqual(self.context.querystring_params, {
  900             'name': ["John's Doe"]
  901         })
  902 
  903     def test_querytstring_value_escaped_quote(self):
  904         execute(r"'name==John\'s Doe'", self.context)
  905         self.assertEqual(self.context.querystring_params, {
  906             'name': ["John's Doe"]
  907         })
  908 
  909     def test_querystring_key_escaped_quote(self):
  910         execute(r"'john\'s last name==Doe'", self.context)
  911         self.assertEqual(self.context.querystring_params, {
  912             "john's last name": ['Doe']
  913         })
  914 
  915     def test_simple_body_params(self):
  916         execute('username=john password=123', self.context)
  917         self.assertEqual(self.context.body_params, {
  918             'username': 'john',
  919             'password': '123'
  920         })
  921 
  922     def test_body_param_value_with_double_quotes(self):
  923         execute('name="John Doe" password=123', self.context)
  924         self.assertEqual(self.context.body_params, {
  925             'name': 'John Doe',
  926             'password': '123'
  927         })
  928 
  929     def test_body_param_value_with_single_quotes(self):
  930         execute("name='John Doe' password=123", self.context)
  931         self.assertEqual(self.context.body_params, {
  932             'name': 'John Doe',
  933             'password': '123'
  934         })
  935 
  936     def test_body_param_with_double_quotes(self):
  937         execute('"name=John Doe" password=123', self.context)
  938         self.assertEqual(self.context.body_params, {
  939             'name': 'John Doe',
  940             'password': '123'
  941         })
  942 
  943     def test_body_param_with_spanish(self):
  944         execute('name=Jesús', self.context)
  945         self.assertEqual(self.context.body_params, {
  946             'name': 'Jesús'
  947         })
  948 
  949     def test_body_param_escaped_chars(self):
  950         execute(r'name=John\'s\ Doe', self.context)
  951         self.assertEqual(self.context.body_params, {
  952             'name': "John's Doe"
  953         })
  954 
  955     def test_body_param_value_escaped_quote(self):
  956         execute(r"'name=John\'s Doe'", self.context)
  957         self.assertEqual(self.context.body_params, {
  958             'name': "John's Doe"
  959         })
  960 
  961     def test_body_param_key_escaped_quote(self):
  962         execute(r"'john\'s last name=Doe'", self.context)
  963         self.assertEqual(self.context.body_params, {
  964             "john's last name": 'Doe'
  965         })
  966 
  967     def test_long_option_names(self):
  968         execute('--auth user:pass --form', self.context)
  969         self.assertEqual(self.context.options, {
  970             '--form': None,
  971             '--auth': 'user:pass'
  972         })
  973 
  974     def test_long_option_names_with_its_prefix(self):
  975         execute('--auth-type basic --auth user:pass --session user '
  976                 '--session-read-only user', self.context)
  977         self.assertEqual(self.context.options, {
  978             '--auth-type': 'basic',
  979             '--auth': 'user:pass',
  980             '--session-read-only': 'user',
  981             '--session': 'user'
  982         })
  983 
  984     def test_long_short_option_names_mixed(self):
  985         execute('--style=default -j --stream', self.context)
  986         self.assertEqual(self.context.options, {
  987             '-j': None,
  988             '--stream': None,
  989             '--style': 'default'
  990         })
  991 
  992     def test_option_and_body_param(self):
  993         execute('--form name="John Doe"', self.context)
  994         self.assertEqual(self.context.options, {
  995             '--form': None
  996         })
  997         self.assertEqual(self.context.body_params, {
  998             'name': 'John Doe'
  999         })
 1000 
 1001     def test_mixed(self):
 1002         execute('   --form  name="John Doe"   password=1234\ 5678    '
 1003                 'User-Agent:HTTP\ Prompt  -a   \'john:1234 5678\'  '
 1004                 '"Accept:text/html"  ', self.context)
 1005         self.assertEqual(self.context.options, {
 1006             '--form': None,
 1007             '-a': 'john:1234 5678'
 1008         })
 1009         self.assertEqual(self.context.headers, {
 1010             'User-Agent': 'HTTP Prompt',
 1011             'Accept': 'text/html'
 1012         })
 1013         self.assertEqual(self.context.options, {
 1014             '--form': None,
 1015             '-a': 'john:1234 5678'
 1016         })
 1017         self.assertEqual(self.context.body_params, {
 1018             'name': 'John Doe',
 1019             'password': '1234 5678'
 1020         })
 1021 
 1022     def test_multi_querystring(self):
 1023         execute('name==john name==doe', self.context)
 1024         self.assertEqual(self.context.querystring_params, {
 1025             'name': ['john', 'doe']
 1026         })
 1027 
 1028         execute('name==jane', self.context)
 1029         self.assertEqual(self.context.querystring_params, {
 1030             'name': ['jane']
 1031         })
 1032 
 1033     def test_raw_json_object(self):
 1034         execute("""definition:={"id":819,"name":"ML"}""", self.context)
 1035         self.assertEqual(self.context.body_json_params, {
 1036             'definition': {
 1037                 'id': 819,
 1038                 'name': 'ML'
 1039             }
 1040         })
 1041 
 1042     def test_raw_json_object_quoted(self):
 1043         execute("""definition:='{"id": 819, "name": "ML"}'""", self.context)
 1044         self.assertEqual(self.context.body_json_params, {
 1045             'definition': {
 1046                 'id': 819,
 1047                 'name': 'ML'
 1048             }
 1049         })
 1050 
 1051     def test_raw_json_array(self):
 1052         execute("""names:=["foo","bar"]""", self.context)
 1053         self.assertEqual(self.context.body_json_params, {
 1054             'names': ["foo", "bar"]
 1055         })
 1056 
 1057     def test_raw_json_array_quoted(self):
 1058         execute("""names:='["foo", "bar"]'""", self.context)
 1059         self.assertEqual(self.context.body_json_params, {
 1060             'names': ["foo", "bar"]
 1061         })
 1062 
 1063     def test_raw_json_integer(self):
 1064         execute('number:=999', self.context)
 1065         self.assertEqual(self.context.body_json_params, {'number': 999})
 1066 
 1067     def test_raw_json_string(self):
 1068         execute("""name:='"john doe"'""", self.context)
 1069         self.assertEqual(self.context.body_json_params, {'name': 'john doe'})
 1070 
 1071     def test_escape_colon(self):
 1072         execute(r'where[id\:gt]:=2', self.context)
 1073         self.assertEqual(self.context.body_json_params, {
 1074             r'where[id\:gt]': 2
 1075         })
 1076 
 1077     def test_escape_equal(self):
 1078         execute(r'foo\=bar=hello', self.context)
 1079         self.assertEqual(self.context.body_params, {
 1080             r'foo\=bar': 'hello'
 1081         })
 1082 
 1083 
 1084 class TestHttpAction(ExecutionTestCase):
 1085 
 1086     def test_get(self):
 1087         execute('get', self.context)
 1088         self.assert_httpie_main_called_with(['GET', 'http://localhost'])
 1089 
 1090     def test_get_uppercase(self):
 1091         execute('GET', self.context)
 1092         self.assert_httpie_main_called_with(['GET', 'http://localhost'])
 1093 
 1094     def test_get_multi_querystring(self):
 1095         execute('get foo==1 foo==2 foo==3', self.context)
 1096         self.assert_httpie_main_called_with([
 1097             'GET', 'http://localhost', 'foo==1', 'foo==2', 'foo==3'])
 1098 
 1099     def test_post(self):
 1100         execute('post page==1', self.context)
 1101         self.assert_httpie_main_called_with(['POST', 'http://localhost',
 1102                                              'page==1'])
 1103         self.assertFalse(self.context.querystring_params)
 1104 
 1105     def test_post_with_absolute_path(self):
 1106         execute('post /api/v3 name=bob', self.context)
 1107         self.assert_httpie_main_called_with(['POST', 'http://localhost/api/v3',
 1108                                              'name=bob'])
 1109         self.assertFalse(self.context.body_params)
 1110         self.assertEqual(self.context.url, 'http://localhost')
 1111 
 1112     def test_post_with_relative_path(self):
 1113         self.context.url = 'http://localhost/api/v3'
 1114         execute('post ../v2/movie id=8', self.context)
 1115         self.assert_httpie_main_called_with([
 1116             'POST', 'http://localhost/api/v2/movie', 'id=8'])
 1117         self.assertFalse(self.context.body_params)
 1118         self.assertEqual(self.context.url, 'http://localhost/api/v3')
 1119 
 1120     def test_post_with_full_url(self):
 1121         execute('post http://httpbin.org/post id=9', self.context)
 1122         self.assert_httpie_main_called_with([
 1123             'POST', 'http://httpbin.org/post', 'id=9'])
 1124         self.assertFalse(self.context.body_params)
 1125         self.assertEqual(self.context.url, 'http://localhost')
 1126 
 1127     def test_post_with_full_https_url(self):
 1128         execute('post https://httpbin.org/post id=9', self.context)
 1129         self.assert_httpie_main_called_with([
 1130             'POST', 'https://httpbin.org/post', 'id=9'])
 1131         self.assertFalse(self.context.body_params)
 1132         self.assertEqual(self.context.url, 'http://localhost')
 1133 
 1134     def test_post_uppercase(self):
 1135         execute('POST content=text', self.context)
 1136         self.assert_httpie_main_called_with(['POST', 'http://localhost',
 1137                                              'content=text'])
 1138         self.assertFalse(self.context.body_params)
 1139 
 1140     def test_post_raw_json_object(self):
 1141         execute("""post definition:={"id":819,"name":"ML"}""",
 1142                 self.context)
 1143         self.assert_httpie_main_called_with([
 1144             'POST', 'http://localhost',
 1145             """definition:={"id": 819, "name": "ML"}"""])
 1146         self.assertFalse(self.context.body_json_params)
 1147 
 1148     def test_post_raw_json_object_quoted(self):
 1149         execute("""post definition:='{"id": 819, "name": "ML"}'""",
 1150                 self.context)
 1151         self.assert_httpie_main_called_with([
 1152             'POST', 'http://localhost',
 1153             'definition:={"id": 819, "name": "ML"}'])
 1154         self.assertFalse(self.context.body_json_params)
 1155 
 1156     def test_post_raw_json_array(self):
 1157         execute("""post hobbies:=["foo","bar"]""",
 1158                 self.context)
 1159         self.assert_httpie_main_called_with([
 1160             'POST', 'http://localhost',
 1161             'hobbies:=["foo", "bar"]'])
 1162         self.assertFalse(self.context.body_json_params)
 1163 
 1164     def test_post_raw_json_array_quoted(self):
 1165         execute("""post hobbies:='["foo", "bar"]'""",
 1166                 self.context)
 1167         self.assert_httpie_main_called_with([
 1168             'POST', 'http://localhost',
 1169             'hobbies:=["foo", "bar"]'])
 1170         self.assertFalse(self.context.body_json_params)
 1171 
 1172     def test_post_raw_json_integer(self):
 1173         execute('post number:=123',
 1174                 self.context)
 1175         self.assert_httpie_main_called_with([
 1176             'POST', 'http://localhost', 'number:=123'])
 1177         self.assertFalse(self.context.body_json_params)
 1178 
 1179     def test_post_raw_json_boolean(self):
 1180         execute('post foo:=true',
 1181                 self.context)
 1182         self.assert_httpie_main_called_with([
 1183             'POST', 'http://localhost', 'foo:=true'])
 1184         self.assertFalse(self.context.body_json_params)
 1185 
 1186     def test_delete(self):
 1187         execute('delete', self.context)
 1188         self.assert_httpie_main_called_with(['DELETE', 'http://localhost'])
 1189 
 1190     def test_delete_uppercase(self):
 1191         execute('DELETE', self.context)
 1192         self.assert_httpie_main_called_with(['DELETE', 'http://localhost'])
 1193 
 1194     def test_patch(self):
 1195         execute('patch', self.context)
 1196         self.assert_httpie_main_called_with(['PATCH', 'http://localhost'])
 1197 
 1198     def test_patch_uppercase(self):
 1199         execute('PATCH', self.context)
 1200         self.assert_httpie_main_called_with(['PATCH', 'http://localhost'])
 1201 
 1202     def test_head(self):
 1203         execute('head', self.context)
 1204         self.assert_httpie_main_called_with(['HEAD', 'http://localhost'])
 1205 
 1206     def test_head_uppercase(self):
 1207         execute('HEAD', self.context)
 1208         self.assert_httpie_main_called_with(['HEAD', 'http://localhost'])
 1209 
 1210     def test_options(self):
 1211         execute('options', self.context)
 1212         self.assert_httpie_main_called_with(['OPTIONS', 'http://localhost'])
 1213 
 1214 
 1215 class TestHttpActionRedirection(ExecutionTestCase):
 1216 
 1217     def test_get(self):
 1218         execute('get > data.json', self.context)
 1219         self.assert_httpie_main_called_with(['GET', 'http://localhost'])
 1220 
 1221         env = self.httpie_main.call_args[1]['env']
 1222         self.assertFalse(env.stdout_isatty)
 1223         self.assertEqual(env.stdout.fp.name, 'data.json')
 1224 
 1225 
 1226 @pytest.mark.slow
 1227 class TestHttpBin(TempAppDirTestCase):
 1228     """Send real requests to http://httpbin.org, save the responses to files,
 1229     and asserts on the file content.
 1230     """
 1231     def setUp(self):
 1232         super(TestHttpBin, self).setUp()
 1233 
 1234         # XXX: pytest doesn't allow HTTPie to read stdin while it's capturing
 1235         # stdout, so we replace stdin with a file temporarily during the test.
 1236         class MockStdin(object):
 1237             def __init__(self, fp):
 1238                 self.fp = fp
 1239 
 1240             def isatty(self):
 1241                 return True
 1242 
 1243             def __getattr__(self, name):
 1244                 if name == 'isatty':
 1245                     return self.isatty
 1246                 return getattr(self.fp, name)
 1247 
 1248         self.orig_stdin = sys.stdin
 1249         filename = self.make_tempfile()
 1250         sys.stdin = MockStdin(open(filename, 'rb'))
 1251         sys.stdin.isatty = lambda: True
 1252 
 1253         # Mock echo_via_pager() so that we can catch data fed to stdout
 1254         self.patcher = patch('http_prompt.output.click.echo_via_pager')
 1255         self.echo_via_pager = self.patcher.start()
 1256 
 1257     def tearDown(self):
 1258         self.patcher.stop()
 1259 
 1260         sys.stdin.close()
 1261         sys.stdin = self.orig_stdin
 1262 
 1263         super(TestHttpBin, self).tearDown()
 1264 
 1265     def get_stdout(self):
 1266         return self.echo_via_pager.call_args[0][0]
 1267 
 1268     def execute_redirection(self, command):
 1269         context = Context('http://httpbin.org')
 1270         filename = self.make_tempfile()
 1271         execute('%s > %s' % (command, filename), context)
 1272 
 1273         with open(filename, 'rb') as f:
 1274             return f.read()
 1275 
 1276     def execute_pipe(self, command):
 1277         context = Context('http://httpbin.org')
 1278         execute(command, context)
 1279 
 1280     def test_get_image(self):
 1281         data = self.execute_redirection('get /image/png')
 1282         self.assertTrue(data)
 1283         self.assertEqual(hashlib.sha1(data).hexdigest(),
 1284                          '379f5137831350c900e757b39e525b9db1426d53')
 1285 
 1286     def test_get_querystring(self):
 1287         data = self.execute_redirection(
 1288             'get /get id==1234 X-Custom-Header:5678')
 1289         data = json.loads(data.decode('utf-8'))
 1290         self.assertEqual(data['args'], {
 1291             'id': '1234'
 1292         })
 1293         self.assertEqual(data['headers']['X-Custom-Header'], '5678')
 1294 
 1295     def test_post_json(self):
 1296         data = self.execute_redirection(
 1297             'post /post id=1234 X-Custom-Header:5678')
 1298         data = json.loads(data.decode('utf-8'))
 1299         self.assertEqual(data['json'], {
 1300             'id': '1234'
 1301         })
 1302         self.assertEqual(data['headers']['X-Custom-Header'], '5678')
 1303 
 1304     def test_post_form(self):
 1305         data = self.execute_redirection(
 1306             'post /post --form id=1234 X-Custom-Header:5678')
 1307         data = json.loads(data.decode('utf-8'))
 1308         self.assertEqual(data['form'], {
 1309             'id': '1234'
 1310         })
 1311         self.assertEqual(data['headers']['X-Custom-Header'], '5678')
 1312 
 1313     @pytest.mark.skipif(sys.platform == 'win32', reason="Unix only")
 1314     def test_get_and_tee(self):
 1315         filename = self.make_tempfile()
 1316         self.execute_pipe('get /get hello==world | tee %s' % filename)
 1317 
 1318         with open(filename) as f:
 1319             data = json.load(f)
 1320         self.assertEqual(data['args'], {'hello': 'world'})
 1321 
 1322         printed_msg = self.get_stdout()
 1323         data = json.loads(printed_msg)
 1324         self.assertEqual(data['args'], {'hello': 'world'})
 1325 
 1326 
 1327 class TestCommandPreview(ExecutionTestCase):
 1328 
 1329     def test_httpie_without_args(self):
 1330         execute('httpie', self.context)
 1331         self.assert_stdout('http http://localhost\n')
 1332 
 1333     def test_httpie_with_post(self):
 1334         execute('httpie post name=alice', self.context)
 1335         self.assert_stdout('http POST http://localhost name=alice\n')
 1336         self.assertFalse(self.context.body_params)
 1337 
 1338     def test_httpie_with_absolute_path(self):
 1339         execute('httpie post /api name=alice', self.context)
 1340         self.assert_stdout('http POST http://localhost/api name=alice\n')
 1341         self.assertFalse(self.context.body_params)
 1342 
 1343     def test_httpie_with_full_url(self):
 1344         execute('httpie POST http://httpbin.org/post name=alice', self.context)
 1345         self.assert_stdout('http POST http://httpbin.org/post name=alice\n')
 1346         self.assertEqual(self.context.url, 'http://localhost')
 1347         self.assertFalse(self.context.body_params)
 1348 
 1349     def test_httpie_with_full_https_url(self):
 1350         execute('httpie post https://httpbin.org/post name=alice',
 1351                 self.context)
 1352         self.assert_stdout('http POST https://httpbin.org/post name=alice\n')
 1353         self.assertEqual(self.context.url, 'http://localhost')
 1354         self.assertFalse(self.context.body_params)
 1355 
 1356     def test_httpie_with_quotes(self):
 1357         execute(r'httpie post http://httpbin.org/post name="john doe" '
 1358                 r"apikey==abc\ 123 'Authorization:ApiKey 1234'",
 1359                 self.context)
 1360         self.assert_stdout(
 1361             "http POST http://httpbin.org/post 'apikey==abc 123' "
 1362             "'name=john doe' 'Authorization:ApiKey 1234'\n")
 1363         self.assertEqual(self.context.url, 'http://localhost')
 1364         self.assertFalse(self.context.body_params)
 1365         self.assertFalse(self.context.querystring_params)
 1366         self.assertFalse(self.context.headers)
 1367 
 1368     def test_httpie_with_multi_querystring(self):
 1369         execute('httpie get foo==1 foo==2 foo==3', self.context)
 1370         self.assert_stdout('http GET http://localhost foo==1 foo==2 foo==3\n')
 1371         self.assertEqual(self.context.url, 'http://localhost')
 1372         self.assertFalse(self.context.querystring_params)
 1373 
 1374 
 1375 class TestPipe(ExecutionTestCase):
 1376 
 1377     @pytest.mark.skipif(sys.platform == 'win32', reason="Unix only")
 1378     def test_httpie_sed(self):
 1379         execute("httpie get some==data | sed 's/data$/input/'", self.context)
 1380         self.assert_stdout('http GET http://localhost some==input\n')
 1381 
 1382     @pytest.mark.skipif(sys.platform == 'win32', reason="Unix only")
 1383     def test_httpie_sed_with_echo(self):
 1384         execute("httpie post | `echo \"sed 's/localhost$/127.0.0.1/'\"`",
 1385                 self.context)
 1386         self.assert_stdout("http POST http://127.0.0.1\n")
 1387 
 1388     @pytest.mark.skipif(sys.platform == 'win32', reason="Unix only")
 1389     def test_env_grep(self):
 1390         self.context.body_params = {
 1391             'username': 'jane',
 1392             'name': 'Jane',
 1393             'password': '1234'
 1394         }
 1395         execute('env | grep name', self.context)
 1396         self.assert_stdout('name=Jane\nusername=jane\n')
 1397 
 1398 
 1399 class TestShellSubstitution(ExecutionTestCase):
 1400 
 1401     def test_unquoted_option(self):
 1402         execute("--auth `echo user:pass`", self.context)
 1403         self.assertEqual(self.context.options, {
 1404             '--auth': 'user:pass'
 1405         })
 1406 
 1407     def test_partial_unquoted_option(self):
 1408         execute("--auth user:`echo pass`", self.context)
 1409         self.assertEqual(self.context.options, {
 1410             '--auth': 'user:pass'
 1411         })
 1412 
 1413     def test_partial_squoted_option(self):
 1414         execute("--auth='user:`echo pass`'", self.context)
 1415         self.assertEqual(self.context.options, {
 1416             '--auth': 'user:pass'
 1417         })
 1418 
 1419     def test_partial_dquoted_option(self):
 1420         execute('--auth="user:`echo pass`"', self.context)
 1421         self.assertEqual(self.context.options, {
 1422             '--auth': 'user:pass'
 1423         })
 1424 
 1425     def test_unquoted_header(self):
 1426         execute("`echo 'X-Greeting'`:`echo 'hello world'`", self.context)
 1427         if sys.platform == 'win32':
 1428             expected_key = "'X-Greeting'"
 1429             expected_value = "'hello world'"
 1430         else:
 1431             expected_key = 'X-Greeting'
 1432             expected_value = 'hello world'
 1433 
 1434         self.assertEqual(self.context.headers, {
 1435             expected_key: expected_value
 1436         })
 1437 
 1438     def test_full_squoted_header(self):
 1439         execute("'`echo X-Greeting`:`echo hello`'", self.context)
 1440         self.assertEqual(self.context.headers, {
 1441             'X-Greeting': 'hello'
 1442         })
 1443 
 1444     def test_full_dquoted_header(self):
 1445         execute('"`echo X-Greeting`:`echo hello`"', self.context)
 1446         self.assertEqual(self.context.headers, {
 1447             'X-Greeting': 'hello'
 1448         })
 1449 
 1450     def test_value_squoted_header(self):
 1451         execute("`echo X-Greeting`:'`echo hello`'", self.context)
 1452         self.assertEqual(self.context.headers, {
 1453             'X-Greeting': 'hello'
 1454         })
 1455 
 1456     def test_value_dquoted_header(self):
 1457         execute('`echo X-Greeting`:"`echo hello`"', self.context)
 1458         self.assertEqual(self.context.headers, {
 1459             'X-Greeting': 'hello'
 1460         })
 1461 
 1462     def test_partial_value_dquoted_header(self):
 1463         execute('Authorization:"Bearer `echo OAUTH TOKEN`"', self.context)
 1464         self.assertEqual(self.context.headers, {
 1465             'Authorization': 'Bearer OAUTH TOKEN'
 1466         })
 1467 
 1468     def test_partial_full_dquoted_header(self):
 1469         execute('"Authorization:Bearer `echo OAUTH TOKEN`"', self.context)
 1470         self.assertEqual(self.context.headers, {
 1471             'Authorization': 'Bearer OAUTH TOKEN'
 1472         })
 1473 
 1474     def test_unquoted_querystring(self):
 1475         execute("`echo greeting`==`echo 'hello world'`", self.context)
 1476         expected = ("'hello world'"
 1477                     if sys.platform == 'win32' else 'hello world')
 1478         self.assertEqual(self.context.querystring_params, {
 1479             'greeting': [expected]
 1480         })
 1481 
 1482     def test_full_squoted_querystring(self):
 1483         execute("'`echo greeting`==`echo hello`'", self.context)
 1484         self.assertEqual(self.context.querystring_params, {
 1485             'greeting': ['hello']
 1486         })
 1487 
 1488     def test_value_squoted_querystring(self):
 1489         execute("`echo greeting`=='`echo hello`'", self.context)
 1490         self.assertEqual(self.context.querystring_params, {
 1491             'greeting': ['hello']
 1492         })
 1493 
 1494     def test_value_dquoted_querystring(self):
 1495         execute('`echo greeting`=="`echo hello`"', self.context)
 1496         self.assertEqual(self.context.querystring_params, {
 1497             'greeting': ['hello']
 1498         })
 1499 
 1500     def test_unquoted_body_param(self):
 1501         execute("`echo greeting`=`echo 'hello world'`", self.context)
 1502         expected = ("'hello world'"
 1503                     if sys.platform == 'win32' else 'hello world')
 1504         self.assertEqual(self.context.body_params, {
 1505             'greeting': expected
 1506         })
 1507 
 1508     def test_full_squoted_body_param(self):
 1509         execute("'`echo greeting`=`echo hello`'", self.context)
 1510         self.assertEqual(self.context.body_params, {
 1511             'greeting': 'hello'
 1512         })
 1513 
 1514     def test_value_squoted_body_param(self):
 1515         execute("`echo greeting`='`echo hello`'", self.context)
 1516         self.assertEqual(self.context.body_params, {
 1517             'greeting': 'hello'
 1518         })
 1519 
 1520     def test_full_dquoted_body_param(self):
 1521         execute('"`echo greeting`=`echo hello`"', self.context)
 1522         self.assertEqual(self.context.body_params, {
 1523             'greeting': 'hello'
 1524         })
 1525 
 1526     def test_bad_command(self):
 1527         execute("name=`bad command test`", self.context)
 1528         self.assertEqual(self.context.body_params, {'name': ''})
 1529 
 1530     @pytest.mark.skipif(sys.platform == 'win32', reason="Unix only")
 1531     def test_pipe_and_grep(self):
 1532         execute("greeting=`echo 'hello world\nhihi\n' | grep hello`",
 1533                 self.context)
 1534         self.assertEqual(self.context.body_params, {
 1535             'greeting': 'hello world'
 1536         })
 1537 
 1538 
 1539 class TestCommandPreviewRedirection(ExecutionTestCase):
 1540 
 1541     def test_httpie_redirect_write(self):
 1542         filename = self.make_tempfile()
 1543 
 1544         # Write something first to make sure it's a full overwrite
 1545         with open(filename, 'w') as f:
 1546             f.write('hello world\n')
 1547 
 1548         execute('httpie > %s' % filename, self.context)
 1549 
 1550         with open(filename) as f:
 1551             content = f.read()
 1552         self.assertEqual(content, 'http http://localhost\n')
 1553 
 1554     def test_httpie_redirect_write_quoted_filename(self):
 1555         filename = self.make_tempfile()
 1556 
 1557         # Write something first to make sure it's a full overwrite
 1558         with open(filename, 'w') as f:
 1559             f.write('hello world\n')
 1560 
 1561         execute('httpie > "%s"' % filename, self.context)
 1562 
 1563         with open(filename) as f:
 1564             content = f.read()
 1565         self.assertEqual(content, 'http http://localhost\n')
 1566 
 1567     @pytest.mark.skipif(sys.platform == 'win32',
 1568                         reason="Windows doesn't use backslashes to escape")
 1569     def test_httpie_redirect_write_escaped_filename(self):
 1570         filename = self.make_tempfile()
 1571         filename += r' copy'
 1572 
 1573         # Write something first to make sure it's a full overwrite
 1574         with open(filename, 'w') as f:
 1575             f.write('hello world\n')
 1576 
 1577         execute('httpie > %s' % filename.replace(' ', r'\ '), self.context)
 1578 
 1579         with open(filename) as f:
 1580             content = f.read()
 1581         self.assertEqual(content, 'http http://localhost\n')
 1582 
 1583     def test_httpie_redirect_write_with_args(self):
 1584         filename = self.make_tempfile()
 1585 
 1586         # Write something first to make sure it's a full overwrite
 1587         with open(filename, 'w') as f:
 1588             f.write('hello world\n')
 1589 
 1590         execute('httpie post http://example.org name=john > %s' % filename,
 1591                 self.context)
 1592 
 1593         with open(filename) as f:
 1594             content = f.read()
 1595         self.assertEqual(content, 'http POST http://example.org name=john\n')
 1596 
 1597     def test_httpie_redirect_append(self):
 1598         filename = self.make_tempfile()
 1599 
 1600         # Write something first to make sure it's an append
 1601         with open(filename, 'w') as f:
 1602             f.write('hello world\n')
 1603 
 1604         execute('httpie >> %s' % filename, self.context)
 1605 
 1606         with open(filename) as f:
 1607             content = f.read()
 1608         self.assertEqual(content, 'hello world\nhttp http://localhost\n')
 1609 
 1610     def test_httpie_redirect_append_without_spaces(self):
 1611         filename = self.make_tempfile()
 1612 
 1613         # Write something first to make sure it's an append
 1614         with open(filename, 'w') as f:
 1615             f.write('hello world\n')
 1616 
 1617         execute('httpie>>%s' % filename, self.context)
 1618 
 1619         with open(filename) as f:
 1620             content = f.read()
 1621         self.assertEqual(content, 'hello world\nhttp http://localhost\n')
 1622 
 1623     def test_httpie_redirect_append_quoted_filename(self):
 1624         filename = self.make_tempfile()
 1625 
 1626         # Write something first to make sure it's an append
 1627         with open(filename, 'w') as f:
 1628             f.write('hello world\n')
 1629 
 1630         execute("httpie >> '%s'" % filename, self.context)
 1631 
 1632         with open(filename) as f:
 1633             content = f.read()
 1634         self.assertEqual(content, 'hello world\nhttp http://localhost\n')