Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/bittan/bittan/models/chapter_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class ChapterEvent(models.Model):
event_at = models.DateTimeField()
door_open_before = models.DurationField(default=timezone.timedelta(hours=1))

# First come first serve.
fcfs = models.BooleanField(default=True)

def save(self, *args, **kwargs):
if not self.swish_message:
self.swish_message = self.title[:50]
Expand Down
76 changes: 74 additions & 2 deletions backend/bittan/bittan/tests/views/test_reserve_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,36 @@ def test_reserve_ticket(self):
content_type="application/json"
)
payment_pk = Payment.objects.first().pk
payment_email = Payment.objects.first().email
self.assertEqual(response.status_code, 201, "/reserve_ticket/ did not return status code 201 correctly. ")
self.assertEqual(response.data, payment_pk)
self.assertEqual(payment_email, "mail@mail.com")
payment = Payment.objects.get(id=payment_pk)
self.assertEqual(payment.status, PaymentStatus.RESERVED)
self.assertEqual(payment.email, "mail@mail.com")

def test_reserve_ticket_no_form_no_fcfs(self):
self.test_event.fcfs = False
self.test_event.save()
response = self.client.post(
"/reserve_ticket/",
{
"chapter_event": str(self.test_event.pk),
"email_address": "mail@mail.com",
"tickets": [
{
"ticket_type": self.test_ticket.pk,
"count": 1
}
]
},
content_type="application/json"
)
payment_pk = Payment.objects.first().pk
self.assertEqual(response.status_code, 201, "/reserve_ticket/ did not return status code 201 correctly. ")
self.assertEqual(response.data, payment_pk)
payment = Payment.objects.get(id=payment_pk)
self.assertEqual(payment.status, PaymentStatus.FORM_SUBMITTED)
self.assertEqual(payment.email, "mail@mail.com")


def test_too_many_tickets(self):
response = self.client.post(
Expand Down Expand Up @@ -103,6 +129,52 @@ def test_out_of_tickets(self):
)
self.assertEqual(response.status_code, 403)

def test_out_of_tickets_non_fcfs(self):
self.test_event.max_tickets_per_payment = 10
self.test_event.fcfs = False
self.test_event.save()
prep_res = self.client.post(
"/reserve_ticket/",
{
"chapter_event": str(self.test_event.pk),
"email_address": "mail@mail.com",
"tickets": [
{
"ticket_type": self.test_ticket.pk,
"count": 10
}
]
},
content_type="application/json"
)

if prep_res.status_code != 201:
raise Exception("Failed to perform reservation of tickets in preparation for testing test_expired_session_out_of_tickets.")

self.client = Client()

response = self.client.post(
"/reserve_ticket/",
{
"chapter_event": str(self.test_event.pk),
"email_address": "mail@mail.com",
"tickets": [
{
"ticket_type": self.test_ticket.pk,
"count": 1
}
]
},
content_type="application/json"
)

payment= Payment.objects.exclude(id=prep_res.data).first()
payment_email = payment.email
self.assertEqual(response.status_code, 201, "/reserve_ticket/ did not return status code 201 correctly. ")
self.assertEqual(response.data, payment.id)
self.assertEqual(payment_email, "mail@mail.com")


