Final touches for #1088 (#1091)

* Make sure there’s no trailing \n in test files for easier output inspection

* Refactor output matching test utils

* More robust `test_http_307_allow_redirect_post_verbose()`

* Changelog

* Mention HTTP 307 Temporary Redirect re-post behaviour in README
This commit is contained in:
Jakub Roztocil 2021-06-15 14:48:44 +02:00 committed by GitHub
parent 2d55c01c7e
commit 07a0359316
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 64 deletions

View File

@ -9,9 +9,10 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
`2.5.0-dev`_ (unreleased) `2.5.0-dev`_ (unreleased)
------------------------- -------------------------
* Fixed ``--continue --download`` with a single byte to be downloaded left. (`#1032`_)
* Added ``--raw`` to allow specifying the raw request body without extra processing as * Added ``--raw`` to allow specifying the raw request body without extra processing as
an alternative to ``stdin``. (`#534`_) an alternative to ``stdin``. (`#534`_)
* Fixed ``--continue --download`` with a single byte to be downloaded left. (`#1032`_)
* Fixed ``--verbose`` HTTP 307 redirects with streamed request body. (`#1088`_)
`2.4.0`_ (2021-02-06) `2.4.0`_ (2021-02-06)
@ -477,6 +478,7 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
.. _#128: https://github.com/httpie/httpie/issues/128 .. _#128: https://github.com/httpie/httpie/issues/128
.. _#201: https://github.com/httpie/httpie/issues/201 .. _#201: https://github.com/httpie/httpie/issues/201
.. _#488: https://github.com/httpie/httpie/issues/488 .. _#488: https://github.com/httpie/httpie/issues/488
.. _#534: https://github.com/httpie/httpie/issues/534
.. _#668: https://github.com/httpie/httpie/issues/668 .. _#668: https://github.com/httpie/httpie/issues/668
.. _#684: https://github.com/httpie/httpie/issues/684 .. _#684: https://github.com/httpie/httpie/issues/684
.. _#718: https://github.com/httpie/httpie/issues/718 .. _#718: https://github.com/httpie/httpie/issues/718
@ -500,3 +502,4 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
.. _#1026: https://github.com/httpie/httpie/issues/1026 .. _#1026: https://github.com/httpie/httpie/issues/1026
.. _#1029: https://github.com/httpie/httpie/issues/1029 .. _#1029: https://github.com/httpie/httpie/issues/1029
.. _#1032: https://github.com/httpie/httpie/issues/1032 .. _#1032: https://github.com/httpie/httpie/issues/1032
.. _#1088: https://github.com/httpie/httpie/issues/1088

View File

@ -1071,6 +1071,10 @@ and show the final response instead, use the ``--follow, -F`` option:
$ http --follow pie.dev/redirect/3 $ http --follow pie.dev/redirect/3
With ``HTTP 307 Temporary Redirect``, the method and the body of the original request
are reused to perform the redirected request. Otherwise a body-less ``GET`` request is performed.
Showing intermediary redirect responses Showing intermediary redirect responses
--------------------------------------- ---------------------------------------

6
tests/fixtures/.editorconfig vendored Normal file
View File

@ -0,0 +1,6 @@
# https://editorconfig.org
[{*.txt, *.json}]
trim_trailing_whitespace = false
insert_final_newline = false

View File

@ -2,8 +2,9 @@
import pytest import pytest
from httpie.status import ExitStatus from httpie.status import ExitStatus
from .fixtures import FILE_PATH_ARG from .fixtures import FILE_PATH_ARG, FILE_CONTENT
from .utils import http, HTTP_OK from .utils import http, HTTP_OK
from .utils.matching import assert_output_matches, Expect, ExpectSequence
def test_follow_all_redirects_shown(httpbin): def test_follow_all_redirects_shown(httpbin):
@ -73,4 +74,11 @@ def test_http_307_allow_redirect_post_verbose(httpbin):
'@' + FILE_PATH_ARG) '@' + FILE_PATH_ARG)
assert r.count('POST /redirect-to') == 1 assert r.count('POST /redirect-to') == 1
assert r.count('POST /post') == 1 assert r.count('POST /post') == 1
assert r.count(FILE_CONTENT) == 3 # two requests + final response contain it
assert HTTP_OK in r assert HTTP_OK in r
assert_output_matches(r, [
*ExpectSequence.TERMINAL_REQUEST,
Expect.RESPONSE_HEADERS,
Expect.SEPARATOR,
*ExpectSequence.TERMINAL_EXCHANGE,
])

