From d1f79faef2be40e976f899ba9dc3457b878daf22 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 20 Sep 2016 16:13:13 +0200 Subject: [PATCH] New tests and minor fixes --- src/pretix/base/i18n.py | 2 +- src/pretix/control/views/orders.py | 9 +- src/pretix/control/views/vouchers.py | 3 +- src/tests/control/test_orders.py | 176 ++++++++++++++++++++++++--- src/tests/control/test_vouchers.py | 83 +++++++++++++ src/tests/presale/test_orders.py | 47 ++++++- 6 files changed, 292 insertions(+), 28 deletions(-) diff --git a/src/pretix/base/i18n.py b/src/pretix/base/i18n.py index d50420bbd5..76436993e7 100644 --- a/src/pretix/base/i18n.py +++ b/src/pretix/base/i18n.py @@ -85,7 +85,7 @@ class LazyI18nString: else: return str(self.data) - def __repr__(self) -> str: + def __repr__(self) -> str: # NOQA return '' % repr(self.data) def __lt__(self, other) -> bool: # NOQA diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index a9479a6ff8..17896c37a2 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -296,7 +296,7 @@ class OrderInvoiceRegenerate(OrderView): def post(self, *args, **kwargs): try: inv = self.order.invoices.get(pk=kwargs.get('id')) - except Order.DoesNotExist: + except Invoice.DoesNotExist: messages.error(self.request, _('Unknown invoice.')) else: if inv.canceled: @@ -309,7 +309,7 @@ class OrderInvoiceRegenerate(OrderView): messages.success(self.request, _('The invoice has been regenerated.')) return redirect(self.get_order_url()) - def get(self, *args, **kwargs): + def get(self, *args, **kwargs): # NOQA return HttpResponseNotAllowed(['POST']) @@ -319,7 +319,7 @@ class OrderInvoiceReissue(OrderView): def post(self, *args, **kwargs): try: inv = self.order.invoices.get(pk=kwargs.get('id')) - except Order.DoesNotExist: + except Invoice.DoesNotExist: messages.error(self.request, _('Unknown invoice.')) else: if inv.canceled: @@ -333,7 +333,7 @@ class OrderInvoiceReissue(OrderView): messages.success(self.request, _('The invoice has been reissued.')) return redirect(self.get_order_url()) - def get(self, *args, **kwargs): + def get(self, *args, **kwargs): # NOQA return HttpResponseNotAllowed(['POST']) @@ -515,7 +515,6 @@ class OrderChange(OrderView): form_valid = True for p in self.positions: if not p.form.is_valid(): - print(p.pk, 'Form invalid') form_valid = False break diff --git a/src/pretix/control/views/vouchers.py b/src/pretix/control/views/vouchers.py index c045202bdf..d8b9e55eb6 100644 --- a/src/pretix/control/views/vouchers.py +++ b/src/pretix/control/views/vouchers.py @@ -267,14 +267,13 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView): class VoucherRNG(EventPermissionRequiredMixin, View): - template_name = 'pretixcontrol/vouchers/bulk.html' permission = 'can_change_vouchers' def get(self, request, *args, **kwargs): codes = set() try: num = int(request.GET.get('num', '5')) - except ValueError: + except ValueError: # NOQA return HttpResponseBadRequest() while len(codes) < num: diff --git a/src/tests/control/test_orders.py b/src/tests/control/test_orders.py index 06ab32341f..afab0902ee 100644 --- a/src/tests/control/test_orders.py +++ b/src/tests/control/test_orders.py @@ -2,12 +2,16 @@ from datetime import timedelta from decimal import Decimal import pytest +from django.core import mail from django.utils.timezone import now from tests.base import SoupTest from pretix.base.models import ( - CachedTicket, Event, EventPermission, Item, Order, OrderPosition, - Organizer, Quota, User, + CachedTicket, Event, EventPermission, InvoiceAddress, Item, Order, + OrderPosition, Organizer, Quota, User, +) +from pretix.base.services.invoices import ( + generate_cancellation, generate_invoice, ) @@ -30,7 +34,7 @@ def env(): code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, datetime=now(), expires=now() + timedelta(days=10), - total=0, payment_provider='banktransfer' + total=14, payment_provider='banktransfer', locale='en' ) ticket = Item.objects.create(event=event, name='Early-bird ticket', category=None, default_price=23, @@ -61,10 +65,23 @@ def test_order_list(client, env): assert 'FOO' not in response.rendered_content response = client.get('/control/event/dummy/dummy/orders/?status=n') assert 'FOO' in response.rendered_content + response = client.get('/control/event/dummy/dummy/orders/?status=ne') + assert 'FOO' in response.rendered_content response = client.get('/control/event/dummy/dummy/orders/?item=15') assert 'FOO' not in response.rendered_content response = client.get('/control/event/dummy/dummy/orders/?item=%s' % env[3].id) assert 'FOO' in response.rendered_content + response = client.get('/control/event/dummy/dummy/orders/?provider=foo') + assert 'FOO' not in response.rendered_content + response = client.get('/control/event/dummy/dummy/orders/?provider=banktransfer') + assert 'FOO' in response.rendered_content + + response = client.get('/control/event/dummy/dummy/orders/?status=o') + assert 'FOO' not in response.rendered_content + env[2].expires = now() - timedelta(days=10) + env[2].save() + response = client.get('/control/event/dummy/dummy/orders/?status=o') + assert 'FOO' in response.rendered_content @pytest.mark.django_db @@ -75,6 +92,42 @@ def test_order_detail(client, env): assert 'Peter' in response.rendered_content +@pytest.mark.django_db +def test_order_set_contact(client, env): + q = Quota.objects.create(event=env[0], size=0) + q.items.add(env[3]) + client.login(email='dummy@dummy.dummy', password='dummy') + client.post('/control/event/dummy/dummy/orders/FOO/contact', { + 'email': 'admin@rami.io' + }) + o = Order.objects.get(id=env[2].id) + assert o.email == 'admin@rami.io' + + +@pytest.mark.django_db +def test_order_set_comment(client, env): + q = Quota.objects.create(event=env[0], size=0) + q.items.add(env[3]) + client.login(email='dummy@dummy.dummy', password='dummy') + client.post('/control/event/dummy/dummy/orders/FOO/comment', { + 'comment': 'Foo' + }) + o = Order.objects.get(id=env[2].id) + assert o.comment == 'Foo' + + +@pytest.mark.django_db +def test_order_transition_to_expired_success(client, env): + q = Quota.objects.create(event=env[0], size=0) + q.items.add(env[3]) + client.login(email='dummy@dummy.dummy', password='dummy') + client.post('/control/event/dummy/dummy/orders/FOO/transition', { + 'status': 'e' + }) + o = Order.objects.get(id=env[2].id) + assert o.status == Order.STATUS_EXPIRED + + @pytest.mark.django_db def test_order_transition_to_paid_in_time_success(client, env): q = Quota.objects.create(event=env[0], size=0) @@ -103,45 +156,35 @@ def test_order_transition_to_paid_expired_quota_left(client, env): assert o.status == Order.STATUS_PAID -@pytest.mark.django_db -def test_order_transition_to_paid_expired_quota_full(client, env): - o = Order.objects.get(id=env[2].id) - o.status = Order.STATUS_EXPIRED - o.save() - q = Quota.objects.create(event=env[0], size=0) - q.items.add(env[3]) - client.login(email='dummy@dummy.dummy', password='dummy') - client.post('/control/event/dummy/dummy/orders/FOO/transition', { - 'status': 'p' - }) - o = Order.objects.get(id=env[2].id) - assert o.status == Order.STATUS_EXPIRED - - @pytest.mark.django_db @pytest.mark.parametrize("process", [ # (Old status, new status, success expected) (Order.STATUS_CANCELLED, Order.STATUS_PAID, False), (Order.STATUS_CANCELLED, Order.STATUS_PENDING, False), (Order.STATUS_CANCELLED, Order.STATUS_REFUNDED, False), + (Order.STATUS_CANCELLED, Order.STATUS_EXPIRED, False), (Order.STATUS_PAID, Order.STATUS_PENDING, True), (Order.STATUS_PAID, Order.STATUS_CANCELLED, False), (Order.STATUS_PAID, Order.STATUS_REFUNDED, True), + (Order.STATUS_PAID, Order.STATUS_EXPIRED, False), (Order.STATUS_PENDING, Order.STATUS_CANCELLED, True), (Order.STATUS_PENDING, Order.STATUS_PAID, True), (Order.STATUS_PENDING, Order.STATUS_REFUNDED, False), + (Order.STATUS_PENDING, Order.STATUS_EXPIRED, True), (Order.STATUS_REFUNDED, Order.STATUS_CANCELLED, False), (Order.STATUS_REFUNDED, Order.STATUS_PAID, False), - (Order.STATUS_REFUNDED, Order.STATUS_PENDING, False) + (Order.STATUS_REFUNDED, Order.STATUS_PENDING, False), + (Order.STATUS_REFUNDED, Order.STATUS_EXPIRED, False), ]) def test_order_transition(client, env, process): o = Order.objects.get(id=env[2].id) o.status = process[0] o.save() client.login(email='dummy@dummy.dummy', password='dummy') + client.get('/control/event/dummy/dummy/orders/FOO/transition?status=' + process[1]) client.post('/control/event/dummy/dummy/orders/FOO/transition', { 'status': process[1] }) @@ -278,6 +321,101 @@ def test_order_download_success(client, env, mocker): assert dl == response['Location'] +@pytest.mark.django_db +def test_order_invoice_create_forbidden(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + env[0].settings.set('invoice_generate', 'no') + response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True) + assert 'alert-danger' in response.rendered_content + + +@pytest.mark.django_db +def test_order_invoice_create_duplicate(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + generate_invoice(env[2]) + env[0].settings.set('invoice_generate', 'admin') + response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True) + assert 'alert-danger' in response.rendered_content + + +@pytest.mark.django_db +def test_order_invoice_create_ok(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + env[0].settings.set('invoice_generate', 'admin') + response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True) + assert 'alert-success' in response.rendered_content + assert env[2].invoices.exists() + + +@pytest.mark.django_db +def test_order_invoice_regenerate(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + i = generate_invoice(env[2]) + InvoiceAddress.objects.create(name='Foo', order=env[2]) + env[0].settings.set('invoice_generate', 'admin') + response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % i.pk, {}, follow=True) + assert 'alert-success' in response.rendered_content + i.refresh_from_db() + assert 'Foo' in i.invoice_to + assert env[2].invoices.exists() + + +@pytest.mark.django_db +def test_order_invoice_regenerate_canceled(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + i = generate_invoice(env[2]) + generate_cancellation(i) + response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % i.pk, {}, follow=True) + assert 'alert-danger' in response.rendered_content + + +@pytest.mark.django_db +def test_order_invoice_regenerate_unknown(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % 3, {}, follow=True) + assert 'alert-danger' in response.rendered_content + + +@pytest.mark.django_db +def test_order_invoice_reissue(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + i = generate_invoice(env[2]) + InvoiceAddress.objects.create(name='Foo', order=env[2]) + env[0].settings.set('invoice_generate', 'admin') + response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % i.pk, {}, follow=True) + assert 'alert-success' in response.rendered_content + i.refresh_from_db() + assert env[2].invoices.count() == 3 + assert 'Foo' not in env[2].invoices.all()[0].invoice_to + assert 'Foo' not in env[2].invoices.all()[1].invoice_to + assert 'Foo' in env[2].invoices.all()[2].invoice_to + + +@pytest.mark.django_db +def test_order_invoice_reissue_canceled(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + i = generate_invoice(env[2]) + generate_cancellation(i) + response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % i.pk, {}, follow=True) + assert 'alert-danger' in response.rendered_content + + +@pytest.mark.django_db +def test_order_invoice_reissue_unknown(client, env): + client.login(email='dummy@dummy.dummy', password='dummy') + response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % 3, {}, follow=True) + assert 'alert-danger' in response.rendered_content + + +@pytest.mark.django_db +def test_order_resend_link(client, env): + mail.outbox = [] + client.login(email='dummy@dummy.dummy', password='dummy') + response = client.post('/control/event/dummy/dummy/orders/FOO/resend', {}, follow=True) + assert 'alert-success' in response.rendered_content + assert 'FOO' in mail.outbox[0].body + + @pytest.mark.django_db def test_order_extend_not_pending(client, env): o = Order.objects.get(id=env[2].id) diff --git a/src/tests/control/test_vouchers.py b/src/tests/control/test_vouchers.py index a0f4521e07..bfe345ef2a 100644 --- a/src/tests/control/test_vouchers.py +++ b/src/tests/control/test_vouchers.py @@ -1,4 +1,5 @@ import datetime +import json from django.utils.timezone import now from tests.base import SoupTest, extract_form_fields @@ -71,6 +72,67 @@ class VoucherFormTest(SoupTest): else: assert doc.select(".alert-success") + def test_list(self): + self.event.vouchers.create(item=self.ticket, code='ABCDEFG') + doc = self.client.get('/control/event/%s/%s/vouchers/' % (self.orga.slug, self.event.slug)) + assert 'ABCDEFG' in doc.rendered_content + + def test_csv(self): + self.event.vouchers.create(item=self.ticket, code='ABCDEFG') + doc = self.client.get('/control/event/%s/%s/vouchers/?download=yes' % (self.orga.slug, self.event.slug)) + assert doc.content.strip() == '"Voucher code","Valid until","Product","Reserve quota","Bypass quota","Price",' \ + '"Tag","Redeemed"\r\n"ABCDEFG","","Early-bird ticket","No","No","","",' \ + '"No"'.encode('utf-8') + + def test_filter_status_valid(self): + v = self.event.vouchers.create(item=self.ticket) + doc = self.client.get('/control/event/%s/%s/vouchers/?status=v' % (self.orga.slug, self.event.slug)) + assert v.code in doc.rendered_content + v.redeemed = True + v.save() + doc = self.client.get('/control/event/%s/%s/vouchers/?status=v' % (self.orga.slug, self.event.slug)) + assert v.code not in doc.rendered_content + + def test_filter_status_redeemed(self): + v = self.event.vouchers.create(item=self.ticket, redeemed=True) + doc = self.client.get('/control/event/%s/%s/vouchers/?status=r' % (self.orga.slug, self.event.slug)) + assert v.code in doc.rendered_content + v.redeemed = False + v.save() + doc = self.client.get('/control/event/%s/%s/vouchers/?status=r' % (self.orga.slug, self.event.slug)) + assert v.code not in doc.rendered_content + + def test_filter_status_expired(self): + v = self.event.vouchers.create(item=self.ticket, valid_until=now() + datetime.timedelta(days=1)) + doc = self.client.get('/control/event/%s/%s/vouchers/?status=e' % (self.orga.slug, self.event.slug)) + assert v.code not in doc.rendered_content + v.valid_until = now() - datetime.timedelta(days=1) + v.save() + doc = self.client.get('/control/event/%s/%s/vouchers/?status=e' % (self.orga.slug, self.event.slug)) + assert v.code in doc.rendered_content + + def test_filter_tag(self): + self.event.vouchers.create(item=self.ticket, code='ABCDEFG', comment='Foo', tag='bar') + doc = self.client.get('/control/event/%s/%s/vouchers/?tag=bar' % (self.orga.slug, self.event.slug)) + assert 'ABCDEFG' in doc.rendered_content + doc = self.client.get('/control/event/%s/%s/vouchers/?tag=baz' % (self.orga.slug, self.event.slug)) + assert 'ABCDEFG' not in doc.rendered_content + + def test_search_code(self): + self.event.vouchers.create(item=self.ticket, code='ABCDEFG', comment='Foo') + doc = self.client.get('/control/event/%s/%s/vouchers/?search=ABCDEFG' % (self.orga.slug, self.event.slug)) + assert 'ABCDEFG' in doc.rendered_content + doc = self.client.get('/control/event/%s/%s/vouchers/?search=Foo' % (self.orga.slug, self.event.slug)) + assert 'ABCDEFG' in doc.rendered_content + doc = self.client.get('/control/event/%s/%s/vouchers/?search=12345' % (self.orga.slug, self.event.slug)) + assert 'ABCDEFG' not in doc.rendered_content + + def test_bulk_rng(self): + rng = self.client.get('/control/event/%s/%s/vouchers/rng?num=7' % (self.orga.slug, self.event.slug)) + codes = json.loads(rng.content.decode('utf-8'))['codes'] + assert len(codes) == 7 + assert all([len(r) == 16 for r in codes]) + def test_create_non_blocking_item_voucher(self): self._create_voucher({ 'itemvar': '%d' % self.ticket.pk @@ -331,3 +393,24 @@ class VoucherFormTest(SoupTest): 'codes': 'ABCDE\n%s' % v.code, 'itemvar': '%d' % self.shirt.pk, }, expected_failure=True) + + def test_delete_voucher(self): + v = self.event.vouchers.create(quota=self.quota_tickets) + doc = self.get_doc('/control/event/%s/%s/vouchers/%s/delete' % (self.orga.slug, self.event.slug, v.pk), + follow=True) + assert not doc.select(".alert-danger") + + doc = self.post_doc('/control/event/%s/%s/vouchers/%s/delete' % (self.orga.slug, self.event.slug, v.pk), + {}, follow=True) + assert doc.select(".alert-success") + assert not self.event.vouchers.filter(pk=v.id).exists() + + def test_delete_voucher_redeemed(self): + v = self.event.vouchers.create(quota=self.quota_tickets, redeemed=True) + doc = self.get_doc('/control/event/%s/%s/vouchers/%s/delete' % (self.orga.slug, self.event.slug, v.pk), + follow=True) + assert doc.select(".alert-danger") + + doc = self.post_doc('/control/event/%s/%s/vouchers/%s/delete' % (self.orga.slug, self.event.slug, v.pk), + {}, follow=True) + assert doc.select(".alert-danger") diff --git a/src/tests/presale/test_orders.py b/src/tests/presale/test_orders.py index 8ad11c8848..8e8082f154 100644 --- a/src/tests/presale/test_orders.py +++ b/src/tests/presale/test_orders.py @@ -9,6 +9,7 @@ from pretix.base.models import ( Event, Item, ItemCategory, ItemVariation, Order, OrderPosition, Organizer, Question, Quota, ) +from pretix.base.services.invoices import generate_invoice class OrdersTest(TestCase): @@ -49,7 +50,8 @@ class OrdersTest(TestCase): datetime=now() - datetime.timedelta(days=3), expires=now() + datetime.timedelta(days=11), total=Decimal("23"), - payment_provider='banktransfer' + payment_provider='banktransfer', + locale='en' ) self.ticket_pos = OrderPosition.objects.create( order=self.order, @@ -108,6 +110,14 @@ class OrdersTest(TestCase): '/%s/%s/order/%s/123/cancel' % (self.orga.slug, self.event.slug, self.not_my_order.code) ) assert response.status_code == 404 + response = self.client.post( + '/%s/%s/order/ABCDE/123/cancel/do' % (self.orga.slug, self.event.slug) + ) + assert response.status_code == 404 + response = self.client.post( + '/%s/%s/order/%s/123/cancel/do' % (self.orga.slug, self.event.slug, self.not_my_order.code) + ) + assert response.status_code == 404 def test_orders_detail(self): response = self.client.get( @@ -229,6 +239,10 @@ class OrdersTest(TestCase): def test_orders_cancel_invalid(self): self.order.status = Order.STATUS_PAID self.order.save() + r = self.client.post( + '/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), { + }, follow=True) + assert 'btn-danger' not in r.rendered_content self.client.post( '/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), { }, follow=True) @@ -258,6 +272,37 @@ class OrdersTest(TestCase): self.order.refresh_from_db() assert self.order.status == Order.STATUS_PENDING + def test_invoice_create_notallowed(self): + self.event.settings.set('invoice_generate', 'no') + response = self.client.post( + '/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), + {}, follow=True) + assert 'alert-danger' in response.rendered_content + + def test_invoice_create_duplicate(self): + self.event.settings.set('invoice_generate', 'user') + generate_invoice(self.order) + response = self.client.post( + '/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), + {}, follow=True) + assert 'alert-danger' in response.rendered_content + + def test_invoice_create_wrong_secret(self): + self.event.settings.set('invoice_generate', 'user') + generate_invoice(self.order) + response = self.client.post( + '/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, '1234'), + {}) + assert 404 == response.status_code + + def test_invoice_create_ok(self): + self.event.settings.set('invoice_generate', 'user') + response = self.client.post( + '/%s/%s/order/%s/%s/invoice' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), + {}, follow=True) + assert 'alert-success' in response.rendered_content + assert self.order.invoices.exists() + def test_orders_download(self): self.event.settings.set('ticket_download', True) del self.event.settings['ticket_download_date']