File: test_choice.py

package info (click to toggle)
python-sqlalchemy-utils 0.41.2-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 1,252 kB
  • sloc: python: 13,566; makefile: 141
file content (212 lines) | stat: -rw-r--r-- 5,732 bytes parent folder | download | duplicates (2)
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
import pytest
import sqlalchemy as sa
from flexmock import flexmock

from sqlalchemy_utils import Choice, ChoiceType, ImproperlyConfigured
from sqlalchemy_utils.compat import _select_args
from sqlalchemy_utils.types.choice import Enum


class TestChoice:
    def test_equality_operator(self):
        assert Choice(1, 1) == 1
        assert 1 == Choice(1, 1)
        assert Choice(1, 1) == Choice(1, 1)

    def test_non_equality_operator(self):
        assert Choice(1, 1) != 2
        assert not (Choice(1, 1) != 1)

    def test_hash(self):
        assert hash(Choice(1, 1)) == hash(1)


class TestChoiceType:
    @pytest.fixture
    def User(self, Base):
        class User(Base):
            TYPES = [
                ('admin', 'Admin'),
                ('regular-user', 'Regular user')
            ]

            __tablename__ = 'user'
            id = sa.Column(sa.Integer, primary_key=True)
            type = sa.Column(ChoiceType(TYPES))

            def __repr__(self):
                return 'User(%r)' % self.id

        return User

    @pytest.fixture
    def init_models(self, User):
        pass

    def test_python_type(self, User):
        type_ = User.__table__.c.type.type
        assert type_.python_type

    def test_string_processing(self, session, User):
        flexmock(ChoiceType).should_receive('_coerce').and_return(
            'admin'
        )
        user = User(
            type='admin'
        )

        session.add(user)
        session.commit()

        user = session.query(User).first()
        assert user.type.value == 'Admin'

    def test_parameter_processing(self, session, User):
        user = User(
            type='admin'
        )

        session.add(user)
        session.commit()

        user = session.query(User).first()
        assert user.type.value == 'Admin'

    def test_scalar_attributes_get_coerced_to_objects(self, User):
        user = User(type='admin')

        assert isinstance(user.type, Choice)

    def test_throws_exception_if_no_choices_given(self):
        with pytest.raises(ImproperlyConfigured):
            ChoiceType([])

    def test_compilation(self, User, session):
        query = sa.select(*_select_args(User.type))
        # the type should be cacheable and not throw exception
        session.execute(query)


class TestChoiceTypeWithCustomUnderlyingType:
    def test_init_type(self):
        type_ = ChoiceType([(1, 'something')], impl=sa.Integer)
        assert type_.impl == sa.Integer


@pytest.mark.skipif('Enum is None')
class TestEnumType:

    @pytest.fixture
    def OrderStatus(self):
        class OrderStatus(Enum):
            unpaid = 0
            paid = 1
        return OrderStatus

    @pytest.fixture
    def Order(self, Base, OrderStatus):

        class Order(Base):
            __tablename__ = 'order'
            id_ = sa.Column(sa.Integer, primary_key=True)
            status = sa.Column(
                ChoiceType(OrderStatus, impl=sa.Integer()),
                default=OrderStatus.unpaid,
            )

            def __repr__(self):
                return f'Order({self.id_!r}, {self.status!r})'

        return Order

    @pytest.fixture
    def OrderNullable(self, Base, OrderStatus):

        class OrderNullable(Base):
            __tablename__ = 'order_nullable'
            id_ = sa.Column(sa.Integer, primary_key=True)
            status = sa.Column(
                ChoiceType(OrderStatus, impl=sa.Integer()),
                nullable=True,
            )

        return OrderNullable

    @pytest.fixture
    def init_models(self, Order, OrderNullable):
        pass

    def test_parameter_initialization(self, session, Order, OrderStatus):
        order = Order()

        session.add(order)
        session.commit()

        order = session.query(Order).first()
        assert order.status is OrderStatus.unpaid
        assert order.status.value == 0

    def test_setting_by_value(self, session, Order, OrderStatus):
        order = Order()
        order.status = 1

        session.add(order)
        session.commit()

        order = session.query(Order).first()
        assert order.status is OrderStatus.paid

    def test_setting_by_enum(self, session, Order, OrderStatus):
        order = Order()
        order.status = OrderStatus.paid

        session.add(order)
        session.commit()

        order = session.query(Order).first()
        assert order.status is OrderStatus.paid

    def test_setting_value_that_resolves_to_none(
        self,
        session,
        Order,
        OrderStatus
    ):
        order = Order()
        order.status = 0

        session.add(order)
        session.commit()

        order = session.query(Order).first()
        assert order.status is OrderStatus.unpaid

    def test_setting_to_wrong_enum_raises_valueerror(self, Order):
        class WrongEnum(Enum):
            foo = 0
            bar = 1

        order = Order()

        with pytest.raises(ValueError):
            order.status = WrongEnum.foo

    def test_setting_to_uncoerceable_type_raises_valueerror(self, Order):
        order = Order()
        with pytest.raises(ValueError):
            order.status = 'Bad value'

    def test_order_nullable_stores_none(self, session, OrderNullable):
        # With nullable=False as in `Order`, a `None` value is always
        # converted to the default value, unless we explicitly set it to
        # sqlalchemy.sql.null(), so we use this class to test our ability
        # to set and retrive `None`.
        order_nullable = OrderNullable()
        assert order_nullable.status is None

        order_nullable.status = None

        session.add(order_nullable)
        session.commit()

        assert order_nullable.status is None