cosmopolitan/third_party/python/Lib/test/test_http_cookies.py
Justine Tunney 8af197560e Improve Libc by making Python work even better
Actually Portable Python is now outperforming the Python binaries
that come bundled with Linux distros, at things like HTTP serving.
You can now have a fully featured Python install in just one .com
file that runs on six operating systems and is about 10mb in size.
With tuning, the tiniest is ~1mb. We've got most of the libraries
working, including pysqlite, and the repl now feels very pleasant.
The things you can't do quite yet are: threads and shared objects
but that can happen in the future, if the community falls in love
with this project and wants to see it developed further. Changes:

- Add siginterrupt()
- Add sqlite3 to Python
- Add issymlink() helper
- Make GetZipCdir() faster
- Add tgamma() and finite()
- Add legacy function lutimes()
- Add readlink() and realpath()
- Use heap allocations when appropriate
- Reorganize Python into two-stage build
- Save Lua / Python shell history to dotfile
- Integrate Python Lib embedding into linkage
- Make isregularfile() and isdirectory() go faster
- Make Python shell auto-completion work perfectly
- Make crash reports work better if changed directory
- Fix Python+NT open() / access() flag overflow error
- Disable Python tests relating to \N{LONG NAME} syntax
- Have Python REPL copyright() show all notice embeddings

The biggest technical challenge at the moment is working around
when Python tries to be too clever about filenames.
2021-08-18 22:16:23 -07:00

483 lines
19 KiB
Python