View File

@ -9,45 +9,10 @@ TODO: cover more scenarios
* streamed uploads * streamed uploads
""" """
from .utils.matching import assert_output_matches, Expect from .utils.matching import assert_output_matches, Expect, ExpectSequence
from .utils import http, HTTP_OK, MockEnvironment from .utils import http, HTTP_OK, MockEnvironment
RAW_REQUEST = [
Expect.REQUEST_HEADERS,
Expect.BODY,
]
RAW_RESPONSE = [
Expect.RESPONSE_HEADERS,
Expect.BODY,
]
RAW_EXCHANGE = [
*RAW_REQUEST,
Expect.SEPARATOR, # Good choice?
*RAW_RESPONSE,
]
RAW_BODY = [
Expect.BODY,
]
TERMINAL_REQUEST = [
*RAW_REQUEST,
Expect.SEPARATOR,
]
TERMINAL_RESPONSE = [
*RAW_RESPONSE,
Expect.SEPARATOR,
]
TERMINAL_EXCHANGE = [
*TERMINAL_REQUEST,
*TERMINAL_RESPONSE,
]
TERMINAL_BODY = [
RAW_BODY,
Expect.SEPARATOR
]
def test_headers(): def test_headers():
r = http('--print=H', '--offline', 'pie.dev') r = http('--print=H', '--offline', 'pie.dev')
assert_output_matches(r, [Expect.REQUEST_HEADERS]) assert_output_matches(r, [Expect.REQUEST_HEADERS])
@ -60,17 +25,17 @@ def test_redirected_headers():
def test_terminal_headers_and_body(): def test_terminal_headers_and_body():
r = http('--print=HB', '--offline', 'pie.dev', 'AAA=BBB') r = http('--print=HB', '--offline', 'pie.dev', 'AAA=BBB')
assert_output_matches(r, TERMINAL_REQUEST) assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST)
def test_terminal_request_headers_response_body(httpbin): def test_terminal_request_headers_response_body(httpbin):
r = http('--print=Hb', httpbin + '/get') r = http('--print=Hb', httpbin + '/get')
assert_output_matches(r, TERMINAL_REQUEST) assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST)
def test_raw_request_headers_response_body(httpbin): def test_raw_request_headers_response_body(httpbin):
r = http('--print=Hb', httpbin + '/get', env=MockEnvironment(stdout_isatty=False)) r = http('--print=Hb', httpbin + '/get', env=MockEnvironment(stdout_isatty=False))
assert_output_matches(r, RAW_REQUEST) assert_output_matches(r, ExpectSequence.RAW_REQUEST)
def test_terminal_request_headers_response_headers(httpbin): def test_terminal_request_headers_response_headers(httpbin):
@ -93,29 +58,29 @@ def test_raw_headers_and_body():
'--print=HB', '--offline', 'pie.dev', 'AAA=BBB', '--print=HB', '--offline', 'pie.dev', 'AAA=BBB',
env=MockEnvironment(stdout_isatty=False), env=MockEnvironment(stdout_isatty=False),
) )
assert_output_matches(r, RAW_REQUEST) assert_output_matches(r, ExpectSequence.RAW_REQUEST)
def test_raw_body(): def test_raw_body():
r = http('--print=B', '--offline', 'pie.dev', 'AAA=BBB', env=MockEnvironment(stdout_isatty=False)) r = http('--print=B', '--offline', 'pie.dev', 'AAA=BBB', env=MockEnvironment(stdout_isatty=False))
assert_output_matches(r, RAW_BODY) assert_output_matches(r, ExpectSequence.RAW_BODY)
def test_raw_exchange(httpbin): def test_raw_exchange(httpbin):
r = http('--verbose', httpbin + '/post', 'a=b', env=MockEnvironment(stdout_isatty=False)) r = http('--verbose', httpbin + '/post', 'a=b', env=MockEnvironment(stdout_isatty=False))
assert HTTP_OK in r assert HTTP_OK in r
assert_output_matches(r, RAW_EXCHANGE) assert_output_matches(r, ExpectSequence.RAW_EXCHANGE)
def test_terminal_exchange(httpbin): def test_terminal_exchange(httpbin):
r = http('--verbose', httpbin + '/post', 'a=b') r = http('--verbose', httpbin + '/post', 'a=b')
assert HTTP_OK in r assert HTTP_OK in r
assert_output_matches(r, TERMINAL_EXCHANGE) assert_output_matches(r, ExpectSequence.TERMINAL_EXCHANGE)
def test_headers_multipart_body_separator(): def test_headers_multipart_body_separator():
r = http('--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB') r = http('--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB')
assert_output_matches(r, TERMINAL_REQUEST) assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST)
def test_redirected_headers_multipart_no_separator(): def test_redirected_headers_multipart_no_separator():
@ -123,16 +88,16 @@ def test_redirected_headers_multipart_no_separator():
'--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB', '--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB',
env=MockEnvironment(stdout_isatty=False), env=MockEnvironment(stdout_isatty=False),
) )
assert_output_matches(r, RAW_REQUEST) assert_output_matches(r, ExpectSequence.RAW_REQUEST)
def test_verbose_chunked(httpbin_with_chunked_support): def test_verbose_chunked(httpbin_with_chunked_support):
r = http('--verbose', '--chunked', httpbin_with_chunked_support + '/post', 'hello=world') r = http('--verbose', '--chunked', httpbin_with_chunked_support + '/post', 'hello=world')
assert HTTP_OK in r assert HTTP_OK in r
assert 'Transfer-Encoding: chunked' in r assert 'Transfer-Encoding: chunked' in r
assert_output_matches(r, TERMINAL_EXCHANGE) assert_output_matches(r, ExpectSequence.TERMINAL_EXCHANGE)
def test_request_headers_response_body(httpbin): def test_request_headers_response_body(httpbin):
r = http('--print=Hb', httpbin + '/get') r = http('--print=Hb', httpbin + '/get')
assert_output_matches(r, TERMINAL_REQUEST) assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST)

