Passed
Push — main ( 57d628...c66ff9 )
by Jochen
04:13
created

assert_line_item()   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 5
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
"""
2
:Copyright: 2006-2021 Jochen Kupperschmidt
3
:License: Revised BSD (see `LICENSE` file for details)
4
"""
5
6
from unittest.mock import patch
7
8
import pytest
9
10
from byceps.events.shop import ShopOrderPlaced
11
from byceps.services.shop.article import service as article_service
12
from byceps.services.shop.order.dbmodels.order import Order
13
from byceps.services.shop.order import (
14
    sequence_service as order_sequence_service,
15
    service as order_service,
16
)
17
from byceps.services.shop.shop import service as shop_service
18
from byceps.services.shop.storefront import service as storefront_service
19
from byceps.services.site import service as site_service
20
from byceps.services.snippet import service as snippet_service
21
22
from tests.helpers import create_site, generate_token, http_client, login_user
23
from tests.integration.services.shop.helpers import (
24
    create_article,
25
    create_shop,
26
    create_shop_fragment,
27
)
28
29
30
COMMON_FORM_DATA = {
31
    'first_names': 'Hiro',
32
    'last_name': 'Protagonist',
33
    'country': 'State of Mind',
34
    'zip_code': '31337',
35
    'city': 'Atrocity',
36
    'street': 'L33t Street 101',
37
}
38
39
40
@pytest.fixture
41
def shop(make_brand, admin_user):
42
    brand = make_brand()
43
    shop = create_shop(brand.id)
44
    snippet_id = create_shop_fragment(
45
        shop.id, admin_user.id, 'payment_instructions', 'Send all ur moneyz!'
46
    )
47
48
    yield shop
49
50
    snippet_service.delete_snippet(snippet_id)
51
    shop_service.delete_shop(shop.id)
52
53
54
@pytest.fixture
55
def order_number_sequence_id(shop) -> None:
56
    sequence_id = order_sequence_service.create_order_number_sequence(
57
        shop.id, 'AEC-01-B', value=4
58
    )
59
60
    yield sequence_id
61
62
    order_sequence_service.delete_order_number_sequence(sequence_id)
63
64
65
@pytest.fixture
66
def storefront(shop, order_number_sequence_id) -> None:
67
    storefront = storefront_service.create_storefront(
68
        f'{shop.id}-storefront', shop.id, order_number_sequence_id, closed=False
69
    )
70
71
    yield storefront
72
73
    storefront_service.delete_storefront(storefront.id)
74
75
76
@pytest.fixture
77
def site(make_brand, storefront):
78
    brand = make_brand()
79
    site = create_site(
80
        'acmecon-2014-shop-website', brand.id, storefront_id=storefront.id
81
    )
82
    yield site
83
    site_service.delete_site(site.id)
84
85
86
@pytest.fixture
87
def site_app(site, make_site_app):
88
    app = make_site_app(SITE_ID=site.id)
89
    with app.app_context():
90
        yield app
91
92
93
@pytest.fixture
94
def article(admin_app, shop):
95
    article = create_article(shop.id, total_quantity=5)
96
    article_id = article.id
97
    yield article
98
    article_service.delete_article(article_id)
99
100
101
@pytest.fixture
102
def orderer(admin_app, user):
103
    login_user(user.id)
104
    return user
105
106
107 View Code Duplication
@patch('byceps.signals.shop.order_placed.send')
108
@patch('byceps.blueprints.site.shop.order.views.order_email_service')
109
def test_order(
110
    order_email_service_mock,
111
    order_placed_mock,
112
    site_app,
113
    site,
114
    admin_user,
115
    orderer,
116
    article,
117
):
118
    assert get_article_quantity(article.id) == 5
119
120
    url = '/shop/order'
121
    article_quantity_key = f'article_{article.id}'
122
    form_data = {
123
        **COMMON_FORM_DATA,
124
        article_quantity_key: 3,
125
    }
126
    with http_client(site_app, user_id=orderer.id) as client:
127
        response = client.post(url, data=form_data)
128
129
    assert get_article_quantity(article.id) == 2
130
131
    order = Order.query.filter_by(placed_by_id=orderer.id).one()
