httpie-cli/tests/test_sessions.py

369 lines
12 KiB
Python
Raw Normal View History

# coding=utf-8
import json
2014-04-24 14:07:31 +02:00
import os
import shutil
from datetime import datetime
from tempfile import gettempdir
import pytest
2014-04-24 14:07:31 +02:00
from fixtures import UNICODE
2014-04-26 19:32:08 +02:00
from httpie.plugins.builtin import HTTPBasicAuth
from httpie.sessions import Session
from httpie.utils import get_expired_cookies
from utils import MockEnvironment, mk_config_dir, http, HTTP_OK
2014-04-24 14:07:31 +02:00
class SessionTestBase:
def start_session(self, httpbin):
2014-04-25 13:57:33 +02:00
"""Create and reuse a unique config dir for each test."""
2014-04-24 14:07:31 +02:00
self.config_dir = mk_config_dir()
def teardown_method(self, method):
2014-04-24 14:07:31 +02:00
shutil.rmtree(self.config_dir)
2014-04-25 13:50:44 +02:00
def env(self):
"""
Return an environment.
2019-12-02 18:12:51 +01:00
Each environment created within a test method
2014-04-25 13:50:44 +02:00
will share the same config_dir. It is necessary
for session files being reused.
"""
return MockEnvironment(config_dir=self.config_dir)
2014-04-24 14:07:31 +02:00
class CookieTestBase:
def setup_method(self, method):
self.config_dir = mk_config_dir()
orig_session = {
'cookies': {
'cookie1': {
'value': 'foo',
},
'cookie2': {
'value': 'foo',
}
}
}
self.session_path = self.config_dir / 'test-session.json'
self.session_path.write_text(json.dumps(orig_session))
def teardown_method(self, method):
shutil.rmtree(self.config_dir)
2014-04-25 13:52:43 +02:00
class TestSessionFlow(SessionTestBase):
2014-04-25 13:50:44 +02:00
"""
2014-04-25 13:57:33 +02:00
These tests start with an existing session created in `setup_method()`.
2014-04-25 13:50:44 +02:00
"""
def start_session(self, httpbin):
2014-04-25 13:50:44 +02:00
"""
Start a full-blown session with a custom request header,
authorization, and response cookies.
"""
super().start_session(httpbin)
r1 = http(
'--follow',
'--session=test',
'--auth=username:password',
'GET',
httpbin.url + '/cookies/set?hello=world',
'Hello:World',
env=self.env()
)
2014-04-25 13:50:44 +02:00
assert HTTP_OK in r1
def test_session_created_and_reused(self, httpbin):
self.start_session(httpbin)
2014-04-25 13:50:44 +02:00
# Verify that the session created in setup_method() has been used.
r2 = http('--session=test',
'GET', httpbin.url + '/get', env=self.env())
assert HTTP_OK in r2
2014-04-25 13:50:44 +02:00
assert r2.json['headers']['Hello'] == 'World'
assert r2.json['headers']['Cookie'] == 'hello=world'
assert 'Basic ' in r2.json['headers']['Authorization']
2014-04-24 14:07:31 +02:00
def test_session_update(self, httpbin):
self.start_session(httpbin)
2014-04-24 14:07:31 +02:00
# Get a response to a request from the original session.
2015-10-22 19:32:16 +02:00
r2 = http('--session=test', 'GET', httpbin.url + '/get',
env=self.env())
2014-04-25 13:50:44 +02:00
assert HTTP_OK in r2
2014-04-24 14:07:31 +02:00
# Make a request modifying the session data.
2014-04-25 13:50:44 +02:00
r3 = http('--follow', '--session=test', '--auth=username:password2',
2015-10-22 19:32:16 +02:00
'GET', httpbin.url + '/cookies/set?hello=world2',
'Hello:World2',
2014-04-25 13:50:44 +02:00
env=self.env())
assert HTTP_OK in r3
2014-04-24 14:07:31 +02:00
# Get a response to a request from the updated session.
2015-10-22 19:32:16 +02:00
r4 = http('--session=test', 'GET', httpbin.url + '/get',
env=self.env())
2014-04-25 13:50:44 +02:00
assert HTTP_OK in r4
assert r4.json['headers']['Hello'] == 'World2'
assert r4.json['headers']['Cookie'] == 'hello=world2'
assert (r2.json['headers']['Authorization']
!= r4.json['headers']['Authorization'])
2014-04-24 14:07:31 +02:00
def test_session_read_only(self, httpbin):
self.start_session(httpbin)
2014-04-24 14:07:31 +02:00
# Get a response from the original session.
2015-10-22 19:32:16 +02:00
r2 = http('--session=test', 'GET', httpbin.url + '/get',
env=self.env())
2014-04-25 13:50:44 +02:00
assert HTTP_OK in r2
2014-04-24 14:07:31 +02:00
# Make a request modifying the session data but
# with --session-read-only.
2014-04-25 13:50:44 +02:00
r3 = http('--follow', '--session-read-only=test',
2014-04-24 15:48:01 +02:00
'--auth=username:password2', 'GET',
httpbin.url + '/cookies/set?hello=world2', 'Hello:World2',
2014-04-25 13:50:44 +02:00
env=self.env())
assert HTTP_OK in r3
2014-04-24 14:07:31 +02:00
# Get a response from the updated session.
2015-10-22 19:32:16 +02:00
r4 = http('--session=test', 'GET', httpbin.url + '/get',
env=self.env())
2014-04-25 13:50:44 +02:00
assert HTTP_OK in r4
2014-04-24 14:07:31 +02:00
# Origin can differ on Travis.
2014-04-25 13:50:44 +02:00
del r2.json['origin'], r4.json['origin']
2014-04-24 14:07:31 +02:00
# Different for each request.
2014-04-25 13:50:44 +02:00
# Should be the same as before r3.
assert r2.json == r4.json
2014-04-25 13:52:43 +02:00
class TestSession(SessionTestBase):
2014-04-25 13:50:44 +02:00
"""Stand-alone session tests."""
def test_session_ignored_header_prefixes(self, httpbin):
self.start_session(httpbin)
r1 = http('--session=test', 'GET', httpbin.url + '/get',
2014-04-25 13:52:43 +02:00
'Content-Type: text/plain',
'If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT',
env=self.env())
2014-04-25 13:50:44 +02:00
assert HTTP_OK in r1
2015-10-22 19:32:16 +02:00
r2 = http('--session=test', 'GET', httpbin.url + '/get',
env=self.env())
2014-04-25 13:50:44 +02:00
assert HTTP_OK in r2
2016-03-01 16:22:50 +01:00
assert 'Content-Type' not in r2.json['headers']
2014-04-25 13:50:44 +02:00
assert 'If-Unmodified-Since' not in r2.json['headers']
2014-04-24 14:07:31 +02:00
def test_session_by_path(self, httpbin):
self.start_session(httpbin)
session_path = self.config_dir / 'session-by-path.json'
r1 = http('--session', str(session_path), 'GET', httpbin.url + '/get',
2014-04-25 13:50:44 +02:00
'Foo:Bar', env=self.env())
assert HTTP_OK in r1
2014-04-24 14:07:31 +02:00
r2 = http('--session', str(session_path), 'GET', httpbin.url + '/get',
2014-04-25 13:50:44 +02:00
env=self.env())
assert HTTP_OK in r2
2014-04-25 12:18:35 +02:00
assert r2.json['headers']['Foo'] == 'Bar'
def test_session_unicode(self, httpbin):
self.start_session(httpbin)
r1 = http('--session=test', u'--auth=test:' + UNICODE,
'GET', httpbin.url + '/get', u'Test:%s' % UNICODE,
env=self.env())
assert HTTP_OK in r1
2014-06-03 19:44:22 +02:00
r2 = http('--session=test', '--verbose', 'GET',
httpbin.url + '/get', env=self.env())
assert HTTP_OK in r2
# FIXME: Authorization *sometimes* is not present on Python3
assert (r2.json['headers']['Authorization']
== HTTPBasicAuth.make_header(u'test', UNICODE))
2014-06-03 19:44:22 +02:00
# httpbin doesn't interpret utf8 headers
assert UNICODE in r2
def test_session_default_header_value_overwritten(self, httpbin):
self.start_session(httpbin)
2017-03-10 11:27:38 +01:00
# https://github.com/jakubroztocil/httpie/issues/180
r1 = http('--session=test',
httpbin.url + '/headers', 'User-Agent:custom',
env=self.env())
assert HTTP_OK in r1
assert r1.json['headers']['User-Agent'] == 'custom'
r2 = http('--session=test', httpbin.url + '/headers', env=self.env())
assert HTTP_OK in r2
assert r2.json['headers']['User-Agent'] == 'custom'
def test_download_in_session(self, httpbin):
2017-03-10 11:27:38 +01:00
# https://github.com/jakubroztocil/httpie/issues/412
self.start_session(httpbin)
cwd = os.getcwd()
2016-02-28 12:15:35 +01:00
os.chdir(gettempdir())
try:
http('--session=test', '--download',
httpbin.url + '/get', env=self.env())
finally:
os.chdir(cwd)
class TestExpiredCookies(CookieTestBase):
2020-06-15 23:02:16 +02:00
@pytest.mark.parametrize(
argnames=['initial_cookie', 'expired_cookie'],
argvalues=[
({'id': {'value': 123}}, 'id'),
({'id': {'value': 123}}, 'token')
]
)
def test_removes_expired_cookies_from_session_obj(self, initial_cookie, expired_cookie, httpbin):
2020-06-15 23:02:16 +02:00
session = Session(self.config_dir)
session['cookies'] = initial_cookie
session.remove_cookies([expired_cookie])
assert expired_cookie not in session.cookies
def test_expired_cookies(self, httpbin):
r = http(
'--session', str(self.session_path),
'--print=H',
httpbin.url + '/cookies/delete?cookie2',
)
assert 'Cookie: cookie1=foo; cookie2=foo' in r
updated_session = json.loads(self.session_path.read_text())
assert 'cookie1' in updated_session['cookies']
assert 'cookie2' not in updated_session['cookies']
@pytest.mark.parametrize(
2020-06-15 23:02:16 +02:00
argnames=['headers', 'now', 'expected_expired'],
argvalues=[
(
[
('Set-Cookie', 'hello=world; Path=/; Expires=Thu, 01-Jan-1970 00:00:00 GMT; HttpOnly'),
('Connection', 'keep-alive')
],
None,
[
{
'name': 'hello',
'path': '/'
}
]
),
(
[
('Set-Cookie', 'hello=world; Path=/; Expires=Thu, 01-Jan-1970 00:00:00 GMT; HttpOnly'),
('Set-Cookie', 'pea=pod; Path=/ab; Expires=Thu, 01-Jan-1970 00:00:00 GMT; HttpOnly'),
('Connection', 'keep-alive')
],
None,
[
{'name': 'hello', 'path': '/'},
{'name': 'pea', 'path': '/ab'}
]
),
(
[
('Set-Cookie', 'hello=world; Path=/; Expires=Fri, 12 Jun 2020 12:28:55 GMT; HttpOnly'),
('Connection', 'keep-alive')
],
datetime(2020, 6, 11).timestamp(),
2020-06-15 23:02:16 +02:00
[]
)
]
)
2020-06-15 23:02:16 +02:00
def test_get_expired_cookies_manages_multiple_cookie_headers(self, headers, now, expected_expired):
assert get_expired_cookies(headers, now=now) == expected_expired
class TestCookieStorage(CookieTestBase):
@pytest.mark.parametrize(
argnames=['new_cookies', 'new_cookies_dict', 'expected'],
argvalues=[(
'new=bar',
{'new': 'bar'},
'cookie1=foo; cookie2=foo; new=bar'
),
(
'new=bar;chocolate=milk',
{'new': 'bar', 'chocolate': 'milk'},
'chocolate=milk; cookie1=foo; cookie2=foo; new=bar'
),
(
'new=bar; chocolate=milk',
{'new': 'bar', 'chocolate': 'milk'},
'chocolate=milk; cookie1=foo; cookie2=foo; new=bar'
),
(
'new=bar;; chocolate=milk;;;',
{'new': 'bar', 'chocolate': 'milk'},
'cookie1=foo; cookie2=foo; new=bar'
),
(
'new=bar; chocolate=milk;;;',
{'new': 'bar', 'chocolate': 'milk'},
'chocolate=milk; cookie1=foo; cookie2=foo; new=bar'
)
]
)
def test_existing_and_new_cookies_sent_in_request(self, new_cookies, new_cookies_dict, expected, httpbin):
r = http(
'--session', str(self.session_path),
'--print=H',
httpbin.url,
'Cookie:' + new_cookies,
)
# Note: cookies in response are in alphabetical order
assert 'Cookie: ' + expected in r
updated_session = json.loads(self.session_path.read_text())
for name, value in new_cookies_dict.items():
assert name, value in updated_session['cookies']
assert 'Cookie' not in updated_session['headers']
@pytest.mark.parametrize(
argnames=['cli_cookie', 'set_cookie', 'expected'],
argvalues=[(
'',
'/cookies/set/cookie1/bar',
'bar'
),
(
'cookie1=not_foo',
'/cookies/set/cookie1/bar',
'bar'
),
(
'cookie1=not_foo',
'',
'not_foo'
),
(
'',
'',
'foo'
)
]
)
def test_cookie_storage_priority(self, cli_cookie, set_cookie, expected, httpbin):
"""
Expected order of priority for cookie storage in session file:
1. set-cookie (from server)
2. command line arg
3. cookie already stored in session file
"""
r = http(
'--session', str(self.session_path),
httpbin.url + set_cookie,
'Cookie:' + cli_cookie,
)
updated_session = json.loads(self.session_path.read_text())
assert updated_session['cookies']['cookie1']['value'] == expected