def test_negative_tickets(self):
response = self.client.post(
"/reserve_ticket/",
Expand Down
139 changes: 136 additions & 3 deletions backend/bittan/bittan/tests/views/test_start_payment.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from bittan.models.payment import PaymentStatus, PaymentMethod
from unittest.mock import patch
from bittan.models.question import QuestionType
from django.test import TestCase, Client


from datetime import datetime
from django.utils import timezone

from bittan.models import TicketType, ChapterEvent, Payment
from bittan.models import TicketType, ChapterEvent, Payment, Question
from bittan.services.swish.swish import Swish

class StartPaymentTest(TestCase):
Expand Down Expand Up @@ -44,7 +45,6 @@ def setUp(self):
self.swish = Swish.get_instance()

def test_start_payment(self):
mail_address = "mail@mail.com"
response = self.client.post(
"/start_payment/",
{
Expand All @@ -58,10 +58,143 @@ def test_start_payment(self):
swish_payment_request = self.swish.get_payment_request(payment.swish_id)

self.assertEqual(payment.payment_started, True)
self.assertEqual(payment.email, mail_address)
self.assertEqual(payment.status, PaymentStatus.RESERVED)
self.assertEqual(payment.payment_method, PaymentMethod.SWISH)
self.assertEqual(swish_payment_request.amount, 4*self.test_ticket.price)

def test_start_payment_non_fcfs_non_confirmed(self):
self.test_event.fcfs = False
self.test_event.save()
response = self.client.post(
"/start_payment/",
{
"session_id": self.session_id
}
)

self.assertEqual(response.status_code, 403)
self.assertEqual(response.data, "PaymentNotPayable")

payment = Payment.objects.get(pk=self.session_id)
self.assertEqual(payment.payment_started, False)
self.assertEqual(payment.status, PaymentStatus.RESERVED)

def test_start_payment_non_fcfs_confirmed(self):
self.test_event.fcfs = False
self.test_event.save()
payment = Payment.objects.get(pk=self.session_id)
payment.status = PaymentStatus.CONFIRMED
payment.save()
response = self.client.post(
"/start_payment/",
{
"session_id": self.session_id
}
)

self.assertEqual(response.status_code, 200)

payment = Payment.objects.get(pk=self.session_id)
swish_payment_request = self.swish.get_payment_request(payment.swish_id)

self.assertEqual(payment.payment_started, True)
self.assertEqual(payment.status, PaymentStatus.CONFIRMED)
self.assertEqual(payment.payment_method, PaymentMethod.SWISH)
self.assertEqual(swish_payment_request.amount, 4*self.test_ticket.price)

def test_start_payment_form_fcfs(self):
q1 = Question.objects.create(
title="Test question",
question_type=QuestionType.RADIO,
chapter_event=self.test_event
)
response = self.client.post(
"/start_payment/",
{
"session_id": self.session_id
}
)

self.assertEqual(response.status_code, 403)
self.assertEqual(response.data, "PaymentNotPayable")

payment = Payment.objects.get(pk=self.session_id)
self.assertEqual(payment.payment_started, False)
self.assertEqual(payment.status, PaymentStatus.RESERVED)

payment.status = PaymentStatus.FORM_SUBMITTED
payment.save()
response = self.client.post(
"/start_payment/",
{
"session_id": self.session_id
}
)
self.assertEqual(response.status_code, 200)

payment = Payment.objects.get(pk=self.session_id)
swish_payment_request = self.swish.get_payment_request(payment.swish_id)

self.assertEqual(payment.payment_started, True)
self.assertEqual(payment.status, PaymentStatus.FORM_SUBMITTED)
self.assertEqual(payment.payment_method, PaymentMethod.SWISH)
self.assertEqual(swish_payment_request.amount, 4*self.test_ticket.price)

def test_start_payment_form_non_fcfs(self):
q1 = Question.objects.create(
title="Test question",
question_type=QuestionType.RADIO,
chapter_event=self.test_event
)
self.test_event.fcfs = False
self.test_event.save()
response = self.client.post(
"/start_payment/",
{
"session_id": self.session_id
}
)

self.assertEqual(response.status_code, 403)
self.assertEqual(response.data, "PaymentNotPayable")

payment = Payment.objects.get(pk=self.session_id)
self.assertEqual(payment.payment_started, False)
self.assertEqual(payment.status, PaymentStatus.RESERVED)

payment.status = PaymentStatus.FORM_SUBMITTED
payment.save()
response = self.client.post(
"/start_payment/",
{
"session_id": self.session_id
}
)
self.assertEqual(response.status_code, 403)
self.assertEqual(response.data, "PaymentNotPayable")

payment = Payment.objects.get(pk=self.session_id)
self.assertEqual(payment.payment_started, False)
self.assertEqual(payment.status, PaymentStatus.FORM_SUBMITTED)

payment.status = PaymentStatus.CONFIRMED
payment.save()
response = self.client.post(
"/start_payment/",
{
"session_id": self.session_id
}
)
self.assertEqual(response.status_code, 200)

payment = Payment.objects.get(pk=self.session_id)
swish_payment_request = self.swish.get_payment_request(payment.swish_id)

self.assertEqual(payment.payment_started, True)
self.assertEqual(payment.status, PaymentStatus.CONFIRMED)
self.assertEqual(payment.payment_method, PaymentMethod.SWISH)
self.assertEqual(swish_payment_request.amount, 4*self.test_ticket.price)


def test_no_session_id(self):
response = self.client.post(
Expand Down
11 changes: 9 additions & 2 deletions backend/bittan/bittan/views/reserve_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def reserve_ticket(request: Request) -> Response:
status=status.HTTP_403_FORBIDDEN
)

if reservation_count > chapter_event.total_seats - chapter_event.alive_ticket_count:
if chapter_event.fcfs and reservation_count > chapter_event.total_seats - chapter_event.alive_ticket_count:
return Response(
{
"error": "OutOfTickets",
Expand All @@ -92,10 +92,17 @@ def reserve_ticket(request: Request) -> Response:
status=status.HTTP_403_FORBIDDEN
)

creation_status = PaymentStatus.RESERVED
if not chapter_event.fcfs and not chapter_event.question_set.exists():
# We set creation_status directly to FORM_SUBMITTED if we have no form
# and the event is not first come first serve. This is to ensure that the
# tickets are not cleaned.
creation_status = PaymentStatus.FORM_SUBMITTED

payment = Payment.objects.create(
expires_at = timezone.now() + chapter_event.reservation_duration,
swish_id = None,
status = PaymentStatus.RESERVED,
status = creation_status,
email = response_data["email_address"],
)

Expand Down
18 changes: 15 additions & 3 deletions backend/bittan/bittan/views/start_payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ def start_payment(request):

chapter_event = tickets.first().chapter_event

if payment.status != PaymentStatus.RESERVED:
if payment.status not in (PaymentStatus.RESERVED, PaymentStatus.FORM_SUBMITTED, PaymentStatus.CONFIRMED):
# TODO This comparison and update should also happen on the database
if tickets.count() > chapter_event.total_seats - chapter_event.alive_ticket_count:
if chapter_event.fcfs and tickets.count() > chapter_event.total_seats - chapter_event.alive_ticket_count:
payment.status = PaymentStatus.FAILED_EXPIRED_RESERVATION
payment.save()
return Response(
Expand All @@ -67,12 +67,24 @@ def start_payment(request):
payment.expires_at = timezone.now() + chapter_event.reservation_duration
payment.status = PaymentStatus.RESERVED
payment.save()


good_status = PaymentStatus.RESERVED
if not chapter_event.fcfs:
good_status = PaymentStatus.CONFIRMED
elif chapter_event.question_set.exists():
good_status = PaymentStatus.FORM_SUBMITTED

if payment.status != good_status:
return Response(
"PaymentNotPayable",
status=status.HTTP_403_FORBIDDEN
)

# "Atomically" update the payment status.
Payment.objects.filter(
pk = payment_id,
status = PaymentStatus.RESERVED, # These are just so that we are sure that the payment is in the required status. If it is not get will throw an error and that should be OK.
status = good_status, # These are just so that we are sure that the payment is in the required status. If it is not get will throw an error and that should be OK.
payment_started = False
).update(
payment_started = True
Expand Down