File: test_utils.py

package info (click to toggle)
django-tables 2.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,752 kB
  • sloc: python: 7,120; makefile: 132; sh: 74
file content (269 lines) | stat: -rw-r--r-- 8,563 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
from django.db import models
from django.test import TestCase

from django_tables2.utils import (
    Accessor,
    AttributeDict,
    OrderBy,
    OrderByTuple,
    Sequence,
    call_with_appropriate,
    computed_values,
    segment,
    signature,
)


class OrderByTupleTest(TestCase):
    def test_basic(self):
        obt = OrderByTuple(("a", "b", "c"))
        assert obt == (OrderBy("a"), OrderBy("b"), OrderBy("c"))

    def test_indexing(self):
        obt = OrderByTuple(("a", "b", "c"))
        assert obt[0] == OrderBy("a")
        assert obt["b"] == OrderBy("b")
        with self.assertRaises(KeyError):
            obt["d"]
        with self.assertRaises(TypeError):
            obt[("tuple",)]

    def test_get(self):
        obt = OrderByTuple(("a", "b", "c"))
        sentinel = object()
        assert obt.get("b", sentinel) is obt["b"]  # keying
        assert obt.get("-", sentinel) is sentinel
        assert obt.get(0, sentinel) is obt["a"]  # indexing
        assert obt.get(3, sentinel) is sentinel

    def test_opposite(self):
        assert OrderByTuple(("a", "-b", "c")).opposite == ("-a", "b", "-c")

    def test_in(self):
        obt = OrderByTuple(("a", "b", "c"))
        assert "a" in obt and "-a" in obt

    def test_sort_key_multiple(self):
        obt = OrderByTuple(("a", "-b"))
        items = [{"a": 1, "b": 2}, {"a": 1, "b": 3}]
        assert sorted(items, key=obt.key) == [{"a": 1, "b": 3}, {"a": 1, "b": 2}]

    def test_sort_key_empty_comes_first(self):
        obt = OrderByTuple("a")
        items = [{"a": 1}, {"a": ""}, {"a": 2}]
        assert sorted(items, key=obt.key) == [{"a": ""}, {"a": 1}, {"a": 2}]


class OrderByTest(TestCase):
    def test_orderby_ascending(self):
        a = OrderBy("a")
        self.assertEqual(a, "a")
        self.assertEqual(a.bare, "a")
        self.assertEqual(a.opposite, "-a")
        self.assertTrue(a.is_ascending)
        self.assertFalse(a.is_descending)

    def test_orderby_descending(self):
        b = OrderBy("-b")
        self.assertEqual(b, "-b")
        self.assertEqual(b.bare, "b")
        self.assertEqual(b.opposite, "b")
        self.assertTrue(b.is_descending)
        self.assertFalse(b.is_ascending)

    def test_error_on_legacy_separator(self):
        message = "Use '__' to separate path components, not '.' in accessor 'a.b'"
        with self.assertWarnsRegex(DeprecationWarning, message):
            OrderBy("a.b")

    def test_for_queryset(self):
        ab = OrderBy("a.b")
        self.assertEqual(ab.for_queryset(), "a__b")
        ab = OrderBy("a__b")
        self.assertEqual(ab.for_queryset(), "a__b")


class AccessorTest(TestCase):
    def test_bare(self):
        self.assertEqual(Accessor("").resolve("Brad"), "Brad")
        self.assertEqual(Accessor("").resolve({"Brad"}), {"Brad"})
        self.assertEqual(Accessor("").resolve({"Brad": "author"}), {"Brad": "author"})

    def test_index_lookup(self):
        self.assertEqual(Accessor("0").resolve("Brad"), "B")
        self.assertEqual(Accessor("1").resolve("Brad"), "r")
        self.assertEqual(Accessor("-1").resolve("Brad"), "d")
        self.assertEqual(Accessor("-2").resolve("Brad"), "a")

    def test_calling_methods(self):
        self.assertEqual(Accessor("2__upper").resolve("Brad"), "A")

    def test_error_on_legacy_separator(self):
        message = "Use '__' to separate path components, not '.' in accessor '2.upper'"
        with self.assertWarnsRegex(DeprecationWarning, message):
            Accessor("2.upper")

    def test_honors_alters_data(self):
        class Foo:
            deleted = False

            def delete(self):
                self.deleted = True

            delete.alters_data = True

        foo = Foo()
        with self.assertRaisesMessage(ValueError, "Refusing to call delete() because"):
            Accessor("delete").resolve(foo)
        self.assertFalse(foo.deleted)

    def test_accessor_can_be_quiet(self):
        self.assertIsNone(Accessor("bar").resolve({}, quiet=True))

    def test_penultimate(self):
        context = {"a": {"a": 1, "b": {"c": 2, "d": 4}}}
        self.assertEqual(Accessor("a__b__c").penultimate(context), (context["a"]["b"], "c"))
        self.assertEqual(Accessor("a___b___c___d___e").penultimate(context), (None, "e"))

    def test_short_circuit_dict(self):
        context = {"occupation__name": "Carpenter"}

        self.assertEqual(Accessor("occupation__name").resolve(context), "Carpenter")

    def test_callable_args_kwargs(self):
        class MyClass:
            def method(self, *args, **kwargs):
                return args, kwargs

        callable_args = ("arg1", "arg2")
        callable_kwargs = {"kwarg1": "val1", "kwarg2": "val2"}
        obj = MyClass()
        result = Accessor("method", callable_args, callable_kwargs).resolve(obj)
        self.assertEqual(result, (callable_args, callable_kwargs))