View File

@ -1,14 +1,20 @@
"""
Utilities for testing output composition.
"""
from typing import Iterable from typing import Iterable
import pytest import pytest
from .parsing import OutputMatchingError, expect_tokens, Expect from .parsing import OutputMatchingError, expect_tokens
from .tokens import Expect, ExpectSequence
__all__ = [ __all__ = [
'assert_output_matches', 'assert_output_matches',
'assert_output_does_not_match', 'assert_output_does_not_match',
'Expect', 'Expect',
'ExpectSequence',
] ]

View File

@ -1,22 +1,11 @@
import re import re
from typing import Iterable from typing import Iterable
from enum import Enum, auto
from httpie.output.writer import MESSAGE_SEPARATOR from httpie.output.writer import MESSAGE_SEPARATOR
from .tokens import Expect
from ...utils import CRLF from ...utils import CRLF
class Expect(Enum):
"""
Predefined token types we can expect in the output.
"""
REQUEST_HEADERS = auto()
RESPONSE_HEADERS = auto()
BODY = auto()
SEPARATOR = auto()
SEPARATOR_RE = re.compile(f'^{MESSAGE_SEPARATOR}') SEPARATOR_RE = re.compile(f'^{MESSAGE_SEPARATOR}')

View File

@ -0,0 +1,51 @@
from enum import Enum, auto
class Expect(Enum):
"""
Predefined token types we can expect in the output.
"""
REQUEST_HEADERS = auto()
RESPONSE_HEADERS = auto()
BODY = auto()
SEPARATOR = auto()
class ExpectSequence:
"""
Standard combined chunks. These predefined requests and responses assume a body.
"""
RAW_REQUEST = [
Expect.REQUEST_HEADERS,
Expect.BODY,
]
RAW_RESPONSE = [
Expect.RESPONSE_HEADERS,
Expect.BODY,
]
RAW_EXCHANGE = [
*RAW_REQUEST,
Expect.SEPARATOR, # Good choice?
*RAW_RESPONSE,
]
RAW_BODY = [
Expect.BODY,
]
TERMINAL_REQUEST = [
*RAW_REQUEST,
Expect.SEPARATOR,
]
TERMINAL_RESPONSE = [
*RAW_RESPONSE,
Expect.SEPARATOR,
]
TERMINAL_EXCHANGE = [
*TERMINAL_REQUEST,
*TERMINAL_RESPONSE,
]
TERMINAL_BODY = [
RAW_BODY,
Expect.SEPARATOR
]