5
from contextlib import contextmanager
7
from copy import deepcopy
11
from werkzeug import datastructures as ds
12
from werkzeug import http
13
from werkzeug.exceptions import BadRequestKeyError
16
class TestNativeItermethods:
19
def keys(self, multi=1):
20
return iter(["a", "b", "c"] * multi)
22
def values(self, multi=1):
23
return iter([1, 2, 3] * multi)
25
def items(self, multi=1):
27
zip(iter(self.keys(multi=multi)), iter(self.values(multi=multi)))
31
expected_keys = ["a", "b", "c"]
32
expected_values = [1, 2, 3]
33
expected_items = list(zip(expected_keys, expected_values))
35
assert list(d.keys()) == expected_keys
36
assert list(d.values()) == expected_values
37
assert list(d.items()) == expected_items
39
assert list(d.keys(2)) == expected_keys * 2
40
assert list(d.values(2)) == expected_values * 2
41
assert list(d.items(2)) == expected_items * 2
44
class _MutableMultiDictTests:
45
storage_class: t.Type["ds.MultiDict"]
47
def test_pickle(self):
48
cls = self.storage_class
50
def create_instance(module=None):
55
cls.__module__ = module
58
d.setlist(b"foo", [1, 2, 3, 4])
59
d.setlist(b"bar", b"foo bar baz".split())
62
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
64
s = pickle.dumps(d, protocol)
66
assert type(ud) == type(d)
68
alternative = pickle.dumps(create_instance("werkzeug"), protocol)
69
assert pickle.loads(alternative) == d
70
ud[b"newkey"] = b"bla"
73
def test_multidict_dict_interop(self):
75
md = self.storage_class([("a", 1), ("a", 2)])
76
assert dict(md)["a"] != [1, 2]
77
assert dict(md)["a"] == 1
78
assert dict(md) == {**md} == {"a": 1}
80
def test_basic_interface(self):
81
md = self.storage_class()
82
assert isinstance(md, dict)
94
md = self.storage_class(mapping)
99
with pytest.raises(KeyError):
101
assert md.get("a") == 1
104
assert md.getlist("a") == [1, 2, 1, 3]
105
assert md.getlist("d") == [3, 4]
107
assert md.getlist("x") == []
111
assert md.getlist("a") == [42]
114
md.setlist("a", [1, 2, 3])
116
assert md.getlist("a") == [1, 2, 3]
125
assert md.setdefault("u", 23) == 23
126
assert md.getlist("u") == [23]
129
md.setlist("u", [-1, -2])
133
with pytest.raises(KeyError):
136
assert md.getlist("d") == []
139
assert list(sorted(md.keys())) == ["a", "b", "c"]
140
assert list(sorted(md.keys())) == ["a", "b", "c"]
142
assert list(sorted(md.values())) == [1, 2, 3]
143
assert list(sorted(md.values())) == [1, 2, 3]
145
assert list(sorted(md.items())) == [("a", 1), ("b", 2), ("c", 3)]
146
assert list(sorted(md.items(multi=True))) == [
153
assert list(sorted(md.items())) == [("a", 1), ("b", 2), ("c", 3)]
154
assert list(sorted(md.items(multi=True))) == [
162
assert list(sorted(md.lists())) == [("a", [1, 2, 3]), ("b", [2]), ("c", [3])]
163
assert list(sorted(md.lists())) == [("a", [1, 2, 3]), ("b", [2]), ("c", [3])]
168
assert c.getlist("a") == [1, 2, 3]
173
assert c.getlist("a") == [1, 2, 3]
178
assert c.getlist("a") == [1, 2, 3]
183
assert c.getlist("a") == [1, 2, 3]
186
od = self.storage_class([("a", 4), ("a", 5), ("y", 0)])
188
assert md.getlist("a") == [1, 2, 3, 4, 5]
189
assert md.getlist("y") == [0]
193
od = {"a": 4, "y": 0}
195
assert md.getlist("a") == [1, 2, 3, 4]
196
assert md.getlist("y") == [0]
199
assert md.pop("y") == 0
201
assert md.poplist("a") == [1, 2, 3, 4]
203
assert md.poplist("missing") == []
206
popped = md.popitem()
207
assert popped in [("b", 2), ("c", 3)]
208
popped = md.popitemlist()
209
assert popped in [("b", [2]), ("c", [3])]
212
md = self.storage_class({"a": "4", "b": ["2", "3"]})
213
assert md.get("a", type=int) == 4
214
assert md.getlist("b", type=int) == [2, 3]
217
md = self.storage_class([("a", 1), ("a", 2), ("b", 3)])
218
assert "('a', 1)" in repr(md)
219
assert "('a', 2)" in repr(md)
220
assert "('b', 3)" in repr(md)
225
assert md.getlist("c") == ["42", "23"]
227
assert md.getlist("c", type=int) == [42, 23]
230
md = self.storage_class()
231
md.setdefault("x", []).append(42)
232
md.setdefault("x", []).append(23)
233
assert md["x"] == [42, 23]
236
md = self.storage_class()
240
assert md.to_dict() == {"foo": 42, "bar": 1}
241
assert md.to_dict(flat=False) == {"foo": [42], "bar": [1, 2]}
244
with pytest.raises(KeyError):
245
self.storage_class().popitem()
247
with pytest.raises(KeyError):
248
self.storage_class().popitemlist()
251
with pytest.raises(BadRequestKeyError):
252
self.storage_class()[42]
255
md = self.storage_class()
257
md.setlist("foo", [1, 2])
258
assert md.getlist("foo") == [1, 2]
261
class _ImmutableDictTests:
262
storage_class: t.Type[dict]
264
def test_follows_dict_interface(self):
265
cls = self.storage_class
267
data = {"foo": 1, "bar": 2, "baz": 3}
273
assert sorted(d.keys()) == ["bar", "baz", "foo"]
275
assert "foox" not in d
278
def test_copies_are_mutable(self):
279
cls = self.storage_class
280
immutable = cls({"a": 1})
281
with pytest.raises(TypeError):
284
mutable = immutable.copy()
286
assert "a" in immutable
287
assert mutable is not immutable
288
assert copy(immutable) is immutable
290
def test_dict_is_hashable(self):
291
cls = self.storage_class
292
immutable = cls({"a": 1, "b": 2})
293
immutable2 = cls({"a": 2, "b": 2})
295
assert immutable in x
296
assert immutable2 not in x
298
assert immutable not in x
299
assert immutable2 not in x
301
assert immutable not in x
302
assert immutable2 in x
304
assert immutable in x
305
assert immutable2 in x
308
class TestImmutableTypeConversionDict(_ImmutableDictTests):
309
storage_class = ds.ImmutableTypeConversionDict
312
class TestImmutableMultiDict(_ImmutableDictTests):
313
storage_class = ds.ImmutableMultiDict
315
def test_multidict_is_hashable(self):
316
cls = self.storage_class
317
immutable = cls({"a": [1, 2], "b": 2})
318
immutable2 = cls({"a": [1], "b": 2})
320
assert immutable in x
321
assert immutable2 not in x
323
assert immutable not in x
324
assert immutable2 not in x
326
assert immutable not in x
327
assert immutable2 in x
329
assert immutable in x
330
assert immutable2 in x
333
class TestImmutableDict(_ImmutableDictTests):
334
storage_class = ds.ImmutableDict
337
class TestImmutableOrderedMultiDict(_ImmutableDictTests):
338
storage_class = ds.ImmutableOrderedMultiDict
340
def test_ordered_multidict_is_hashable(self):
341
a = self.storage_class([("a", 1), ("b", 1), ("a", 2)])
342
b = self.storage_class([("a", 1), ("a", 2), ("b", 1)])
343
assert hash(a) != hash(b)
346
class TestMultiDict(_MutableMultiDictTests):
347
storage_class = ds.MultiDict
349
def test_multidict_pop(self):
351
return self.storage_class({"foo": [1, 2, 3, 4]})
354
assert d.pop("foo") == 1
357
assert d.pop("foo", 32) == 1
360
assert d.pop("foos", 32) == 32
363
with pytest.raises(KeyError):
366
def test_multidict_pop_raise_badrequestkeyerror_for_empty_list_value(self):
367
mapping = [("a", "b"), ("a", "c")]
368
md = self.storage_class(mapping)
370
md.setlistdefault("empty", [])
372
with pytest.raises(KeyError):
375
def test_multidict_popitem_raise_badrequestkeyerror_for_empty_list_value(self):
377
md = self.storage_class(mapping)
379
md.setlistdefault("empty", [])
381
with pytest.raises(BadRequestKeyError):
384
def test_setlistdefault(self):
385
md = self.storage_class()
386
assert md.setlistdefault("u", [-1, -2]) == [-1, -2]
387
assert md.getlist("u") == [-1, -2]
390
def test_iter_interfaces(self):
401
md = self.storage_class(mapping)
402
assert list(zip(md.keys(), md.listvalues())) == list(md.lists())
403
assert list(zip(md, md.listvalues())) == list(md.lists())
404
assert list(zip(md.keys(), md.listvalues())) == list(md.lists())
406
def test_getitem_raise_badrequestkeyerror_for_empty_list_value(self):
407
mapping = [("a", "b"), ("a", "c")]
408
md = self.storage_class(mapping)
410
md.setlistdefault("empty", [])
412
with pytest.raises(KeyError):
416
class TestOrderedMultiDict(_MutableMultiDictTests):
417
storage_class = ds.OrderedMultiDict
419
def test_ordered_interface(self):
420
cls = self.storage_class
428
assert list(d.items()) == [("foo", "bar")]
429
assert list(d) == ["foo"]
430
assert list(d.items(multi=True)) == [("foo", "bar"), ("foo", "baz")]
436
d.update([("foo", 1), ("foo", 2), ("bar", 42)])
438
assert d.getlist("foo") == [1, 2, 3]
439
assert d.getlist("bar") == [42]
440
assert list(d.items()) == [("foo", 1), ("bar", 42)]
442
expected = ["foo", "bar"]
444
assert list(d.keys()) == expected
445
assert list(d) == expected
446
assert list(d.keys()) == expected
448
assert list(d.items(multi=True)) == [
456
assert d.pop("foo") == 1
457
assert d.pop("blafasel", None) is None
458
assert d.pop("blafasel", 42) == 42
460
assert d.poplist("bar") == [42]
463
assert d.get("missingkey") is None
469
assert d == ds.MultiDict(d)
470
id = self.storage_class(d)
475
d.update({"blah": [1, 2, 3]})
476
assert d["blah"] == 1
477
assert d.getlist("blah") == [1, 2, 3]
480
d = self.storage_class()
482
d.setlist("foo", [1, 2])
483
assert d.getlist("foo") == [1, 2]
484
with pytest.raises(BadRequestKeyError):
487
with pytest.raises(BadRequestKeyError):
491
d = self.storage_class()
495
assert d.popitem() == ("foo", 23)
496
with pytest.raises(BadRequestKeyError):
503
assert d.popitemlist() == ("foo", [23, 42, 1])
505
with pytest.raises(BadRequestKeyError):
509
d = self.storage_class()
511
pytest.raises(TypeError, hash, d)
513
def test_iterables(self):
514
a = ds.MultiDict((("key_a", "value_a"),))
515
b = ds.MultiDict((("key_b", "value_b"),))
516
ab = ds.CombinedMultiDict((a, b))
518
assert sorted(ab.lists()) == [("key_a", ["value_a"]), ("key_b", ["value_b"])]
519
assert sorted(ab.listvalues()) == [["value_a"], ["value_b"]]
520
assert sorted(ab.keys()) == ["key_a", "key_b"]
522
assert sorted(ab.lists()) == [("key_a", ["value_a"]), ("key_b", ["value_b"])]
523
assert sorted(ab.listvalues()) == [["value_a"], ["value_b"]]
524
assert sorted(ab.keys()) == ["key_a", "key_b"]
526
def test_get_description(self):
527
data = ds.OrderedMultiDict()
529
with pytest.raises(BadRequestKeyError) as exc_info:
532
assert "baz" not in exc_info.value.get_description()
533
exc_info.value.show_exception = True
534
assert "baz" in exc_info.value.get_description()
536
with pytest.raises(BadRequestKeyError) as exc_info:
539
exc_info.value.show_exception = True
540
assert "baz" in exc_info.value.get_description()
541
exc_info.value.args = ()
542
assert "baz" not in exc_info.value.get_description()
545
class TestTypeConversionDict:
546
storage_class = ds.TypeConversionDict
548
def test_value_conversion(self):
549
d = self.storage_class(foo="1")
550
assert d.get("foo", type=int) == 1
552
def test_return_default_when_conversion_is_not_possible(self):
553
d = self.storage_class(foo="bar", baz=None)
554
assert d.get("foo", default=-1, type=int) == -1
555
assert d.get("baz", default=-1, type=int) == -1
557
def test_propagate_exceptions_in_conversion(self):
558
d = self.storage_class(foo="bar")
560
with pytest.raises(KeyError):
561
d.get("foo", type=lambda x: switch[x])
564
class TestCombinedMultiDict:
565
storage_class = ds.CombinedMultiDict
567
def test_basic_interface(self):
568
d1 = ds.MultiDict([("foo", "1")])
569
d2 = ds.MultiDict([("bar", "2"), ("bar", "3")])
570
d = self.storage_class([d1, d2])
573
assert d["foo"] == "1"
574
assert d["bar"] == "2"
575
assert d.getlist("bar") == ["2", "3"]
577
assert sorted(d.items()) == [("bar", "2"), ("foo", "1")]
578
assert sorted(d.items(multi=True)) == [("bar", "2"), ("bar", "3"), ("foo", "1")]
579
assert "missingkey" not in d
583
assert d.get("foo", type=int) == 1
584
assert d.getlist("bar", type=int) == [2, 3]
587
with pytest.raises(KeyError):
591
with pytest.raises(TypeError):
599
md1 = ds.MultiDict((("foo", "bar"), ("foo", "baz")))
600
md2 = ds.MultiDict((("foo", "blafasel"),))
601
x = self.storage_class((md1, md2))
602
assert list(x.lists()) == [("foo", ["bar", "baz", "blafasel"])]
605
assert x.to_dict() == {"foo": "bar"}
606
assert x.to_dict(flat=False) == {"foo": ["bar", "baz", "blafasel"]}
608
def test_length(self):
609
d1 = ds.MultiDict([("foo", "1")])
610
d2 = ds.MultiDict([("bar", "2")])
611
assert len(d1) == len(d2) == 1
612
d = self.storage_class([d1, d2])
620
storage_class = ds.Headers
622
def test_basic_interface(self):
623
headers = self.storage_class()
624
headers.add("Content-Type", "text/plain")
625
headers.add("X-Foo", "bar")
626
assert "x-Foo" in headers
627
assert "Content-type" in headers
629
with pytest.raises(ValueError):
630
headers.add("X-Example", "foo\r\n bar")
632
headers["Content-Type"] = "foo/bar"
633
assert headers["Content-Type"] == "foo/bar"
634
assert len(headers.getlist("Content-Type")) == 1
637
assert headers.to_wsgi_list() == [("Content-Type", "foo/bar"), ("X-Foo", "bar")]
638
assert str(headers) == "Content-Type: foo/bar\r\nX-Foo: bar\r\n\r\n"
639
assert str(self.storage_class()) == "\r\n"
642
headers.add("Content-Disposition", "attachment", filename="foo")
643
assert headers["Content-Disposition"] == "attachment; filename=foo"
645
headers.add("x", "y", z='"')
646
assert headers["x"] == r'y; z="\""'
650
assert headers["a"] == "1"
652
def test_defaults_and_conversion(self):
654
headers = self.storage_class(
656
("Content-Type", "text/plain"),
662
assert headers.getlist("x-bar") == ["1", "2"]
663
assert headers.get("x-Bar") == "1"
664
assert headers.get("Content-Type") == "text/plain"
666
assert headers.setdefault("X-Foo", "nope") == "bar"
667
assert headers.setdefault("X-Bar", "nope") == "1"
668
assert headers.setdefault("X-Baz", "quux") == "quux"
669
assert headers.setdefault("X-Baz", "nope") == "quux"
673
with pytest.raises(ValueError):
674
self.storage_class([("X-Example", "foo\r\n bar")])
677
assert headers.get("x-bar", type=int) == 1
678
assert headers.getlist("x-bar", type=int) == [1, 2]
681
assert headers[0] == ("Content-Type", "text/plain")
682
assert headers[:1] == self.storage_class([("Content-Type", "text/plain")])
685
assert headers == self.storage_class([("X-Bar", "1")])
687
def test_copying(self):
688
a = self.storage_class([("foo", "bar")])
691
assert a.getlist("foo") == ["bar", "baz"]
692
assert b.getlist("foo") == ["bar"]
694
def test_popping(self):
695
headers = self.storage_class([("a", 1)])
698
assert headers.pop("a") == "1"
699
assert headers.pop("b", "2") == "2"
701
with pytest.raises(KeyError):
704
def test_set_arguments(self):
705
a = self.storage_class()
706
a.set("Content-Disposition", "useless")
707
a.set("Content-Disposition", "attachment", filename="foo")
708
assert a["Content-Disposition"] == "attachment; filename=foo"
710
def test_reject_newlines(self):
711
h = self.storage_class()
713
for variation in "foo\nbar", "foo\r\nbar", "foo\rbar":
714
with pytest.raises(ValueError):
716
with pytest.raises(ValueError):
717
h.add("foo", variation)
718
with pytest.raises(ValueError):
719
h.add("foo", "test", option=variation)
720
with pytest.raises(ValueError):
721
h.set("foo", variation)
722
with pytest.raises(ValueError):
723
h.set("foo", "test", option=variation)
725
def test_slicing(self):
728
h = self.storage_class()
729
h.set("X-Foo-Poo", "bleh")
730
h.set("Content-Type", "application/whocares")
731
h.set("X-Forwarded-For", "192.168.0.123")
732
h[:] = [(k, v) for k, v in h if k.startswith("X-")]
733
assert list(h) == [("X-Foo-Poo", "bleh"), ("X-Forwarded-For", "192.168.0.123")]
735
def test_extend(self):
736
h = self.storage_class([("a", "0"), ("b", "1"), ("c", "2")])
737
h.extend(ds.Headers([("a", "3"), ("a", "4")]))
738
assert h.getlist("a") == ["0", "3", "4"]
739
h.extend(b=["5", "6"])
740
assert h.getlist("b") == ["1", "5", "6"]
741
h.extend({"c": "7", "d": ["8", "9"]}, c="10")
742
assert h.getlist("c") == ["2", "7", "10"]
743
assert h.getlist("d") == ["8", "9"]
745
with pytest.raises(TypeError):
746
h.extend({"x": "x"}, {"x": "x"})
748
def test_update(self):
749
h = self.storage_class([("a", "0"), ("b", "1"), ("c", "2")])
750
h.update(ds.Headers([("a", "3"), ("a", "4")]))
751
assert h.getlist("a") == ["3", "4"]
752
h.update(b=["5", "6"])
753
assert h.getlist("b") == ["5", "6"]
754
h.update({"c": "7", "d": ["8", "9"]})
755
assert h.getlist("c") == ["7"]
756
assert h.getlist("d") == ["8", "9"]
757
h.update({"c": "10"}, c="11")
758
assert h.getlist("c") == ["11"]
760
with pytest.raises(TypeError):
761
h.extend({"x": "x"}, {"x": "x"})
763
def test_setlist(self):
764
h = self.storage_class([("a", "0"), ("b", "1"), ("c", "2")])
765
h.setlist("b", ["3", "4"])
766
assert h[1] == ("b", "3")
767
assert h[-1] == ("b", "4")
770
h.setlist("d", ["5"])
773
def test_setlistdefault(self):
774
h = self.storage_class([("a", "0"), ("b", "1"), ("c", "2")])
775
assert h.setlistdefault("a", ["3"]) == ["0"]
776
assert h.setlistdefault("d", ["4", "5"]) == ["4", "5"]
778
def test_to_wsgi_list(self):
779
h = self.storage_class()
780
h.set("Key", "Value")
781
for key, value in h.to_wsgi_list():
783
assert value == "Value"
785
def test_equality(self):
787
h1 = self.storage_class()
788
h1.add("X-Foo", "foo")
789
h1.add("X-Bar", "bah")
790
h1.add("X-Bar", "humbug")
792
h2 = self.storage_class()
793
h2.add("x-foo", "foo")
794
h2.add("x-bar", "bah")
795
h2.add("x-bar", "humbug")
800
class TestEnvironHeaders:
801
storage_class = ds.EnvironHeaders
803
def test_basic_interface(self):
807
"HTTP_CONTENT_TYPE": "text/html",
808
"CONTENT_TYPE": "text/html",
809
"HTTP_CONTENT_LENGTH": "0",
810
"CONTENT_LENGTH": "0",
812
"wsgi.version": (1, 0),
814
headers = self.storage_class(broken_env)
816
assert len(headers) == 3
817
assert sorted(headers) == [
819
("Content-Length", "0"),
820
("Content-Type", "text/html"),
822
assert not self.storage_class({"wsgi.version": (1, 0)})
823
assert len(self.storage_class({"wsgi.version": (1, 0)})) == 0
824
assert 42 not in headers
826
def test_skip_empty_special_vars(self):
827
env = {"HTTP_X_FOO": "42", "CONTENT_TYPE": "", "CONTENT_LENGTH": ""}
828
headers = self.storage_class(env)
829
assert dict(headers) == {"X-Foo": "42"}
831
env = {"HTTP_X_FOO": "42", "CONTENT_TYPE": "", "CONTENT_LENGTH": "0"}
832
headers = self.storage_class(env)
833
assert dict(headers) == {"X-Foo": "42", "Content-Length": "0"}
835
def test_return_type_is_str(self):
836
headers = self.storage_class({"HTTP_FOO": "\xe2\x9c\x93"})
837
assert headers["Foo"] == "\xe2\x9c\x93"
838
assert next(iter(headers)) == ("Foo", "\xe2\x9c\x93")
842
storage_class = ds.HeaderSet
844
def test_basic_interface(self):
845
hs = self.storage_class()
849
assert hs.find("foo") == 0
850
assert hs.find("BAR") == 1
851
assert hs.find("baz") < 0
852
hs.discard("missing")
854
assert hs.find("foo") < 0
855
assert hs.find("bar") == 0
857
with pytest.raises(IndexError):
860
assert hs.index("bar") == 0
866
class TestImmutableList:
867
storage_class = ds.ImmutableList
869
def test_list_hashable(self):
871
store = self.storage_class(data)
872
assert hash(data) == hash(store)
876
def make_call_asserter(func=None):
877
"""Utility to assert a certain number of function calls.
879
:param func: Additional callback for each function call.
881
.. code-block:: python
882
assert_calls, func = make_call_asserter()
883
with assert_calls(2):
890
def asserter(count, msg=None):
893
assert calls[0] == count
895
def wrapped(*args, **kwargs):
898
return func(*args, **kwargs)
900
return asserter, wrapped
903
class TestCallbackDict:
904
storage_class = ds.CallbackDict
906
def test_callback_dict_reads(self):
907
assert_calls, func = make_call_asserter()
908
initial = {"a": "foo", "b": "bar"}
909
dct = self.storage_class(initial=initial, on_update=func)
910
with assert_calls(0, "callback triggered by read-only method"):
914
pytest.raises(KeyError, lambda: dct["x"])
918
with assert_calls(0, "callback triggered without modification"):
923
def test_callback_dict_writes(self):
924
assert_calls, func = make_call_asserter()
925
initial = {"a": "foo", "b": "bar"}
926
dct = self.storage_class(initial=initial, on_update=func)
927
with assert_calls(8, "callback not triggered by write method"):
937
with assert_calls(0, "callback triggered by failed del"):
938
pytest.raises(KeyError, lambda: dct.__delitem__("x"))
939
with assert_calls(0, "callback triggered by failed pop"):
940
pytest.raises(KeyError, lambda: dct.pop("x"))
943
class TestCacheControl:
945
cc = ds.RequestCacheControl([("max-age", "0"), ("private", "True")])
946
assert repr(cc) == "<RequestCacheControl max-age='0' private='True'>"
948
def test_set_none(self):
949
cc = ds.ResponseCacheControl([("max-age", "0")])
950
assert cc.no_cache is None
952
assert cc.no_cache is None
954
assert cc.no_cache is False
956
def test_no_transform(self):
957
cc = ds.RequestCacheControl([("no-transform", None)])
958
assert cc.no_transform is True
959
cc = ds.RequestCacheControl()
960
assert cc.no_transform is False
962
def test_min_fresh(self):
963
cc = ds.RequestCacheControl([("min-fresh", "0")])
964
assert cc.min_fresh == 0
965
cc = ds.RequestCacheControl([("min-fresh", None)])
966
assert cc.min_fresh is None
967
cc = ds.RequestCacheControl()
968
assert cc.min_fresh is None
970
def test_must_understand(self):
971
cc = ds.ResponseCacheControl([("must-understand", None)])
972
assert cc.must_understand is True
973
cc = ds.ResponseCacheControl()
974
assert cc.must_understand is False
977
class TestContentSecurityPolicy:
978
def test_construct(self):
979
csp = ds.ContentSecurityPolicy([("font-src", "'self'"), ("media-src", "*")])
980
assert csp.font_src == "'self'"
981
assert csp.media_src == "*"
982
policies = [policy.strip() for policy in csp.to_header().split(";")]
983
assert "font-src 'self'" in policies
984
assert "media-src *" in policies
986
def test_properties(self):
987
csp = ds.ContentSecurityPolicy()
988
csp.default_src = "* 'self' quart.com"
989
csp.img_src = "'none'"
990
policies = [policy.strip() for policy in csp.to_header().split(";")]
991
assert "default-src * 'self' quart.com" in policies
992
assert "img-src 'none'" in policies
996
storage_class = ds.Accept
998
def test_accept_basic(self):
999
accept = self.storage_class(
1000
[("tinker", 0), ("tailor", 0.333), ("soldier", 0.667), ("sailor", 1)]
1003
assert accept[3] == ("tinker", 0)
1004
assert accept[2] == ("tailor", 0.333)
1005
assert accept[1] == ("soldier", 0.667)
1006
assert accept[0], ("sailor", 1)
1008
assert accept["tinker"] == 0
1009
assert accept["tailor"] == 0.333
1010
assert accept["soldier"] == 0.667
1011
assert accept["sailor"] == 1
1012
assert accept["spy"] == 0
1014
assert accept.quality("tinker") == 0
1015
assert accept.quality("tailor") == 0.333
1016
assert accept.quality("soldier") == 0.667
1017
assert accept.quality("sailor") == 1
1018
assert accept.quality("spy") == 0
1020
assert "sailor" in accept
1021
assert "spy" not in accept
1023
assert accept.index("tinker") == 3
1024
assert accept.index("tailor") == 2
1025
assert accept.index("soldier") == 1
1026
assert accept.index("sailor") == 0
1027
with pytest.raises(ValueError):
1030
assert accept.find("tinker") == 3
1031
assert accept.find("tailor") == 2
1032
assert accept.find("soldier") == 1
1033
assert accept.find("sailor") == 0
1034
assert accept.find("spy") == -1
1036
assert accept.to_header() == "sailor,soldier;q=0.667,tailor;q=0.333,tinker;q=0"
1039
accept.best_match(["tinker", "tailor", "soldier", "sailor"], default=None)
1043
accept.best_match(["tinker", "tailor", "soldier"], default=None)
1046
assert accept.best_match(["tinker", "tailor"], default=None) == "tailor"
1047
assert accept.best_match(["tinker"], default=None) is None
1048
assert accept.best_match(["tinker"], default="x") == "x"
1050
def test_accept_wildcard(self):
1051
accept = self.storage_class([("*", 0), ("asterisk", 1)])
1052
assert "*" in accept
1053
assert accept.best_match(["asterisk", "star"], default=None) == "asterisk"
1054
assert accept.best_match(["star"], default=None) is None
1056
def test_accept_keep_order(self):
1057
accept = self.storage_class([("*", 1)])
1058
assert accept.best_match(["alice", "bob"]) == "alice"
1059
assert accept.best_match(["bob", "alice"]) == "bob"
1060
accept = self.storage_class([("alice", 1), ("bob", 1)])
1061
assert accept.best_match(["alice", "bob"]) == "alice"
1062
assert accept.best_match(["bob", "alice"]) == "bob"
1064
def test_accept_wildcard_specificity(self):
1065
accept = self.storage_class([("asterisk", 0), ("star", 0.5), ("*", 1)])
1066
assert accept.best_match(["star", "asterisk"], default=None) == "star"
1067
assert accept.best_match(["asterisk", "star"], default=None) == "star"
1068
assert accept.best_match(["asterisk", "times"], default=None) == "times"
1069
assert accept.best_match(["asterisk"], default=None) is None
1071
def test_accept_equal_quality(self):
1072
accept = self.storage_class([("a", 1), ("b", 1)])
1073
assert accept.best == "a"
1076
class TestMIMEAccept:
1077
@pytest.mark.parametrize(
1078
("values", "matches", "default", "expect"),
1080
([("text/*", 1)], ["text/html"], None, "text/html"),
1081
([("text/*", 1)], ["image/png"], "text/plain", "text/plain"),
1082
([("text/*", 1)], ["image/png"], None, None),
1084
[("*/*", 1), ("text/html", 1)],
1085
["image/png", "text/html"],
1090
[("*/*", 1), ("text/html", 1)],
1091
["image/png", "text/plain"],
1096
[("*/*", 1), ("text/html", 1), ("image/*", 1)],
1097
["image/png", "text/html"],
1102
[("*/*", 1), ("text/html", 1), ("image/*", 1)],
1103
["text/plain", "image/png"],
1108
[("text/html", 1), ("text/html; level=1", 1)],
1109
["text/html;level=1"],
1111
"text/html;level=1",
1115
def test_mime_accept(self, values, matches, default, expect):
1116
accept = ds.MIMEAccept(values)
1117
match = accept.best_match(matches, default=default)
1118
assert match == expect
1121
class TestLanguageAccept:
1122
@pytest.mark.parametrize(
1123
("values", "matches", "default", "expect"),
1125
([("en-us", 1)], ["en"], None, "en"),
1126
([("en", 1)], ["en_US"], None, "en_US"),
1127
([("en-GB", 1)], ["en-US"], None, None),
1128
([("de_AT", 1), ("de", 0.9)], ["en"], None, None),
1129
([("de_AT", 1), ("de", 0.9), ("en-US", 0.8)], ["de", "en"], None, "de"),
1130
([("de_AT", 0.9), ("en-US", 1)], ["en"], None, "en"),
1131
([("en-us", 1)], ["en-us"], None, "en-us"),
1132
([("en-us", 1)], ["en-us", "en"], None, "en-us"),
1133
([("en-GB", 1)], ["en-US", "en"], "en-US", "en"),
1134
([("de_AT", 1)], ["en-US", "en"], "en-US", "en-US"),
1135
([("aus-EN", 1)], ["aus"], None, "aus"),
1136
([("aus", 1)], ["aus-EN"], None, "aus-EN"),
1139
def test_best_match_fallback(self, values, matches, default, expect):
1140
accept = ds.LanguageAccept(values)
1141
best = accept.best_match(matches, default=default)
1142
assert best == expect
1145
class TestFileStorage:
1146
storage_class = ds.FileStorage
1148
def test_mimetype_always_lowercase(self):
1149
file_storage = self.storage_class(content_type="APPLICATION/JSON")
1150
assert file_storage.mimetype == "application/json"
1152
@pytest.mark.parametrize("data", [io.StringIO("one\ntwo"), io.BytesIO(b"one\ntwo")])
1153
def test_bytes_proper_sentinel(self, data):
1155
storage = self.storage_class(data)
1158
for idx, _line in enumerate(storage):
1163
@pytest.mark.parametrize("stream", (tempfile.SpooledTemporaryFile, io.BytesIO))
1164
def test_proxy_can_access_stream_attrs(self, stream):
1165
"""``SpooledTemporaryFile`` doesn't implement some of
1166
``IOBase``. Ensure that ``FileStorage`` can still access the
1167
attributes from the backing file object.
1169
https://github.com/pallets/werkzeug/issues/1344
1170
https://github.com/python/cpython/pull/3249
1172
file_storage = self.storage_class(stream=stream())
1174
for name in ("fileno", "writable", "readable", "seekable"):
1175
assert hasattr(file_storage, name)
1177
file_storage.close()
1179
def test_save_to_pathlib_dst(self, tmp_path):
1180
src = tmp_path / "src.txt"
1181
src.write_text("test")
1182
dst = tmp_path / "dst.txt"
1184
with src.open("rb") as f:
1185
storage = self.storage_class(f)
1188
assert dst.read_text() == "test"
1190
def test_save_to_bytes_io(self):
1191
storage = self.storage_class(io.BytesIO(b"one\ntwo"))
1194
assert dst.getvalue() == b"one\ntwo"
1196
def test_save_to_file(self, tmp_path):
1197
path = tmp_path / "file.data"
1198
storage = self.storage_class(io.BytesIO(b"one\ntwo"))
1199
with path.open("wb") as dst:
1201
with path.open("rb") as src:
1202
assert src.read() == b"one\ntwo"
1205
@pytest.mark.parametrize("ranges", ([(0, 1), (-5, None)], [(5, None)]))
1206
def test_range_to_header(ranges):
1207
header = ds.Range("byes", ranges).to_header()
1208
r = http.parse_range_header(header)
1209
assert r.ranges == ranges
1212
@pytest.mark.parametrize(
1213
"ranges", ([(0, 0)], [(None, 1)], [(1, 0)], [(0, 1), (-5, 10)])
1215
def test_range_validates_ranges(ranges):
1216
with pytest.raises(ValueError):
1217
ds.Range("bytes", ranges)