class AccessorTestModel(models.Model):
    foo = models.CharField(max_length=20)

    class Meta:
        app_label = "tests"


class AccessorModelTests(TestCase):
    def test_can_return_field(self):
        context = AccessorTestModel(foo="bar")
        self.assertIsInstance(Accessor("foo").get_field(context), models.CharField)

    def test_returns_None_when_doesnt_exist(self):
        context = AccessorTestModel(foo="bar")
        self.assertIsNone(Accessor("bar").get_field(context))

    def test_returns_None_if_not_a_model(self):
        context = {"bar": 234}
        self.assertIsNone(Accessor("bar").get_field(context))


class AttributeDictTest(TestCase):
    def test_handles_escaping(self):
        # django==3.0 replaces ' with ', drop first option if django==2.2 support is removed
        self.assertIn(
            AttributeDict({"x": "\"'x&"}).as_html(),
            ('x=""'x&"', 'x=""'x&"'),
        )

    def test_omits_None(self):
        self.assertEqual(AttributeDict({"x": None}).as_html(), "")

    def test_self_wrap(self):
        x = AttributeDict({"x": "y"})

        self.assertEqual(x, AttributeDict(x))


class ComputedValuesTest(TestCase):
    def test_supports_shallow_structures(self):
        x = computed_values({"foo": lambda: "bar"})
        self.assertEqual(x, {"foo": "bar"})

    def test_supports_nested_structures(self):
        x = computed_values({"foo": lambda: {"bar": lambda: "baz"}})
        self.assertEqual(x, {"foo": {"bar": "baz"}})

    def test_with_argument(self):
        x = computed_values({"foo": lambda y: {"bar": lambda y: f"baz-{y}"}}, kwargs=dict(y=2))
        self.assertEqual(x, {"foo": {"bar": "baz-2"}})

    def test_returns_None_if_not_enough_kwargs(self):
        x = computed_values({"foo": lambda x: "bar"})
        self.assertEqual(x, {"foo": None})


class SegmentTest(TestCase):
    def test_should_return_all_candidates(self):
        assert set(segment(("a", "-b", "c"), {"x": "a", "y": ("b", "-c"), "-z": ("b", "-c")})) == {
            ("x", "-y"),
            ("x", "z"),
        }


class SequenceTest(TestCase):
    def test_multiple_ellipsis(self):
        sequence = Sequence(["foo", "...", "bar", "..."])

        with self.assertRaisesMessage(ValueError, "'...' must be used at most once in a sequence."):
            sequence.expand(["foo"])


class SignatureTest(TestCase):
    def test_basic(self):
        def foo(bar, baz):
            pass

        args, keywords = signature(foo)
        assert args == ("bar", "baz")
        assert keywords is None

    def test_signature_method(self):
        class Foo:
            def foo(self):
                pass

            def bar(self, bar, baz):
                pass

            def baz(self, bar, *bla, **boo):
                pass

        obj = Foo()
        args, keywords = signature(obj.foo)
        assert args == ()
        assert keywords is None

        args, keywords = signature(obj.bar)
        assert args == ("bar", "baz")
        assert keywords is None

        args, keywords = signature(obj.baz)
        assert args == ("bar",)
        assert keywords == "boo"

    def test_catch_all_kwargs(self):
        def foo(bar, baz, **kwargs):
            pass

        args, keywords = signature(foo)
        assert args == ("bar", "baz")
        assert keywords == "kwargs"


class CallWithAppropriateTest(TestCase):
    def test_basic(self):
        def foo():
            return "bar"

        assert call_with_appropriate(foo, {"a": "d", "c": "e"}) == "bar"

        def bar(baz):
            return baz

        assert call_with_appropriate(bar, dict(baz=23)) == 23