Files
requests/tests/test_structures.py
2018-03-15 14:20:53 -04:00

250 lines
9.0 KiB
Python

# -*- coding: utf-8 -*-
import pytest
from requests.structures import CaseInsensitiveDict, LookupDict, HTTPHeaderDict
from urllib3._collections import HTTPHeaderDict as U3HeaderDict
class TestCaseInsensitiveDict:
@pytest.fixture(autouse=True)
def setup(self):
"""CaseInsensitiveDict instance with "Accept" header."""
self.case_insensitive_dict = CaseInsensitiveDict()
self.case_insensitive_dict['Accept'] = 'application/json'
def test_list(self):
assert list(self.case_insensitive_dict) == ['Accept']
possible_keys = pytest.mark.parametrize(
'key', ('accept', 'ACCEPT', 'aCcEpT', 'Accept')
)
@possible_keys
def test_getitem(self, key):
assert self.case_insensitive_dict[key] == 'application/json'
@possible_keys
def test_delitem(self, key):
del self.case_insensitive_dict[key]
assert key not in self.case_insensitive_dict
def test_lower_items(self):
assert list(self.case_insensitive_dict.lower_items()) == [
('accept', 'application/json')
]
def test_repr(self):
assert repr(
self.case_insensitive_dict
) == "{'Accept': 'application/json'}"
def test_copy(self):
copy = self.case_insensitive_dict.copy()
assert copy is not self.case_insensitive_dict
assert copy == self.case_insensitive_dict
@pytest.mark.parametrize(
'other, result',
(({'AccePT': 'application/json'}, True), ({}, False), (None, False)),
)
def test_instance_equality(self, other, result):
assert (self.case_insensitive_dict == other) is result
class TestHTTPHeaderDictCompatibility(TestCaseInsensitiveDict):
"""HTTPHeaderDict should be completely compatible with CaseInsensitiveDict
when used for headers, so ensure that all the tests for the base class
also pass for this one."""
@pytest.fixture(autouse=True)
def setup(self):
self.case_insensitive_dict = HTTPHeaderDict()
self.case_insensitive_dict['Accept'] = 'application/json'
class TestHTTPHeaderDict:
@pytest.fixture(autouse=True)
def setup(self):
self.kvs = [
('animal', 'chicken'),
('AnimaL', 'Cow'),
('CAKE', 'Cheese!'),
('Sauce', 'Bread'),
('Sauce', 'Cherry, or Plum Tomato'),
]
# HTTPHeaderDict from urllib3.
self.u3dict = ud = U3HeaderDict()
[ud.add(*tpl) for tpl in self.kvs]
# Regular dictionary.
self.ddict = dict(self.kvs)
self.ddict['Sauce'] = ['Bread!', 'Cherry, or Plum Tomato']
# Used by test_extend. All of these "extra" values are mostly
# equivalent to each other.
self.extra_hd = hd2 = HTTPHeaderDict(ANIMAL=['Dog', 'elephant'])
hd2['cake'] = 'Babka'
hd2.setlist('sound', ['quiet', 'LOUD'])
hd2['CUTLERY'] = 'fork'
self.extra_tuple_pairs = tuple_pairs = [
('ANIMAL', 'Dog'),
('Animal', 'elephant'),
('cake', ['Babka']),
('sound', 'quiet'),
('sound', 'LOUD'),
('CUTLERY', 'fork'),
]
self.extra_simple_dict = dict(tuple_pairs)
self.extra_simple_dict['sound'] = ('quiet', 'LOUD')
self.extra_u3 = U3HeaderDict()
for k, v in tuple_pairs:
if isinstance(v, (tuple, list)):
for vi in v:
self.extra_u3.add(k, vi)
else:
self.extra_u3.add(k, v)
def test_item_access(self):
hd = HTTPHeaderDict(self.kvs)
# Test that values are combined.
assert hd['Sauce'] == 'Bread, Cherry, or Plum Tomato'
assert hd['ANIMAL'] == 'chicken, Cow'
# Test we can overwrite values.
hd['animal'] = 'Goat!'
assert hd['anIMal'] == 'Goat!'
# Test deletion works.
del hd['sauce']
pytest.raises(KeyError, hd.__getitem__, 'sauce')
# Only string types allowed.
pytest.raises(ValueError, hd.__setitem__, 'cake', ['Cheese', 'sponge'])
def test_equality(self):
hd = HTTPHeaderDict(self.u3dict)
assert hd == self.u3dict
assert hd == HTTPHeaderDict(hd)
# Test that we still work even if we are comparing to a
# CaseInsensitiveDict instance.
cid = CaseInsensitiveDict(hd)
assert hd == cid
assert cid == hd
def test_lower_items(self):
hd = HTTPHeaderDict(self.kvs, cutlery='fork')
assert list(hd.lower_items()) == [
('animal', 'chicken, Cow'),
('cake', 'Cheese!'),
('sauce', 'Bread, Cherry, or Plum Tomato'),
('cutlery', 'fork'),
]
def test_copy(self):
hd = HTTPHeaderDict(self.u3dict)
hd2 = hd.copy()
assert hd is not hd2
assert hd == hd2
def test_get_and_set_list(self):
hd = HTTPHeaderDict(self.kvs)
assert hd.getlist('SAUCE') == ['Bread', 'Cherry, or Plum Tomato']
assert hd.getlist('CAKE') == ['Cheese!']
assert hd.getlist('DRINK') == []
# Needs to be a regular sequence type containing just strings.
pytest.raises(ValueError, hd.setlist, 'Drink', 'Water')
pytest.raises(ValueError, hd.setlist, 'Drink', ['H', 2, 'O'])
# Test multi-setting.
hd.setlist('Drink', ['Water', 'Juice'])
assert hd.getlist('DRINK') == ['Water', 'Juice']
# Setting to an empty sequence should remove the entry.
hd.setlist('DRInk', [])
pytest.raises(KeyError, hd.__getitem__, 'DrinK')
assert hd.getlist('DRiNK') == []
def test_add(self):
hd = HTTPHeaderDict()
hd.add('sound', 'quiet')
hd.add('SOUND', 'LOUD')
assert hd.getlist('Sound') == ['quiet', 'LOUD']
# Enforce type-checking in the add method.
pytest.raises(ValueError, hd.add, 'Sound', 5)
@pytest.mark.parametrize(
'attr,as_arg,animal_arg_is_ordered',
[('extra_hd', True, True), ('extra_tuple_pairs', True, True), ('extra_simple_dict', True, False), ('extra_u3', True, False), ('extra_simple_dict', False, False)],
# These types will have the "animal" arguments in our preferred order.
# And these types will lose the ordering, so we can't make assertions
# about the final order of those values.
)
def test_extend(self, attr, as_arg, animal_arg_is_ordered):
item = getattr(self, attr)
# Call extend with the associated values - we should see all of the
# merged data in the HTTPHeaderDict instance.
extras = {'cutlery': 'knife'}
hd = HTTPHeaderDict(self.kvs)
if as_arg:
hd.extend(item, **extras)
else:
hd.extend(extras, **item)
# Test all the stored values are what we expect.
mget = hd.getlist
# Depending on the item we merged in, we might be able to make
# assumptions what the overall order of the structure is.
animal_seq = mget('animal')
if animal_arg_is_ordered:
assert animal_seq == ['chicken', 'Cow', 'Dog', 'elephant']
else:
# The existing order in HTTPHeadersDict of the first two values
# should be preserved - no guarantees in which order the other
# two values are added.
assert animal_seq in [
['chicken', 'Cow', 'Dog', 'elephant'],
['chicken', 'Cow', 'elephant', 'Dog'],
]
assert mget('cake') == ['Cheese!', 'Babka']
assert mget('sound') == ['quiet', 'LOUD']
# We don't mandate the order in which these dictionaries are
# processed, so it's fine whichever order it is.
assert mget('cutlery') in [['fork', 'knife'], ['knife', 'fork']]
def test_extend_type_checking(self):
hd = HTTPHeaderDict()
pytest.raises(ValueError, hd.extend, dict(type=['xml', None, 'html']))
def test_repr(self):
hd = HTTPHeaderDict()
assert repr(hd) == '{}'
hd.add('type', 'xml')
assert repr(hd) == "{'type': 'xml'}"
hd.add('type', 'html')
assert repr(hd) == "{'type': ('xml', 'html')}"
# We can't guarantee order once we have more than one key.
hd.add('Accept', 'text/html')
assert repr(hd) in [
"{'type': ('xml', 'html'), 'Accept': 'text/html'}",
"{'Accept': 'text/html', 'type': ('xml', 'html')}",
]
assert str(hd) == repr(hd)
class TestLookupDict:
@pytest.fixture(autouse=True)
def setup(self):
"""LookupDict instance with "bad_gateway" attribute."""
self.lookup_dict = LookupDict('test')
self.lookup_dict.bad_gateway = 502
def test_repr(self):
assert repr(self.lookup_dict) == "<lookup 'test'>"
get_item_parameters = pytest.mark.parametrize(
'key, value', (('bad_gateway', 502), ('not_a_key', None))
)
@get_item_parameters
def test_getitem(self, key, value):
assert self.lookup_dict[key] == value
@get_item_parameters
def test_get(self, key, value):
assert self.lookup_dict.get(key) == value