132
    assert_order(order, 'AEC-01-B00005', 1)
133
134
    first_line_item = order.items[0]
135
    assert_line_item(
136
        first_line_item, article.id, article.price, article.tax_rate, 3,
137
    )
138
139
    order_email_service_mock.send_email_for_incoming_order_to_orderer.assert_called_once_with(
140
        order.id
141
    )
142
143
    event = ShopOrderPlaced(
144
        occurred_at=order.created_at,
145
        initiator_id=orderer.id,
146
        initiator_screen_name=orderer.screen_name,
147
        order_id=order.id,
148
        order_number=order.order_number,
149
        orderer_id=orderer.id,
150
        orderer_screen_name=orderer.screen_name,
151
    )
152
    order_placed_mock.assert_called_once_with(None, event=event)
153
154
    order_detail_page_url = f'http://www.acmecon.test/shop/orders/{order.id}'
155
156
    assert_response_headers(response, order_detail_page_url)
157
158
    with http_client(site_app, user_id=orderer.id) as client:
159
        assert_order_detail_page_works(
160
            client, order_detail_page_url, order.order_number
161
        )
162
163
    order_service.delete_order(order.id)
164
165
166 View Code Duplication
@patch('byceps.signals.shop.order_placed.send')
167
@patch('byceps.blueprints.site.shop.order.views.order_email_service')
168
def test_order_single(
169
    order_email_service_mock,
170
    order_placed_mock,
171
    site_app,
172
    site,
173
    admin_user,
174
    orderer,
175
    article,
176
):
177
    assert get_article_quantity(article.id) == 5
178
179
    url = f'/shop/order_single/{article.id!s}'
180
    form_data = {
181
        **COMMON_FORM_DATA,
182
        'quantity': 1,  # TODO: Test with `3` if limitation is removed.
183
    }
184
    with http_client(site_app, user_id=orderer.id) as client:
185
        response = client.post(url, data=form_data)
186
187
    assert get_article_quantity(article.id) == 4
188
189
    order = Order.query.filter_by(placed_by_id=orderer.id).one()
190
    assert_order(order, 'AEC-01-B00005', 1)
191
192
    first_line_item = order.items[0]
193
    assert_line_item(
194
        first_line_item, article.id, article.price, article.tax_rate, 1,
195
    )
196
197
    order_email_service_mock.send_email_for_incoming_order_to_orderer.assert_called_once_with(
198
        order.id
199
    )
200
201
    event = ShopOrderPlaced(
202
        occurred_at=order.created_at,
203
        initiator_id=orderer.id,
204
        initiator_screen_name=orderer.screen_name,
205
        order_id=order.id,
206
        order_number=order.order_number,
207
        orderer_id=orderer.id,
208
        orderer_screen_name=orderer.screen_name,
209
    )
210
    order_placed_mock.assert_called_once_with(None, event=event)
211
212
    order_detail_page_url = f'http://www.acmecon.test/shop/orders/{order.id}'
213
214
    assert_response_headers(response, order_detail_page_url)
215
216
    with http_client(site_app, user_id=orderer.id) as client:
217
        assert_order_detail_page_works(
218
            client, order_detail_page_url, order.order_number
219
        )
220
221
    order_service.delete_order(order.id)
222
223
224
# helpers
225
226
227
def get_article_quantity(article_id):
228
    article = article_service.get_article(article_id)
229
    return article.quantity
230
231
232
def assert_response_headers(response, order_detail_page_url):
233
    assert response.status_code == 302
234
    assert response.headers.get('Location') == order_detail_page_url
235
236
237
def assert_order(order, order_number, item_quantity):
238
    assert order.order_number == order_number
239
    assert len(order.items) == item_quantity
240
241
242
def assert_line_item(line_item, article_id, unit_price, tax_rate, quantity):
243
    assert line_item.article.id == article_id
244
    assert line_item.unit_price == unit_price
245
    assert line_item.tax_rate == tax_rate
246
    assert line_item.quantity == quantity
247
248
249
def assert_order_detail_page_works(client, order_detail_page_url, order_number):
250
    response = client.get(order_detail_page_url)
251
    assert response.status_code == 200
252
    assert 'AEC-01-B00005' in response.get_data(as_text=True)
253