def test_rules_variation(item, position, clist): v1 = item.variations.create(value="A") v2 = item.variations.create(value="B") position.variation = v2 position.save() clist.rules = { "inList": [ {"var": "variation"}, { "objectList": [ {"lookup": ["variation", str(v1.pk), "Ticket – A"]}, ] } ] } clist.save() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert not OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() assert excinfo.value.code == 'rules' assert 'Ticket type not allowed' in str(excinfo.value) clist.rules = { "inList": [ {"var": "variation"}, { "objectList": [ {"lookup": ["variation", str(v1.pk), "Ticket – A"]}, {"lookup": ["variation", str(v2.pk), "Ticket – B"]}, ] } ] } clist.save() assert OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() perform_checkin(position, clist, {})
def test_rules_product(event, position, clist): i2 = event.items.create(name="Ticket", default_price=3, admission=True) clist.rules = { "inList": [ {"var": "product"}, { "objectList": [ {"lookup": ["product", str(i2.pk), "Ticket"]}, ] } ] } clist.save() assert not OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' assert 'Ticket type not allowed' in str(excinfo.value) clist.rules = { "inList": [ {"var": "product"}, { "objectList": [ {"lookup": ["product", str(i2.pk), "Ticket"]}, {"lookup": ["product", str(position.item.pk), "Ticket"]}, ] } ] } clist.save() assert OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() perform_checkin(position, clist, {})
def test_rules_product(event, position, clist): i2 = event.items.create(name="Ticket", default_price=3, admission=True) clist.rules = { "inList": [ {"var": "product"}, { "objectList": [ {"lookup": ["product", str(i2.pk), "Ticket"]}, ] } ] } clist.save() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' clist.rules = { "inList": [ {"var": "product"}, { "objectList": [ {"lookup": ["product", str(i2.pk), "Ticket"]}, {"lookup": ["product", str(position.item.pk), "Ticket"]}, ] } ] } clist.save() perform_checkin(position, clist, {})
def test_rules_variation(item, position, clist): v1 = item.variations.create(value="A") v2 = item.variations.create(value="B") position.variation = v2 position.save() clist.rules = { "inList": [ {"var": "variation"}, { "objectList": [ {"lookup": ["variation", str(v1.pk), "Ticket – A"]}, ] } ] } clist.save() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' clist.rules = { "inList": [ {"var": "variation"}, { "objectList": [ {"lookup": ["variation", str(v1.pk), "Ticket – A"]}, {"lookup": ["variation", str(v2.pk), "Ticket – B"]}, ] } ] } clist.save() perform_checkin(position, clist, {})
def test_rules_reasoning_prefer_date_over_product(event, position, clist): i2 = event.items.create(name="Ticket", default_price=3, admission=True) clist.rules = { "or": [ { "inList": [ {"var": "product"}, { "objectList": [ {"lookup": ["product", str(i2.pk), "Ticket"]}, ] } ] }, { "and": [ {"isAfter": [{"var": "now"}, {"buildTime": ["custom", "2020-01-02T10:00:00.000Z"]}, None]}, {"isBefore": [{"var": "now"}, {"buildTime": ["custom", "2020-01-02T18:00:00.000Z"]}, None]}, ] } ] } clist.save() with freeze_time("2020-01-02 20:00:00Z"): with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' assert 'Only allowed before 19:00' in str(excinfo.value)
def test_position_queries(django_assert_num_queries, position, clist): with django_assert_num_queries( 12 if 'sqlite' in settings.DATABASES['default']['ENGINE'] else 11) as captured: perform_checkin(position, clist, {}) if 'sqlite' not in settings.DATABASES['default']['ENGINE']: assert any('FOR UPDATE' in s['sql'] for s in captured)
def test_rules_reasoning_prefer_number_over_date(event, position, clist): clist.rules = { "and": [{ "isAfter": [{ "var": "now" }, { "buildTime": ["custom", "2020-01-02T10:00:00.000Z"] }, None] }, { "isBefore": [{ "var": "now" }, { "buildTime": ["custom", "2020-01-02T18:00:00.000Z"] }, None] }, { ">": [{ "var": "entries_today" }, 3] }] } clist.save() with freeze_time("2020-01-01 20:00:00Z"): with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' assert 'Minimum number of entries today exceeded' in str(excinfo.value)
def test_multi_entry_repeat_nonce(position, clist): clist.allow_multiple_entries = True clist.save() perform_checkin(position, clist, {}, nonce='foo') perform_checkin(position, clist, {}, nonce='foo') assert position.checkins.count() == 1
def test_rules_time_isafter_no_tolerance(event, position, clist): # Ticket is valid only after admission time event.settings.timezone = 'Europe/Berlin' event.date_from = event.timezone.localize(datetime(2020, 1, 1, 12, 0, 0)) # also tests that date_admission falls back to date_from event.save() clist.rules = { "isAfter": [{ "var": "now" }, { "buildTime": ["date_admission"] }] } clist.save() with freeze_time("2020-01-01 10:51:00"): assert not OrderPosition.objects.filter( SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' with freeze_time("2020-01-01 11:01:00"): assert OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() perform_checkin(position, clist, {})
def test_unpaid_ignore_without_include_pendung(position, clist): o = position.order o.status = Order.STATUS_PENDING o.save() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'unpaid'
def test_checkin_all_subevents(position, clist, event): event.has_subevents = True event.save() se1 = event.subevents.create(name="Foo", date_from=event.date_from) position.subevent = se1 position.save() perform_checkin(position, clist, {})
def test_unpaid_include_pending_ignore(position, clist): o = position.order o.status = Order.STATUS_PENDING o.save() clist.include_pending = True clist.save() perform_checkin(position, clist, {}, ignore_unpaid=True)
def test_multi_entry(position, clist): clist.allow_multiple_entries = True clist.save() perform_checkin(position, clist, {}) perform_checkin(position, clist, {}) assert position.checkins.count() == 2
def test_rules_time_isbefore_with_tolerance(event, position, clist): # Ticket is valid until 10 minutes after end time event.settings.timezone = 'Europe/Berlin' event.date_to = event.timezone.localize(datetime(2020, 1, 1, 12, 0, 0)) event.save() clist.rules = { "isBefore": [{ "var": "now" }, { "buildTime": ["date_to"] }, 10] } clist.save() with freeze_time("2020-01-01 11:11:00"): assert not OrderPosition.objects.filter( SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' assert 'Only allowed before 12:10' in str(excinfo.value) with freeze_time("2020-01-01 11:09:00"): assert OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() perform_checkin(position, clist, {})
def test_rules_isafter_subevent(position, clist, event): event.has_subevents = True event.save() event.settings.timezone = 'Europe/Berlin' se1 = event.subevents.create(name="Foo", date_from=event.timezone.localize( datetime(2020, 2, 1, 12, 0, 0))) position.subevent = se1 position.save() clist.rules = { "isAfter": [{ "var": "now" }, { "buildTime": ["date_admission"] }] } clist.save() with freeze_time("2020-02-01 10:51:00"): assert not OrderPosition.objects.filter( SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' with freeze_time("2020-02-01 11:01:00"): assert OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() perform_checkin(position, clist, {})
def test_auto_check_out_only_if_checked_in_before_exit_all_at(event, position, clist): clist.exit_all_at = event.timezone.localize(datetime(2020, 1, 2, 3, 0)) clist.save() with freeze_time("2020-01-02 04:05:00+01:00"): perform_checkin(position, clist, {}) process_exit_all(sender=None) assert position.checkins.count() == 1
def test_checkin_invalid_product(position, clist): clist.all_products = False clist.save() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'product' clist.limit_products.add(position.item) perform_checkin(position, clist, {})
def test_single_entry(position, clist): perform_checkin(position, clist, {}) with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'already_redeemed' assert position.checkins.count() == 1
def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') op = self.get_object(ignore_status=True) if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value(self.request.data.get('datetime')) else: dt = now() given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get('questions_supported', True), canceled_supported=self.request.data.get('canceled_supported', False), user=self.request.user, auth=self.request.auth, ) except RequiredQuestionsError as e: return Response({ 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data, 'questions': [ QuestionSerializer(q).data for q in e.questions ] }, status=400) except CheckInError as e: return Response({ 'status': 'error', 'reason': e.code, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=400) else: return Response({ 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=201)
def test_required_online_question_missing(event, position, clist): q = event.questions.create( question="Quo vadis?", type="S", required=True, ask_during_checkin=False, ) q.items.add(position.item) perform_checkin(position, clist, {}, questions_supported=True)
def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') op = self.get_object(ignore_status=True) if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value(self.request.data.get('datetime')) else: dt = now() given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get('questions_supported', True), user=self.request.user, auth=self.request.auth, ) except RequiredQuestionsError as e: return Response({ 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data, 'questions': [ QuestionSerializer(q).data for q in e.questions ] }, status=400) except CheckInError as e: return Response({ 'status': 'error', 'reason': e.code, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=400) else: return Response({ 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=201)
def test_checkin_canceled_position(position, clist): position.canceled = True position.save() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'unpaid' with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}, canceled_supported=True) assert excinfo.value.code == 'canceled' assert position.checkins.count() == 0
def test_question_filled_previously(event, position, clist): q = event.questions.create( question="Quo vadis?", type="S", required=True, ask_during_checkin=True, ) q.items.add(position.item) position.answers.create(question=q, answer='Foo') perform_checkin(position, clist, {}, questions_supported=True)
def post(self, request, **kwargs): secret = request.POST.get('secret', '!INVALID!') force = request.POST.get('force', 'false') in ('true', 'True') ignore_unpaid = request.POST.get('ignore_unpaid', 'false') in ('true', 'True') nonce = request.POST.get('nonce') response = { 'version': API_VERSION } if 'datetime' in request.POST: dt = dateutil.parser.parse(request.POST.get('datetime')) else: dt = now() try: op = OrderPosition.objects.get(order__event=self.event, secret=secret, subevent=self.subevent) except OrderPosition.DoesNotExist: response['status'] = 'error' response['reason'] = 'unknown_ticket' else: given_answers = {} for q in op.item.questions.filter(ask_during_checkin=True): if 'answer_{}'.format(q.pk) in request.POST: try: given_answers[q] = q.clean_answer(request.POST.get('answer_{}'.format(q.pk))) except ValidationError: pass try: if not self.config.all_items and op.item_id not in [i.pk for i in self.config.items.all()]: raise CheckInError('', 'product') perform_checkin( op=op, clist=self.config.list, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=bool(request.POST.get('questions_supported')) ) except RequiredQuestionsError as e: response['status'] = 'incomplete' response['questions'] = [serialize_question(q) for q in e.questions] except CheckInError as e: response['status'] = 'error' response['reason'] = e.code else: response['status'] = 'ok' response['data'] = serialize_op(op, redeemed=op.order.status == Order.STATUS_PAID or force, clist=self.config.list) return JsonResponse(response)
def test_rules_scan_number(position, clist): # Ticket is valid three times clist.allow_multiple_entries = True clist.rules = {"<": [{"var": "entries_number"}, 3]} clist.save() perform_checkin(position, clist, {}) perform_checkin(position, clist, {}) perform_checkin(position, clist, {}, type=Checkin.TYPE_EXIT) perform_checkin(position, clist, {}) with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules'
def test_rules_time_isafter_custom_time(event, position, clist): # Ticket is valid starting at a custom time event.settings.timezone = 'Europe/Berlin' clist.rules = {"isAfter": [{"var": "now"}, {"buildTime": ["custom", "2020-01-01T22:00:00.000Z"]}, None]} clist.save() with freeze_time("2020-01-01 21:55:00"): with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' with freeze_time("2020-01-01 22:05:00"): perform_checkin(position, clist, {})
def test_question_filled(event, position, clist): q = event.questions.create( question="Quo vadis?", type="S", required=True, ask_during_checkin=True, ) q.items.add(position.item) perform_checkin(position, clist, {q: 'Foo'}, questions_supported=True) a = position.answers.get() assert a.question == q assert a.answer == 'Foo'
def test_optional_question_missing(event, position, clist): q = event.questions.create( question="Quo vadis?", type="S", required=False, ask_during_checkin=True, ) q.items.add(position.item) with pytest.raises(RequiredQuestionsError) as excinfo: perform_checkin(position, clist, {}, questions_supported=True) assert excinfo.value.code == 'incomplete' assert excinfo.value.questions == [q]
def test_checkin_invalid_subevent(position, clist, event): event.has_subevents = True event.save() se1 = event.subevents.create(name="Foo", date_from=event.date_from) se2 = event.subevents.create(name="Foo", date_from=event.date_from) position.subevent = se1 position.save() clist.subevent = se2 clist.save() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'product'
def test_rules_time_isafter_custom_datetime(event, position, clist): # Ticket is valid starting at a custom time event.settings.timezone = 'Europe/Berlin' clist.rules = {"isAfter": [{"var": "now"}, {"buildTime": ["custom", "2020-01-01T23:00:00.000+01:00"]}, None]} clist.save() with freeze_time("2020-01-01 21:55:00+00:00"): assert not OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' with freeze_time("2020-01-01 22:05:00+00:00"): assert OrderPosition.objects.filter(SQLLogic(clist).apply(clist.rules), pk=position.pk).exists() perform_checkin(position, clist, {})
def test_rules_time_isbefore_with_tolerance(event, position, clist): # Ticket is valid until 10 minutes after end time event.settings.timezone = 'Europe/Berlin' event.date_to = event.timezone.localize(datetime(2020, 1, 1, 12, 0, 0)) event.save() clist.rules = {"isBefore": [{"var": "now"}, {"buildTime": ["date_to"]}, 10]} clist.save() with freeze_time("2020-01-01 11:11:00"): with pytest.raises(CheckInError) as excinfo: perform_checkin(position, clist, {}) assert excinfo.value.code == 'rules' with freeze_time("2020-01-01 11:09:00"): perform_checkin(position, clist, {})