From f60a99c357fdfb06e06461a756cddd80f1fbcdf2 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 14 Aug 2018 11:31:41 +0200 Subject: [PATCH] Tests --- src/pretix/api/views/order.py | 4 +- src/pretix/presale/checkoutflow.py | 2 +- .../templates/pretixpresale/event/order.html | 12 ++- src/tests/api/test_orders.py | 74 ++++++++++++++++ src/tests/api/test_permissions.py | 2 + src/tests/base/test_orders.py | 86 ++++++++++++++++++- src/tests/control/test_orders.py | 41 +++++++++ src/tests/presale/test_checkout.py | 39 ++++++++- 8 files changed, 251 insertions(+), 9 deletions(-) diff --git a/src/pretix/api/views/order.py b/src/pretix/api/views/order.py index 2f195426b2..2454c91bd1 100644 --- a/src/pretix/api/views/order.py +++ b/src/pretix/api/views/order.py @@ -35,8 +35,8 @@ from pretix.base.services.invoices import ( ) from pretix.base.services.mail import SendMailException from pretix.base.services.orders import ( - OrderChangeManager, OrderError, approve_order, cancel_order, deny_order, extend_order, - mark_order_expired, mark_order_refunded, + OrderChangeManager, OrderError, approve_order, cancel_order, deny_order, + extend_order, mark_order_expired, mark_order_refunded, ) from pretix.base.services.tickets import ( get_cachedticket_for_order, get_cachedticket_for_position, diff --git a/src/pretix/presale/checkoutflow.py b/src/pretix/presale/checkoutflow.py index 74fb977203..2a78acf9e8 100644 --- a/src/pretix/presale/checkoutflow.py +++ b/src/pretix/presale/checkoutflow.py @@ -633,7 +633,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep): return eventreverse(self.request.event, 'presale:event.order', kwargs={ 'order': order.code, 'secret': order.secret, - }) + }) + '?thanks=1' return eventreverse(self.request.event, 'presale:event.order.pay.complete', kwargs={ 'order': order.code, 'secret': order.secret, diff --git a/src/pretix/presale/templates/pretixpresale/event/order.html b/src/pretix/presale/templates/pretixpresale/event/order.html index 2307f47b5d..9153fc7ab9 100644 --- a/src/pretix/presale/templates/pretixpresale/event/order.html +++ b/src/pretix/presale/templates/pretixpresale/event/order.html @@ -14,9 +14,15 @@ {% if order.status != 'p' %}

{% trans "Your order has been placed successfully. See below for details." %}
- - {% trans "Please note that we still await your payment to complete the process." %} - + {% if order.require_approval %} + + {% trans "Please note that we still await approval by the event organizer before you can pay and complete this order." %} + + {% else %} + + {% trans "Please note that we still await your payment to complete the process." %} + + {% endif %}

{% elif order.total == 0 %}

{% trans "Your order has been processed successfully! See below for details." %}

diff --git a/src/tests/api/test_orders.py b/src/tests/api/test_orders.py index f1f7d122cf..0bacb78cb9 100644 --- a/src/tests/api/test_orders.py +++ b/src/tests/api/test_orders.py @@ -1143,6 +1143,80 @@ def test_order_extend_expired_quota_left(token_client, organizer, event, order, assert order.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59" +@pytest.mark.django_db +def test_order_pending_approve(token_client, organizer, event, order): + order.require_approval = True + order.save() + resp = token_client.post( + '/api/v1/organizers/{}/events/{}/orders/{}/approve/'.format( + organizer.slug, event.slug, order.code + ) + ) + assert resp.status_code == 200 + assert resp.data['status'] == Order.STATUS_PENDING + assert not resp.data['require_approval'] + + +@pytest.mark.django_db +def test_order_invalid_state_approve(token_client, organizer, event, order): + order.require_approval = True + order.status = Order.STATUS_CANCELED + order.save() + resp = token_client.post( + '/api/v1/organizers/{}/events/{}/orders/{}/approve/'.format( + organizer.slug, event.slug, order.code + ) + ) + assert resp.status_code == 400 + + order.require_approval = False + order.status = Order.STATUS_PENDING + order.save() + resp = token_client.post( + '/api/v1/organizers/{}/events/{}/orders/{}/approve/'.format( + organizer.slug, event.slug, order.code + ) + ) + assert resp.status_code == 400 + + +@pytest.mark.django_db +def test_order_pending_deny(token_client, organizer, event, order): + order.require_approval = True + order.save() + resp = token_client.post( + '/api/v1/organizers/{}/events/{}/orders/{}/deny/'.format( + organizer.slug, event.slug, order.code + ) + ) + assert resp.status_code == 200 + assert resp.data['status'] == Order.STATUS_CANCELED + assert resp.data['require_approval'] + + +@pytest.mark.django_db +def test_order_invalid_state_deny(token_client, organizer, event, order): + order.require_approval = True + order.status = Order.STATUS_CANCELED + order.save() + resp = token_client.post( + '/api/v1/organizers/{}/events/{}/orders/{}/deny/'.format( + organizer.slug, event.slug, order.code + ) + ) + assert resp.status_code == 400 + + order.require_approval = False + order.status = Order.STATUS_PENDING + order.save() + resp = token_client.post( + '/api/v1/organizers/{}/events/{}/orders/{}/deny/'.format( + organizer.slug, event.slug, order.code + ) + ) + assert resp.status_code == 400 + + ORDER_CREATE_PAYLOAD = { "email": "dummy@dummy.test", "locale": "en", diff --git a/src/tests/api/test_permissions.py b/src/tests/api/test_permissions.py index eeab272fa5..ba6ca2d5cb 100644 --- a/src/tests/api/test_permissions.py +++ b/src/tests/api/test_permissions.py @@ -73,6 +73,8 @@ event_permission_sub_urls = [ ('post', 'can_change_orders', 'orders/ABC12/mark_pending/', 404), ('post', 'can_change_orders', 'orders/ABC12/mark_expired/', 404), ('post', 'can_change_orders', 'orders/ABC12/mark_canceled/', 404), + ('post', 'can_change_orders', 'orders/ABC12/approve/', 404), + ('post', 'can_change_orders', 'orders/ABC12/deny/', 404), ('post', 'can_change_orders', 'orders/ABC12/extend/', 400), ('get', 'can_view_orders', 'orders/ABC12/payments/', 404), ('get', 'can_view_orders', 'orders/ABC12/payments/1/', 404), diff --git a/src/tests/base/test_orders.py b/src/tests/base/test_orders.py index ef71d9a163..b11b787205 100644 --- a/src/tests/base/test_orders.py +++ b/src/tests/base/test_orders.py @@ -18,8 +18,8 @@ from pretix.base.payment import FreeOrderProvider from pretix.base.reldate import RelativeDate, RelativeDateWrapper from pretix.base.services.invoices import generate_invoice from pretix.base.services.orders import ( - OrderChangeManager, OrderError, _create_order, expire_orders, - send_download_reminders, + OrderChangeManager, OrderError, _create_order, approve_order, deny_order, + expire_orders, send_download_reminders, ) @@ -207,6 +207,88 @@ def test_expiring_auto_disabled(event): assert o2.status == Order.STATUS_PENDING +@pytest.mark.django_db +def test_do_not_expire_if_approval_pending(event): + o1 = Order.objects.create( + code='FOO', event=event, email='dummy@dummy.test', + status=Order.STATUS_PENDING, + datetime=now(), expires=now() - timedelta(days=10), + total=0, require_approval=True + ) + o2 = Order.objects.create( + code='FO2', event=event, email='dummy@dummy.test', + status=Order.STATUS_PENDING, + datetime=now(), expires=now() - timedelta(days=10), + total=0, + ) + expire_orders(None) + o1 = Order.objects.get(id=o1.id) + assert o1.status == Order.STATUS_PENDING + o2 = Order.objects.get(id=o2.id) + assert o2.status == Order.STATUS_EXPIRED + + +@pytest.mark.django_db +def test_approve(event): + djmail.outbox = [] + event.settings.invoice_generate = 'True' + o1 = Order.objects.create( + code='FOO', event=event, email='dummy@dummy.test', + status=Order.STATUS_PENDING, + datetime=now(), expires=now() - timedelta(days=10), + total=10, require_approval=True, locale='de' + ) + approve_order(o1) + o1.refresh_from_db() + assert o1.expires > now() + assert o1.status == Order.STATUS_PENDING + assert not o1.require_approval + assert o1.invoices.count() == 1 + assert len(djmail.outbox) == 1 + assert 'awaiting payment' in djmail.outbox[0].subject + + +@pytest.mark.django_db +def test_approve_free(event): + djmail.outbox = [] + event.settings.invoice_generate = 'True' + o1 = Order.objects.create( + code='FOO', event=event, email='dummy@dummy.test', + status=Order.STATUS_PENDING, + datetime=now(), expires=now() - timedelta(days=10), + total=0, require_approval=True + ) + approve_order(o1) + o1.refresh_from_db() + assert o1.expires > now() + assert o1.status == Order.STATUS_PAID + assert not o1.require_approval + assert o1.invoices.count() == 0 + assert len(djmail.outbox) == 1 + assert 'confirmed' in djmail.outbox[0].subject + + +@pytest.mark.django_db +def test_deny(event): + djmail.outbox = [] + event.settings.invoice_generate = 'True' + o1 = Order.objects.create( + code='FOO', event=event, email='dummy@dummy.test', + status=Order.STATUS_PENDING, + datetime=now(), expires=now() - timedelta(days=10), + total=10, require_approval=True, locale='de' + ) + generate_invoice(o1) + deny_order(o1) + o1.refresh_from_db() + assert o1.expires < now() + assert o1.status == Order.STATUS_CANCELED + assert o1.require_approval + assert o1.invoices.count() == 2 + assert len(djmail.outbox) == 1 + assert 'denied' in djmail.outbox[0].subject + + class DownloadReminderTests(TestCase): def setUp(self): super().setUp() diff --git a/src/tests/control/test_orders.py b/src/tests/control/test_orders.py index 789a68ca6f..56f2b8b47c 100644 --- a/src/tests/control/test_orders.py +++ b/src/tests/control/test_orders.py @@ -92,6 +92,13 @@ def test_order_list(client, env): response = client.get('/control/event/dummy/dummy/orders/?status=o') assert 'FOO' in response.rendered_content + response = client.get('/control/event/dummy/dummy/orders/?status=pa') + assert 'FOO' not in response.rendered_content + env[2].require_approval = True + env[2].save() + response = client.get('/control/event/dummy/dummy/orders/?status=pa') + assert 'FOO' in response.rendered_content + q = Question.objects.create(event=env[0], question="Q", type="N", required=True) q.items.add(env[3]) op = env[2].positions.first() @@ -208,6 +215,40 @@ def test_order_transition_to_paid_expired_quota_left(client, env): assert o.status == Order.STATUS_PAID +@pytest.mark.django_db +def test_order_approve(client, env): + o = Order.objects.get(id=env[2].id) + o.status = Order.STATUS_PENDING + o.require_approval = True + o.save() + q = Quota.objects.create(event=env[0], size=10) + q.items.add(env[3]) + client.login(email='dummy@dummy.dummy', password='dummy') + res = client.post('/control/event/dummy/dummy/orders/FOO/approve', { + }) + o = Order.objects.get(id=env[2].id) + assert res.status_code < 400 + assert o.status == Order.STATUS_PENDING + assert not o.require_approval + + +@pytest.mark.django_db +def test_order_deny(client, env): + o = Order.objects.get(id=env[2].id) + o.status = Order.STATUS_PENDING + o.require_approval = True + o.save() + q = Quota.objects.create(event=env[0], size=10) + q.items.add(env[3]) + client.login(email='dummy@dummy.dummy', password='dummy') + res = client.post('/control/event/dummy/dummy/orders/FOO/deny', { + }) + o = Order.objects.get(id=env[2].id) + assert res.status_code < 400 + assert o.status == Order.STATUS_CANCELED + assert o.require_approval + + @pytest.mark.django_db @pytest.mark.parametrize("process", [ # (Old status, new status, success expected) diff --git a/src/tests/presale/test_checkout.py b/src/tests/presale/test_checkout.py index 8d7975d279..4c298748a9 100644 --- a/src/tests/presale/test_checkout.py +++ b/src/tests/presale/test_checkout.py @@ -14,7 +14,7 @@ from django_countries.fields import Country from pretix.base.decimal import round_decimal from pretix.base.models import ( - CartPosition, Event, InvoiceAddress, Item, ItemCategory, Order, + CartPosition, Event, Invoice, InvoiceAddress, Item, ItemCategory, Order, OrderPosition, Organizer, Question, QuestionAnswer, Quota, Voucher, ) from pretix.base.models.items import ItemAddOn, ItemVariation, SubEventItem @@ -803,6 +803,43 @@ class CheckoutTestCase(TestCase): self.assertEqual(OrderPosition.objects.count(), 1) self.assertEqual(OrderPosition.objects.first().subevent, se) + def test_require_approval_no_payment_step(self): + self.event.settings.invoice_generate = 'True' + self.ticket.require_approval = True + self.ticket.save() + cr1 = CartPosition.objects.create( + event=self.event, cart_id=self.session_key, item=self.ticket, + price=42, expires=now() + timedelta(minutes=10) + ) + + response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) + doc = BeautifulSoup(response.rendered_content, "lxml") + print(doc) + self.assertEqual(len(doc.select(".thank-you")), 1) + self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) + self.assertEqual(Order.objects.count(), 1) + self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING) + self.assertTrue(Order.objects.first().require_approval) + self.assertEqual(OrderPosition.objects.count(), 1) + self.assertEqual(Invoice.objects.count(), 0) + + def test_require_approval_no_payment_step_free(self): + self.ticket.require_approval = True + self.ticket.save() + cr1 = CartPosition.objects.create( + event=self.event, cart_id=self.session_key, item=self.ticket, + price=0, expires=now() + timedelta(minutes=10) + ) + + response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) + doc = BeautifulSoup(response.rendered_content, "lxml") + self.assertEqual(len(doc.select(".thank-you")), 1) + self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists()) + self.assertEqual(Order.objects.count(), 1) + self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING) + self.assertTrue(Order.objects.first().require_approval) + self.assertEqual(OrderPosition.objects.count(), 1) + def test_free_price(self): self.ticket.free_price = True self.ticket.save()