# Simple test suite for http/cookies.py
import copy
from test.support import run_unittest, run_doctest, check_warnings
import unittest
from http import cookies
import pickle
import warnings
class CookieTests(unittest.TestCase):
def setUp(self):
self._warnings_manager = check_warnings()
self._warnings_manager.__enter__()
warnings.filterwarnings("ignore", ".* class is insecure.*",
DeprecationWarning)
def tearDown(self):
self._warnings_manager.__exit__(None, None, None)
def test_basic(self):
cases = [
{'data': 'chips=ahoy; vienna=finger',
'dict': {'chips':'ahoy', 'vienna':'finger'},
'repr': "<SimpleCookie: chips='ahoy' vienna='finger'>",
'output': 'Set-Cookie: chips=ahoy\nSet-Cookie: vienna=finger'},
{'data': 'keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"',
'dict': {'keebler' : 'E=mc2; L="Loves"; fudge=\012;'},
'repr': '''<SimpleCookie: keebler='E=mc2; L="Loves"; fudge=\\n;'>''',
'output': 'Set-Cookie: keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"'},
# Check illegal cookies that have an '=' char in an unquoted value
{'data': 'keebler=E=mc2',
'dict': {'keebler' : 'E=mc2'},
'repr': "<SimpleCookie: keebler='E=mc2'>",
'output': 'Set-Cookie: keebler=E=mc2'},
# Cookies with ':' character in their name. Though not mentioned in
# RFC, servers / browsers allow it.
{'data': 'key:term=value:term',
'dict': {'key:term' : 'value:term'},
'repr': "<SimpleCookie: key:term='value:term'>",
'output': 'Set-Cookie: key:term=value:term'},
# issue22931 - Adding '[' and ']' as valid characters in cookie
# values as defined in RFC 6265
{
'data': 'a=b; c=[; d=r; f=h',
'dict': {'a':'b', 'c':'[', 'd':'r', 'f':'h'},
'repr': "<SimpleCookie: a='b' c='[' d='r' f='h'>",
'output': '\n'.join((
'Set-Cookie: a=b',
'Set-Cookie: c=[',
'Set-Cookie: d=r',
'Set-Cookie: f=h'
))
}
]
for case in cases:
C = cookies.SimpleCookie()
C.load(case['data'])
self.assertEqual(repr(C), case['repr'])
self.assertEqual(C.output(sep='\n'), case['output'])
for k, v in sorted(case['dict'].items()):
self.assertEqual(C[k].value, v)
def test_load(self):
C = cookies.SimpleCookie()
C.load('Customer="WILE_E_COYOTE"; Version=1; Path=/acme')
self.assertEqual(C['Customer'].value, 'WILE_E_COYOTE')
self.assertEqual(C['Customer']['version'], '1')
self.assertEqual(C['Customer']['path'], '/acme')
self.assertEqual(C.output(['path']),
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
self.assertEqual(C.js_output(), r"""
<script type="text/javascript">
<!-- begin hiding
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
// end hiding -->
</script>
""")
self.assertEqual(C.js_output(['path']), r"""
<script type="text/javascript">
<!-- begin hiding
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
// end hiding -->
</script>
""")
def test_extended_encode(self):
# Issue 9824: some browsers don't follow the standard; we now
# encode , and ; to keep them from tripping up.
C = cookies.SimpleCookie()
C['val'] = "some,funky;stuff"
self.assertEqual(C.output(['val']),
'Set-Cookie: val="some\\054funky\\073stuff"')
def test_special_attrs(self):
# 'expires'
C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"')
C['Customer']['expires'] = 0
# can't test exact output, it always depends on current date/time
self.assertTrue(C.output().endswith('GMT'))
# loading 'expires'
C = cookies.SimpleCookie()
C.load('Customer="W"; expires=Wed, 01 Jan 2010 00:00:00 GMT')
self.assertEqual(C['Customer']['expires'],
'Wed, 01 Jan 2010 00:00:00 GMT')
C = cookies.SimpleCookie()
C.load('Customer="W"; expires=Wed, 01 Jan 98 00:00:00 GMT')
self.assertEqual(C['Customer']['expires'],
'Wed, 01 Jan 98 00:00:00 GMT')
# 'max-age'
C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"')
C['Customer']['max-age'] = 10
self.assertEqual(C.output(),
'Set-Cookie: Customer="WILE_E_COYOTE"; Max-Age=10')
def test_set_secure_httponly_attrs(self):
C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"')
C['Customer']['secure'] = True
C['Customer']['httponly'] = True
self.assertEqual(C.output(),
'Set-Cookie: Customer="WILE_E_COYOTE"; HttpOnly; Secure')
def test_secure_httponly_false_if_not_present(self):
C = cookies.SimpleCookie()
C.load('eggs=scrambled; Path=/bacon')
self.assertFalse(C['eggs']['httponly'])
self.assertFalse(C['eggs']['secure'])
def test_secure_httponly_true_if_present(self):
# Issue 16611
C = cookies.SimpleCookie()
C.load('eggs=scrambled; httponly; secure; Path=/bacon')
self.assertTrue(C['eggs']['httponly'])
self.assertTrue(C['eggs']['secure'])
def test_secure_httponly_true_if_have_value(self):
# This isn't really valid, but demonstrates what the current code
# is expected to do in this case.
C = cookies.SimpleCookie()
C.load('eggs=scrambled; httponly=foo; secure=bar; Path=/bacon')
self.assertTrue(C['eggs']['httponly'])
self.assertTrue(C['eggs']['secure'])
# Here is what it actually does; don't depend on this behavior. These
# checks are testing backward compatibility for issue 16611.
self.assertEqual(C['eggs']['httponly'], 'foo')
self.assertEqual(C['eggs']['secure'], 'bar')
def test_extra_spaces(self):
C = cookies.SimpleCookie()
C.load('eggs = scrambled ; secure ; path = bar ; foo=foo ')
self.assertEqual(C.output(),
'Set-Cookie: eggs=scrambled; Path=bar; Secure\r\nSet-Cookie: foo=foo')
def test_quoted_meta(self):
# Try cookie with quoted meta-data
C = cookies.SimpleCookie()
C.load('Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"')
self.assertEqual(C['Customer'].value, 'WILE_E_COYOTE')
self.assertEqual(C['Customer']['version'], '1')
self.assertEqual(C['Customer']['path'], '/acme')
self.assertEqual(C.output(['path']),
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
self.assertEqual(C.js_output(), r"""
<script type="text/javascript">
<!-- begin hiding
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1";
// end hiding -->
</script>
""")
self.assertEqual(C.js_output(['path']), r"""
<script type="text/javascript">
<!-- begin hiding
document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme";
// end hiding -->
</script>
""")
def test_invalid_cookies(self):
# Accepting these could be a security issue
C = cookies.SimpleCookie()
for s in (']foo=x', '[foo=x', 'blah]foo=x', 'blah[foo=x',
'Set-Cookie: foo=bar', 'Set-Cookie: foo',
'foo=bar; baz', 'baz; foo=bar',
'secure;foo=bar', 'Version=1;foo=bar'):
C.load(s)
self.assertEqual(dict(C), {})
self.assertEqual(C.output(), '')
def test_pickle(self):
rawdata = 'Customer="WILE_E_COYOTE"; Path=/acme; Version=1'
expected_output = 'Set-Cookie: %s' % rawdata
C = cookies.SimpleCookie()
C.load(rawdata)
self.assertEqual(C.output(), expected_output)
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(proto=proto):
C1 = pickle.loads(pickle.dumps(C, protocol=proto))
self.assertEqual(C1.output(), expected_output)
def test_illegal_chars(self):
rawdata = "a=b; c,d=e"
C = cookies.SimpleCookie()
with self.assertRaises(cookies.CookieError):
C.load(rawdata)
# # TODO(jart): pycomp.com needs \N thing
# def test_comment_quoting(self):
# c = cookies.SimpleCookie()
# c['foo'] = '\N{COPYRIGHT SIGN}'
# self.assertEqual(str(c['foo']), 'Set-Cookie: foo="\\251"')
# c['foo']['comment'] = 'comment \N{COPYRIGHT SIGN}'
# self.assertEqual(
# str(c['foo']),
# 'Set-Cookie: foo="\\251"; Comment="comment \\251"'
# )
class MorselTests(unittest.TestCase):
"""Tests for the Morsel object."""
def test_defaults(self):
morsel = cookies.Morsel()
self.assertIsNone(morsel.key)
self.assertIsNone(morsel.value)
self.assertIsNone(morsel.coded_value)
self.assertEqual(morsel.keys(), cookies.Morsel._reserved.keys())
for key, val in morsel.items():
self.assertEqual(val, '', key)
def test_reserved_keys(self):
M = cookies.Morsel()
# tests valid and invalid reserved keys for Morsels
for i in M._reserved:
# Test that all valid keys are reported as reserved and set them
self.assertTrue(M.isReservedKey(i))
M[i] = '%s_value' % i
for i in M._reserved:
# Test that valid key values come out fine
self.assertEqual(M[i], '%s_value' % i)
for i in "the holy hand grenade".split():
# Test that invalid keys raise CookieError
self.assertRaises(cookies.CookieError,
M.__setitem__, i, '%s_value' % i)
def test_setter(self):
M = cookies.Morsel()
# tests the .set method to set keys and their values
for i in M._reserved:
# Makes sure that all reserved keys can't be set this way
self.assertRaises(cookies.CookieError,
M.set, i, '%s_value' % i, '%s_value' % i)
for i in "thou cast _the- !holy! ^hand| +*grenade~".split():
# Try typical use case. Setting decent values.
# Check output and js_output.
M['path'] = '/foo' # Try a reserved key as well
M.set(i, "%s_val" % i, "%s_coded_val" % i)
self.assertEqual(
M.output(),
"Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i))
expected_js_output = """
<script type="text/javascript">
<!-- begin hiding
document.cookie = "%s=%s; Path=/foo";
// end hiding -->
</script>
""" % (i, "%s_coded_val" % i)
self.assertEqual(M.js_output(), expected_js_output)
for i in ["foo bar", "foo@bar"]:
# Try some illegal characters
self.assertRaises(cookies.CookieError,
M.set, i, '%s_value' % i, '%s_value' % i)
def test_deprecation(self):
morsel = cookies.Morsel()
with self.assertWarnsRegex(DeprecationWarning, r'\bkey\b'):
morsel.key = ''
with self.assertWarnsRegex(DeprecationWarning, r'\bvalue\b'):
morsel.value = ''
with self.assertWarnsRegex(DeprecationWarning, r'\bcoded_value\b'):
morsel.coded_value = ''
with self.assertWarnsRegex(DeprecationWarning, r'\bLegalChars\b'):
morsel.set('key', 'value', 'coded_value', LegalChars='.*')
def test_eq(self):
base_case = ('key', 'value', '"value"')
attribs = {
'path': '/',
'comment': 'foo',
'domain': 'example.com',
'version': 2,
}
morsel_a = cookies.Morsel()
morsel_a.update(attribs)
morsel_a.set(*base_case)
morsel_b = cookies.Morsel()
morsel_b.update(attribs)
morsel_b.set(*base_case)
self.assertTrue(morsel_a == morsel_b)
self.assertFalse(morsel_a != morsel_b)
cases = (
('key', 'value', 'mismatch'),
('key', 'mismatch', '"value"'),
('mismatch', 'value', '"value"'),
)
for case_b in cases:
with self.subTest(case_b):
morsel_b = cookies.Morsel()
morsel_b.update(attribs)
morsel_b.set(*case_b)
self.assertFalse(morsel_a == morsel_b)
self.assertTrue(morsel_a != morsel_b)
morsel_b = cookies.Morsel()
morsel_b.update(attribs)
morsel_b.set(*base_case)
morsel_b['comment'] = 'bar'
self.assertFalse(morsel_a == morsel_b)
self.assertTrue(morsel_a != morsel_b)
# test mismatched types
self.assertFalse(cookies.Morsel() == 1)
self.assertTrue(cookies.Morsel() != 1)
self.assertFalse(cookies.Morsel() == '')
self.assertTrue(cookies.Morsel() != '')
items = list(cookies.Morsel().items())
self.assertFalse(cookies.Morsel() == items)
self.assertTrue(cookies.Morsel() != items)
# morsel/dict
morsel = cookies.Morsel()
morsel.set(*base_case)
morsel.update(attribs)
self.assertTrue(morsel == dict(morsel))
self.assertFalse(morsel != dict(morsel))
def test_copy(self):
morsel_a = cookies.Morsel()
morsel_a.set('foo', 'bar', 'baz')
morsel_a.update({
'version': 2,
'comment': 'foo',
})
morsel_b = morsel_a.copy()
self.assertIsInstance(morsel_b, cookies.Morsel)
self.assertIsNot(morsel_a, morsel_b)
self.assertEqual(morsel_a, morsel_b)
morsel_b = copy.copy(morsel_a)
self.assertIsInstance(morsel_b, cookies.Morsel)
self.assertIsNot(morsel_a, morsel_b)
self.assertEqual(morsel_a, morsel_b)
def test_setitem(self):
morsel = cookies.Morsel()
morsel['expires'] = 0
self.assertEqual(morsel['expires'], 0)
morsel['Version'] = 2
self.assertEqual(morsel['version'], 2)
morsel['DOMAIN'] = 'example.com'
self.assertEqual(morsel['domain'], 'example.com')
with self.assertRaises(cookies.CookieError):
morsel['invalid'] = 'value'
self.assertNotIn('invalid', morsel)
def test_setdefault(self):
morsel = cookies.Morsel()
morsel.update({
'domain': 'example.com',
'version': 2,
})
# this shouldn't override the default value
self.assertEqual(morsel.setdefault('expires', 'value'), '')
self.assertEqual(morsel['expires'], '')
self.assertEqual(morsel.setdefault('Version', 1), 2)
self.assertEqual(morsel['version'], 2)
self.assertEqual(morsel.setdefault('DOMAIN', 'value'), 'example.com')
self.assertEqual(morsel['domain'], 'example.com')
with self.assertRaises(cookies.CookieError):
morsel.setdefault('invalid', 'value')
self.assertNotIn('invalid', morsel)
def test_update(self):
attribs = {'expires': 1, 'Version': 2, 'DOMAIN': 'example.com'}
# test dict update
morsel = cookies.Morsel()
morsel.update(attribs)
self.assertEqual(morsel['expires'], 1)
self.assertEqual(morsel['version'], 2)
self.assertEqual(morsel['domain'], 'example.com')
# test iterable update
morsel = cookies.Morsel()
morsel.update(list(attribs.items()))
self.assertEqual(morsel['expires'], 1)
self.assertEqual(morsel['version'], 2)
self.assertEqual(morsel['domain'], 'example.com')
# test iterator update
morsel = cookies.Morsel()
morsel.update((k, v) for k, v in attribs.items())
self.assertEqual(morsel['expires'], 1)
self.assertEqual(morsel['version'], 2)
self.assertEqual(morsel['domain'], 'example.com')
with self.assertRaises(cookies.CookieError):
morsel.update({'invalid': 'value'})
self.assertNotIn('invalid', morsel)
self.assertRaises(TypeError, morsel.update)
self.assertRaises(TypeError, morsel.update, 0)
def test_pickle(self):
morsel_a = cookies.Morsel()
morsel_a.set('foo', 'bar', 'baz')
morsel_a.update({
'version': 2,
'comment': 'foo',
})
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(proto=proto):
morsel_b = pickle.loads(pickle.dumps(morsel_a, proto))
self.assertIsInstance(morsel_b, cookies.Morsel)
self.assertEqual(morsel_b, morsel_a)
self.assertEqual(str(morsel_b), str(morsel_a))
def test_repr(self):
morsel = cookies.Morsel()
self.assertEqual(repr(morsel), '<Morsel: None=None>')
self.assertEqual(str(morsel), 'Set-Cookie: None=None')
morsel.set('key', 'val', 'coded_val')
self.assertEqual(repr(morsel), '<Morsel: key=coded_val>')
self.assertEqual(str(morsel), 'Set-Cookie: key=coded_val')
morsel.update({
'path': '/',
'comment': 'foo',
'domain': 'example.com',
'max-age': 0,
'secure': 0,
'version': 1,
})
self.assertEqual(repr(morsel),
'<Morsel: key=coded_val; Comment=foo; Domain=example.com; '
'Max-Age=0; Path=/; Version=1>')
self.assertEqual(str(morsel),
'Set-Cookie: key=coded_val; Comment=foo; Domain=example.com; '
'Max-Age=0; Path=/; Version=1')
morsel['secure'] = True
morsel['httponly'] = 1
self.assertEqual(repr(morsel),
'<Morsel: key=coded_val; Comment=foo; Domain=example.com; '
'HttpOnly; Max-Age=0; Path=/; Secure; Version=1>')
self.assertEqual(str(morsel),
'Set-Cookie: key=coded_val; Comment=foo; Domain=example.com; '
'HttpOnly; Max-Age=0; Path=/; Secure; Version=1')
morsel = cookies.Morsel()
morsel.set('key', 'val', 'coded_val')
morsel['expires'] = 0
self.assertRegex(repr(morsel),
r'<Morsel: key=coded_val; '
r'expires=\w+, \d+ \w+ \d+ \d+:\d+:\d+ \w+>')
self.assertRegex(str(morsel),
r'Set-Cookie: key=coded_val; '
r'expires=\w+, \d+ \w+ \d+ \d+:\d+:\d+ \w+')
def test_main():
run_unittest(CookieTests, MorselTests)
run_doctest(cookies)
if __name__ == '__main__':
test_main()