runscope!
[services/python-requests.git] / test_requests.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """Tests for Requests."""
5
6 from __future__ import division
7 import json
8 import os
9 import unittest
10 import pickle
11
12 import requests
13 from requests.auth import HTTPDigestAuth
14 from requests.adapters import HTTPAdapter
15 from requests.compat import str, cookielib, getproxies, urljoin, urlparse
16 from requests.cookies import cookiejar_from_dict
17 from requests.exceptions import InvalidURL, MissingSchema
18 from requests.structures import CaseInsensitiveDict
19
20 try:
21     import StringIO
22 except ImportError:
23     import io as StringIO
24
25 HTTPBIN = os.environ.get('HTTPBIN_URL', 'http://httpbin.org/')
26 # Issue #1483: Make sure the URL always has a trailing slash
27 HTTPBIN = HTTPBIN.rstrip('/') + '/'
28
29
30 def httpbin(*suffix):
31     """Returns url for HTTPBIN resource."""
32     return urljoin(HTTPBIN, '/'.join(suffix))
33
34
35 class RequestsTestCase(unittest.TestCase):
36
37     _multiprocess_can_split_ = True
38
39     def setUp(self):
40         """Create simple data set with headers."""
41         pass
42
43     def tearDown(self):
44         """Teardown."""
45         pass
46
47     def test_entry_points(self):
48
49         requests.session
50         requests.session().get
51         requests.session().head
52         requests.get
53         requests.head
54         requests.put
55         requests.patch
56         requests.post
57
58     def test_invalid_url(self):
59         self.assertRaises(MissingSchema, requests.get, 'hiwpefhipowhefopw')
60         self.assertRaises(InvalidURL, requests.get, 'http://')
61
62     def test_basic_building(self):
63         req = requests.Request()
64         req.url = 'http://kennethreitz.org/'
65         req.data = {'life': '42'}
66
67         pr = req.prepare()
68         assert pr.url == req.url
69         assert pr.body == 'life=42'
70
71     def test_no_content_length(self):
72         get_req = requests.Request('GET', httpbin('get')).prepare()
73         self.assertTrue('Content-Length' not in get_req.headers)
74         head_req = requests.Request('HEAD', httpbin('head')).prepare()
75         self.assertTrue('Content-Length' not in head_req.headers)
76
77     def test_path_is_not_double_encoded(self):
78         request = requests.Request('GET', "http://0.0.0.0/get/test case").prepare()
79
80         self.assertEqual(request.path_url, "/get/test%20case")
81
82     def test_params_are_added_before_fragment(self):
83         request = requests.Request('GET',
84             "http://example.com/path#fragment", params={"a": "b"}).prepare()
85         self.assertEqual(request.url,
86             "http://example.com/path?a=b#fragment")
87         request = requests.Request('GET',
88             "http://example.com/path?key=value#fragment", params={"a": "b"}).prepare()
89         self.assertEqual(request.url,
90             "http://example.com/path?key=value&a=b#fragment")
91
92     def test_mixed_case_scheme_acceptable(self):
93         s = requests.Session()
94         s.proxies = getproxies()
95         parts = urlparse(httpbin('get'))
96         schemes = ['http://', 'HTTP://', 'hTTp://', 'HttP://',
97                    'https://', 'HTTPS://', 'hTTps://', 'HttPs://']
98         for scheme in schemes:
99             url = scheme + parts.netloc + parts.path
100             r = requests.Request('GET', url)
101             r = s.send(r.prepare())
102             self.assertEqual(r.status_code, 200,
103                              "failed for scheme %s" % scheme)
104
105     def test_HTTP_200_OK_GET_ALTERNATIVE(self):
106         r = requests.Request('GET', httpbin('get'))
107         s = requests.Session()
108         s.proxies = getproxies()
109
110         r = s.send(r.prepare())
111
112         self.assertEqual(r.status_code, 200)
113
114     def test_HTTP_302_ALLOW_REDIRECT_GET(self):
115         r = requests.get(httpbin('redirect', '1'))
116         self.assertEqual(r.status_code, 200)
117
118     # def test_HTTP_302_ALLOW_REDIRECT_POST(self):
119     #     r = requests.post(httpbin('status', '302'), data={'some': 'data'})
120     #     self.assertEqual(r.status_code, 200)
121
122     def test_HTTP_200_OK_GET_WITH_PARAMS(self):
123         heads = {'User-agent': 'Mozilla/5.0'}
124
125         r = requests.get(httpbin('user-agent'), headers=heads)
126
127         self.assertTrue(heads['User-agent'] in r.text)
128         self.assertEqual(r.status_code, 200)
129
130     def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
131         heads = {'User-agent': 'Mozilla/5.0'}
132
133         r = requests.get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
134         self.assertEqual(r.status_code, 200)
135
136     def test_set_cookie_on_301(self):
137         s = requests.session()
138         url = httpbin('cookies/set?foo=bar')
139         r = s.get(url)
140         self.assertTrue(s.cookies['foo'] == 'bar')
141
142     def test_cookie_sent_on_redirect(self):
143         s = requests.session()
144         s.get(httpbin('cookies/set?foo=bar'))
145         r = s.get(httpbin('redirect/1'))  # redirects to httpbin('get')
146         self.assertTrue("Cookie" in r.json()["headers"])
147
148     def test_cookie_removed_on_expire(self):
149         s = requests.session()
150         s.get(httpbin('cookies/set?foo=bar'))
151         self.assertTrue(s.cookies['foo'] == 'bar')
152         s.get(
153             httpbin('response-headers'),
154             params={
155                 'Set-Cookie':
156                     'foo=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT'
157             }
158         )
159         assert 'foo' not in s.cookies
160
161     def test_cookie_quote_wrapped(self):
162         s = requests.session()
163         s.get(httpbin('cookies/set?foo="bar:baz"'))
164         self.assertTrue(s.cookies['foo'] == '"bar:baz"')
165
166     def test_request_cookie_overrides_session_cookie(self):
167         s = requests.session()
168         s.cookies['foo'] = 'bar'
169         r = s.get(httpbin('cookies'), cookies={'foo': 'baz'})
170         assert r.json()['cookies']['foo'] == 'baz'
171         # Session cookie should not be modified
172         assert s.cookies['foo'] == 'bar'
173
174     def test_generic_cookiejar_works(self):
175         cj = cookielib.CookieJar()
176         cookiejar_from_dict({'foo': 'bar'}, cj)
177         s = requests.session()
178         s.cookies = cj
179         r = s.get(httpbin('cookies'))
180         # Make sure the cookie was sent
181         assert r.json()['cookies']['foo'] == 'bar'
182         # Make sure the session cj is still the custom one
183         assert s.cookies is cj
184
185     def test_requests_in_history_are_not_overridden(self):
186         resp = requests.get(httpbin('redirect/3'))
187         urls = [r.url for r in resp.history]
188         req_urls = [r.request.url for r in resp.history]
189         self.assertEquals(urls, req_urls)
190
191     def test_user_agent_transfers(self):
192
193         heads = {
194             'User-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
195         }
196
197         r = requests.get(httpbin('user-agent'), headers=heads)
198         self.assertTrue(heads['User-agent'] in r.text)
199
200         heads = {
201             'user-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
202         }
203
204         r = requests.get(httpbin('user-agent'), headers=heads)
205         self.assertTrue(heads['user-agent'] in r.text)
206
207     def test_HTTP_200_OK_HEAD(self):
208         r = requests.head(httpbin('get'))
209         self.assertEqual(r.status_code, 200)
210
211     def test_HTTP_200_OK_PUT(self):
212         r = requests.put(httpbin('put'))
213         self.assertEqual(r.status_code, 200)
214
215     def test_BASICAUTH_TUPLE_HTTP_200_OK_GET(self):
216         auth = ('user', 'pass')
217         url = httpbin('basic-auth', 'user', 'pass')
218
219         r = requests.get(url, auth=auth)
220         self.assertEqual(r.status_code, 200)
221
222         r = requests.get(url)
223         self.assertEqual(r.status_code, 401)
224
225         s = requests.session()
226         s.auth = auth
227         r = s.get(url)
228         self.assertEqual(r.status_code, 200)
229
230     def test_basicauth_with_netrc(self):
231         auth = ('user', 'pass')
232         wrong_auth = ('wronguser', 'wrongpass')
233         url = httpbin('basic-auth', 'user', 'pass')
234
235         def get_netrc_auth_mock(url):
236             return auth
237         requests.sessions.get_netrc_auth = get_netrc_auth_mock
238
239         # Should use netrc and work.
240         r = requests.get(url)
241         self.assertEqual(r.status_code, 200)
242
243         # Given auth should override and fail.
244         r = requests.get(url, auth=wrong_auth)
245         self.assertEqual(r.status_code, 401)
246
247         s = requests.session()
248
249         # Should use netrc and work.
250         r = s.get(url)
251         self.assertEqual(r.status_code, 200)
252
253         # Given auth should override and fail.
254         s.auth = wrong_auth
255         r = s.get(url)
256         self.assertEqual(r.status_code, 401)
257
258     def test_DIGEST_HTTP_200_OK_GET(self):
259
260         auth = HTTPDigestAuth('user', 'pass')
261         url = httpbin('digest-auth', 'auth', 'user', 'pass')
262
263         r = requests.get(url, auth=auth)
264         self.assertEqual(r.status_code, 200)
265
266         r = requests.get(url)
267         self.assertEqual(r.status_code, 401)
268
269         s = requests.session()
270         s.auth = HTTPDigestAuth('user', 'pass')
271         r = s.get(url)
272         self.assertEqual(r.status_code, 200)
273
274     def test_DIGEST_AUTH_RETURNS_COOKIE(self):
275         url = httpbin('digest-auth', 'auth', 'user', 'pass')
276         auth = HTTPDigestAuth('user', 'pass')
277         r = requests.get(url)
278         assert r.cookies['fake'] == 'fake_value'
279
280         r = requests.get(url, auth=auth)
281         assert r.status_code == 200
282
283     def test_DIGEST_AUTH_SETS_SESSION_COOKIES(self):
284         url = httpbin('digest-auth', 'auth', 'user', 'pass')
285         auth = HTTPDigestAuth('user', 'pass')
286         s = requests.Session()
287         s.get(url, auth=auth)
288         assert s.cookies['fake'] == 'fake_value'
289
290     def test_DIGEST_STREAM(self):
291
292         auth = HTTPDigestAuth('user', 'pass')
293         url = httpbin('digest-auth', 'auth', 'user', 'pass')
294
295         r = requests.get(url, auth=auth, stream=True)
296         self.assertNotEqual(r.raw.read(), b'')
297
298         r = requests.get(url, auth=auth, stream=False)
299         self.assertEqual(r.raw.read(), b'')
300
301
302     def test_DIGESTAUTH_WRONG_HTTP_401_GET(self):
303
304         auth = HTTPDigestAuth('user', 'wrongpass')
305         url = httpbin('digest-auth', 'auth', 'user', 'pass')
306
307         r = requests.get(url, auth=auth)
308         self.assertEqual(r.status_code, 401)
309
310         r = requests.get(url)
311         self.assertEqual(r.status_code, 401)
312
313         s = requests.session()
314         s.auth = auth
315         r = s.get(url)
316         self.assertEqual(r.status_code, 401)
317
318     def test_POSTBIN_GET_POST_FILES(self):
319
320         url = httpbin('post')
321         post1 = requests.post(url).raise_for_status()
322
323         post1 = requests.post(url, data={'some': 'data'})
324         self.assertEqual(post1.status_code, 200)
325
326         with open('requirements.txt') as f:
327             post2 = requests.post(url, files={'some': f})
328         self.assertEqual(post2.status_code, 200)
329
330         post4 = requests.post(url, data='[{"some": "json"}]')
331         self.assertEqual(post4.status_code, 200)
332
333         try:
334             requests.post(url, files=['bad file data'])
335         except ValueError:
336             pass
337
338     def test_POSTBIN_GET_POST_FILES_WITH_DATA(self):
339
340         url = httpbin('post')
341         post1 = requests.post(url).raise_for_status()
342
343         post1 = requests.post(url, data={'some': 'data'})
344         self.assertEqual(post1.status_code, 200)
345
346         with open('requirements.txt') as f:
347             post2 = requests.post(url, data={'some': 'data'}, files={'some': f})
348         self.assertEqual(post2.status_code, 200)
349
350         post4 = requests.post(url, data='[{"some": "json"}]')
351         self.assertEqual(post4.status_code, 200)
352
353         try:
354             requests.post(url, files=['bad file data'])
355         except ValueError:
356             pass
357
358     def test_request_ok_set(self):
359         r = requests.get(httpbin('status', '404'))
360         self.assertEqual(r.ok, False)
361
362     def test_status_raising(self):
363         r = requests.get(httpbin('status', '404'))
364         self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
365
366         r = requests.get(httpbin('status', '500'))
367         self.assertFalse(r.ok)
368
369     def test_decompress_gzip(self):
370         r = requests.get(httpbin('gzip'))
371         r.content.decode('ascii')
372
373     def test_unicode_get(self):
374         url = httpbin('/get')
375         requests.get(url, params={'foo': 'føø'})
376         requests.get(url, params={'føø': 'føø'})
377         requests.get(url, params={'føø': 'føø'})
378         requests.get(url, params={'foo': 'foo'})
379         requests.get(httpbin('ø'), params={'foo': 'foo'})
380
381     def test_unicode_header_name(self):
382         requests.put(httpbin('put'), headers={str('Content-Type'): 'application/octet-stream'}, data='\xff') # compat.str is unicode.
383
384     def test_urlencoded_get_query_multivalued_param(self):
385
386         r = requests.get(httpbin('get'), params=dict(test=['foo', 'baz']))
387         self.assertEqual(r.status_code, 200)
388         self.assertEqual(r.url, httpbin('get?test=foo&test=baz'))
389
390     def test_different_encodings_dont_break_post(self):
391         r = requests.post(httpbin('post'),
392                           data={'stuff': json.dumps({'a': 123})},
393                           params={'blah': 'asdf1234'},
394                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
395         self.assertEqual(r.status_code, 200)
396
397     def test_unicode_multipart_post(self):
398         r = requests.post(httpbin('post'),
399                           data={'stuff': u'ëlïxr'},
400                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
401         self.assertEqual(r.status_code, 200)
402
403         r = requests.post(httpbin('post'),
404                           data={'stuff': u'ëlïxr'.encode('utf-8')},
405                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
406         self.assertEqual(r.status_code, 200)
407
408         r = requests.post(httpbin('post'),
409                           data={'stuff': 'elixr'},
410                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
411         self.assertEqual(r.status_code, 200)
412
413         r = requests.post(httpbin('post'),
414                           data={'stuff': 'elixr'.encode('utf-8')},
415                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
416         self.assertEqual(r.status_code, 200)
417
418     def test_unicode_multipart_post_fieldnames(self):
419         filename = os.path.splitext(__file__)[0] + '.py'
420         r = requests.Request(method='POST',
421                              url=httpbin('post'),
422                              data={'stuff'.encode('utf-8'): 'elixr'},
423                              files={'file': ('test_requests.py',
424                                              open(filename, 'rb'))})
425         prep = r.prepare()
426         self.assertTrue(b'name="stuff"' in prep.body)
427         self.assertFalse(b'name="b\'stuff\'"' in prep.body)
428
429     def test_custom_content_type(self):
430         r = requests.post(httpbin('post'),
431                           data={'stuff': json.dumps({'a': 123})},
432                           files={'file1': ('test_requests.py', open(__file__, 'rb')),
433                                  'file2': ('test_requests', open(__file__, 'rb'),
434                                            'text/py-content-type')})
435         self.assertEqual(r.status_code, 200)
436         self.assertTrue(b"text/py-content-type" in r.request.body)
437
438     def test_hook_receives_request_arguments(self):
439         def hook(resp, **kwargs):
440             assert resp is not None
441             assert kwargs != {}
442
443         requests.Request('GET', HTTPBIN, hooks={'response': hook})
444
445     def test_prepared_request_hook(self):
446         def hook(resp, **kwargs):
447             resp.hook_working = True
448             return resp
449
450         req = requests.Request('GET', HTTPBIN, hooks={'response': hook})
451         prep = req.prepare()
452
453         s = requests.Session()
454         s.proxies = getproxies()
455         resp = s.send(prep)
456
457         self.assertTrue(hasattr(resp, 'hook_working'))
458
459     def test_prepared_from_session(self):
460         class DummyAuth(requests.auth.AuthBase):
461             def __call__(self, r):
462                 r.headers['Dummy-Auth-Test'] = 'dummy-auth-test-ok'
463                 return r
464
465         req = requests.Request('GET', httpbin('headers'))
466         self.assertEqual(req.auth, None)
467
468         s = requests.Session()
469         s.auth = DummyAuth()
470
471         prep = s.prepare_request(req)
472         resp = s.send(prep)
473
474         self.assertTrue(resp.json()['headers']['Dummy-Auth-Test'], 'dummy-auth-test-ok')
475
476     def test_links(self):
477         r = requests.Response()
478         r.headers = {
479             'cache-control': 'public, max-age=60, s-maxage=60',
480             'connection': 'keep-alive',
481             'content-encoding': 'gzip',
482             'content-type': 'application/json; charset=utf-8',
483             'date': 'Sat, 26 Jan 2013 16:47:56 GMT',
484             'etag': '"6ff6a73c0e446c1f61614769e3ceb778"',
485             'last-modified': 'Sat, 26 Jan 2013 16:22:39 GMT',
486             'link': ('<https://api.github.com/users/kennethreitz/repos?'
487                      'page=2&per_page=10>; rel="next", <https://api.github.'
488                      'com/users/kennethreitz/repos?page=7&per_page=10>; '
489                      ' rel="last"'),
490             'server': 'GitHub.com',
491             'status': '200 OK',
492             'vary': 'Accept',
493             'x-content-type-options': 'nosniff',
494             'x-github-media-type': 'github.beta',
495             'x-ratelimit-limit': '60',
496             'x-ratelimit-remaining': '57'
497         }
498         self.assertEqual(r.links['next']['rel'], 'next')
499
500     def test_cookie_parameters(self):
501         key = 'some_cookie'
502         value = 'some_value'
503         secure = True
504         domain = 'test.com'
505         rest = {'HttpOnly': True}
506
507         jar = requests.cookies.RequestsCookieJar()
508         jar.set(key, value, secure=secure, domain=domain, rest=rest)
509
510         self.assertEqual(len(jar), 1)
511         self.assertTrue('some_cookie' in jar)
512
513         cookie = list(jar)[0]
514         self.assertEqual(cookie.secure, secure)
515         self.assertEqual(cookie.domain, domain)
516         self.assertEqual(cookie._rest['HttpOnly'], rest['HttpOnly'])
517
518     def test_time_elapsed_blank(self):
519         r = requests.get(httpbin('get'))
520         td = r.elapsed
521         total_seconds = ((td.microseconds + (td.seconds + td.days * 24 * 3600)
522                          * 10**6) / 10**6)
523         self.assertTrue(total_seconds > 0.0)
524
525     def test_response_is_iterable(self):
526         r = requests.Response()
527         io = StringIO.StringIO('abc')
528         read_ = io.read
529
530         def read_mock(amt, decode_content=None):
531             return read_(amt)
532         setattr(io, 'read', read_mock)
533         r.raw = io
534         self.assertTrue(next(iter(r)))
535         io.close()
536
537     def test_get_auth_from_url(self):
538         url = 'http://user:pass@complex.url.com/path?query=yes'
539         self.assertEqual(('user', 'pass'),
540                          requests.utils.get_auth_from_url(url))
541
542     def test_cannot_send_unprepared_requests(self):
543         r = requests.Request(url=HTTPBIN)
544         self.assertRaises(ValueError, requests.Session().send, r)
545
546     def test_http_error(self):
547         error = requests.exceptions.HTTPError()
548         self.assertEqual(error.response, None)
549         response = requests.Response()
550         error = requests.exceptions.HTTPError(response=response)
551         self.assertEqual(error.response, response)
552         error = requests.exceptions.HTTPError('message', response=response)
553         self.assertEqual(str(error), 'message')
554         self.assertEqual(error.response, response)
555
556     def test_session_pickling(self):
557         r = requests.Request('GET', httpbin('get'))
558         s = requests.Session()
559
560         s = pickle.loads(pickle.dumps(s))
561         s.proxies = getproxies()
562
563         r = s.send(r.prepare())
564         self.assertEqual(r.status_code, 200)
565
566     def test_fixes_1329(self):
567         """
568         Ensure that header updates are done case-insensitively.
569         """
570         s = requests.Session()
571         s.headers.update({'ACCEPT': 'BOGUS'})
572         s.headers.update({'accept': 'application/json'})
573         r = s.get(httpbin('get'))
574         headers = r.request.headers
575         self.assertEqual(
576             headers['accept'],
577             'application/json'
578         )
579         self.assertEqual(
580             headers['Accept'],
581             'application/json'
582         )
583         self.assertEqual(
584             headers['ACCEPT'],
585             'application/json'
586         )
587
588     def test_uppercase_scheme_redirect(self):
589         parts = urlparse(httpbin('html'))
590         url = "HTTP://" + parts.netloc + parts.path
591         r = requests.get(httpbin('redirect-to'), params={'url': url})
592         self.assertEqual(r.status_code, 200)
593         self.assertEqual(r.url.lower(), url.lower())
594
595     def test_transport_adapter_ordering(self):
596         s = requests.Session()
597         order = ['https://', 'http://']
598         self.assertEqual(order, list(s.adapters))
599         s.mount('http://git', HTTPAdapter())
600         s.mount('http://github', HTTPAdapter())
601         s.mount('http://github.com', HTTPAdapter())
602         s.mount('http://github.com/about/', HTTPAdapter())
603         order = [
604             'http://github.com/about/',
605             'http://github.com',
606             'http://github',
607             'http://git',
608             'https://',
609             'http://',
610         ]
611         self.assertEqual(order, list(s.adapters))
612         s.mount('http://gittip', HTTPAdapter())
613         s.mount('http://gittip.com', HTTPAdapter())
614         s.mount('http://gittip.com/about/', HTTPAdapter())
615         order = [
616             'http://github.com/about/',
617             'http://gittip.com/about/',
618             'http://github.com',
619             'http://gittip.com',
620             'http://github',
621             'http://gittip',
622             'http://git',
623             'https://',
624             'http://',
625         ]
626         self.assertEqual(order, list(s.adapters))
627         s2 = requests.Session()
628         s2.adapters = {'http://': HTTPAdapter()}
629         s2.mount('https://', HTTPAdapter())
630         self.assertTrue('http://' in s2.adapters)
631         self.assertTrue('https://' in s2.adapters)
632
633     def test_header_remove_is_case_insensitive(self):
634         # From issue #1321
635         s = requests.Session()
636         s.headers['foo'] = 'bar'
637         r = s.get(httpbin('get'), headers={'FOO': None})
638         assert 'foo' not in r.request.headers
639
640     def test_params_are_merged_case_sensitive(self):
641         s = requests.Session()
642         s.params['foo'] = 'bar'
643         r = s.get(httpbin('get'), params={'FOO': 'bar'})
644         assert r.json()['args'] == {'foo': 'bar', 'FOO': 'bar'}
645
646
647     def test_long_authinfo_in_url(self):
648         url = 'http://{0}:{1}@{2}:9000/path?query#frag'.format(
649             'E8A3BE87-9E3F-4620-8858-95478E385B5B',
650             'EA770032-DA4D-4D84-8CE9-29C6D910BF1E',
651             'exactly-------------sixty-----------three------------characters',
652         )
653         r = requests.Request('GET', url).prepare()
654         self.assertEqual(r.url, url)
655
656     def test_header_keys_are_native(self):
657         headers = {u'unicode': 'blah', 'byte'.encode('ascii'): 'blah'}
658         r = requests.Request('GET', httpbin('get'), headers=headers)
659         p = r.prepare()
660
661         # This is testing that they are builtin strings. A bit weird, but there
662         # we go.
663         self.assertTrue('unicode' in p.headers.keys())
664         self.assertTrue('byte' in p.headers.keys())
665
666     def test_can_send_nonstring_objects_with_files(self):
667         data = {'a': 0.0}
668         files = {'b': 'foo'}
669         r = requests.Request('POST', httpbin('post'), data=data, files=files)
670         p = r.prepare()
671
672         self.assertTrue('multipart/form-data' in p.headers['Content-Type'])
673
674
675 class TestCaseInsensitiveDict(unittest.TestCase):
676
677     def test_mapping_init(self):
678         cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
679         self.assertEqual(len(cid), 2)
680         self.assertTrue('foo' in cid)
681         self.assertTrue('bar' in cid)
682
683     def test_iterable_init(self):
684         cid = CaseInsensitiveDict([('Foo', 'foo'), ('BAr', 'bar')])
685         self.assertEqual(len(cid), 2)
686         self.assertTrue('foo' in cid)
687         self.assertTrue('bar' in cid)
688
689     def test_kwargs_init(self):
690         cid = CaseInsensitiveDict(FOO='foo', BAr='bar')
691         self.assertEqual(len(cid), 2)
692         self.assertTrue('foo' in cid)
693         self.assertTrue('bar' in cid)
694
695     def test_docstring_example(self):
696         cid = CaseInsensitiveDict()
697         cid['Accept'] = 'application/json'
698         self.assertEqual(cid['aCCEPT'], 'application/json')
699         self.assertEqual(list(cid), ['Accept'])
700
701     def test_len(self):
702         cid = CaseInsensitiveDict({'a': 'a', 'b': 'b'})
703         cid['A'] = 'a'
704         self.assertEqual(len(cid), 2)
705
706     def test_getitem(self):
707         cid = CaseInsensitiveDict({'Spam': 'blueval'})
708         self.assertEqual(cid['spam'], 'blueval')
709         self.assertEqual(cid['SPAM'], 'blueval')
710
711     def test_fixes_649(self):
712         """__setitem__ should behave case-insensitively."""
713         cid = CaseInsensitiveDict()
714         cid['spam'] = 'oneval'
715         cid['Spam'] = 'twoval'
716         cid['sPAM'] = 'redval'
717         cid['SPAM'] = 'blueval'
718         self.assertEqual(cid['spam'], 'blueval')
719         self.assertEqual(cid['SPAM'], 'blueval')
720         self.assertEqual(list(cid.keys()), ['SPAM'])
721
722     def test_delitem(self):
723         cid = CaseInsensitiveDict()
724         cid['Spam'] = 'someval'
725         del cid['sPam']
726         self.assertFalse('spam' in cid)
727         self.assertEqual(len(cid), 0)
728
729     def test_contains(self):
730         cid = CaseInsensitiveDict()
731         cid['Spam'] = 'someval'
732         self.assertTrue('Spam' in cid)
733         self.assertTrue('spam' in cid)
734         self.assertTrue('SPAM' in cid)
735         self.assertTrue('sPam' in cid)
736         self.assertFalse('notspam' in cid)
737
738     def test_get(self):
739         cid = CaseInsensitiveDict()
740         cid['spam'] = 'oneval'
741         cid['SPAM'] = 'blueval'
742         self.assertEqual(cid.get('spam'), 'blueval')
743         self.assertEqual(cid.get('SPAM'), 'blueval')
744         self.assertEqual(cid.get('sPam'), 'blueval')
745         self.assertEqual(cid.get('notspam', 'default'), 'default')
746
747     def test_update(self):
748         cid = CaseInsensitiveDict()
749         cid['spam'] = 'blueval'
750         cid.update({'sPam': 'notblueval'})
751         self.assertEqual(cid['spam'], 'notblueval')
752         cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
753         cid.update({'fOO': 'anotherfoo', 'bAR': 'anotherbar'})
754         self.assertEqual(len(cid), 2)
755         self.assertEqual(cid['foo'], 'anotherfoo')
756         self.assertEqual(cid['bar'], 'anotherbar')
757
758     def test_update_retains_unchanged(self):
759         cid = CaseInsensitiveDict({'foo': 'foo', 'bar': 'bar'})
760         cid.update({'foo': 'newfoo'})
761         self.assertEquals(cid['bar'], 'bar')
762
763     def test_iter(self):
764         cid = CaseInsensitiveDict({'Spam': 'spam', 'Eggs': 'eggs'})
765         keys = frozenset(['Spam', 'Eggs'])
766         self.assertEqual(frozenset(iter(cid)), keys)
767
768     def test_equality(self):
769         cid = CaseInsensitiveDict({'SPAM': 'blueval', 'Eggs': 'redval'})
770         othercid = CaseInsensitiveDict({'spam': 'blueval', 'eggs': 'redval'})
771         self.assertEqual(cid, othercid)
772         del othercid['spam']
773         self.assertNotEqual(cid, othercid)
774         self.assertEqual(cid, {'spam': 'blueval', 'eggs': 'redval'})
775
776     def test_setdefault(self):
777         cid = CaseInsensitiveDict({'Spam': 'blueval'})
778         self.assertEqual(
779             cid.setdefault('spam', 'notblueval'),
780             'blueval'
781         )
782         self.assertEqual(
783             cid.setdefault('notspam', 'notblueval'),
784             'notblueval'
785         )
786
787     def test_lower_items(self):
788         cid = CaseInsensitiveDict({
789             'Accept': 'application/json',
790             'user-Agent': 'requests',
791         })
792         keyset = frozenset(lowerkey for lowerkey, v in cid.lower_items())
793         lowerkeyset = frozenset(['accept', 'user-agent'])
794         self.assertEqual(keyset, lowerkeyset)
795
796     def test_preserve_key_case(self):
797         cid = CaseInsensitiveDict({
798             'Accept': 'application/json',
799             'user-Agent': 'requests',
800         })
801         keyset = frozenset(['Accept', 'user-Agent'])
802         self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
803         self.assertEqual(frozenset(cid.keys()), keyset)
804         self.assertEqual(frozenset(cid), keyset)
805
806     def test_preserve_last_key_case(self):
807         cid = CaseInsensitiveDict({
808             'Accept': 'application/json',
809             'user-Agent': 'requests',
810         })
811         cid.update({'ACCEPT': 'application/json'})
812         cid['USER-AGENT'] = 'requests'
813         keyset = frozenset(['ACCEPT', 'USER-AGENT'])
814         self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
815         self.assertEqual(frozenset(cid.keys()), keyset)
816         self.assertEqual(frozenset(cid), keyset)
817
818
819 if __name__ == '__main__':
820     unittest.main()