1

How can I test if two users can reserve the same car simultaneously?

def test_if_two_users_can_reserve_the_same_car_simultaneously(self):
    with patch.object(
        timezone,
        "now",
        return_value=make_aware(
            datetime.datetime.combine(
                datetime.date.today() + datetime.timedelta(days=1), datetime.time(10, 30, 0)
            ),
            timezone=pytz.timezone("UTC"),
        ),
    ):
        self.client.login(username=self.user.username, password=self.PASSWORD)
        url = reverse("booking-list")
        data = {
            "book_for": datetime.datetime.combine(
                datetime.date.today() + datetime.timedelta(days=1), datetime.time(11, 30, 0)
            ),
        }
        response = self.client.post(url, data=data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    with patch.object(
        timezone,
        "now",
        return_value=make_aware(
            datetime.datetime.combine(
                datetime.date.today() + datetime.timedelta(days=1), datetime.time(10, 30, 0)
            ),
            timezone=pytz.timezone("UTC"),
        ),
    ):
        self.client.login(
            username=self.another_user.username, password=self.PASSWORD
        )
        url = reverse("booking-list")
        data = {
            "book_for": datetime.datetime.combine(
                datetime.date.today() + datetime.timedelta(days=1), datetime.time(11, 30, 0)
            ),
        }
        response = self.client.post(url, data=data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

This is how I wrote it, but in the unit test, it runs line by line. So first one will create then move to second one but I want them both run at same time. (Please answer with an example)

This is its model:

class Booking(models.Model):
    id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    user_location = models.PointField(help_text="user current location")
    user_address = models.TextField(help_text="user current address")
    start_location = models.PointField(
        blank=True,
        help_text="location where the user pick up the car from",
    )
    start_address = models.TextField(
        blank=True,
        default="",
        help_text="address where the user pick up the car from",
    )
    end_location = models.PointField(
        null=True,
        blank=True,
        help_text="desitination location that a user wants to drive to or driven by the driver",
    )
    end_address = models.TextField(
        blank=True,
        default="",
        help_text="desitination address that a user wants to drive to or driven by the driver",
    )
    vehicle = models.ForeignKey(
        Vehicle,
        on_delete=models.PROTECT,
    )
    book_for = models.DateTimeField()

    drop_off_datetime = models.DateTimeField(
        null=True,
        blank=True,
        help_text="the drop off time for booking",
    )
    status = models.CharField(
        max_length=25,
        choices=BookingStatus.choices,
        default=BookingStatus.OPEN,
    )
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

As you can see I used UUIDField.

def perform_create(self, serializer):
    serializer.save(user=self.request.user)

constraints:

class Meta:
    constraints = [
        UniqueConstraint(
            fields=["user"],
            condition=Q(status=BookingStatus.ACTIVE),
            name="unique_active_booking",
        )
    ]
    ordering = (
        "-book_for",
        "start_address",
    )
enter code here
sahaaari
  • 11
  • 3
  • There is no such thing as "at same time" in Python. What is it that you want to actually test? – AKX Oct 15 '22 at 09:56
  • There is a bug in my code. Two people tried to book the same car at the same time and they could. I want to fix this bug and test it locally and write a unit test for it. – sahaaari Oct 15 '22 at 11:57
  • No, two people don't do anything at _exactly the same time_, so it's useless to try and write a test that would do that. Instead, fix your code to have suitable database-level unique constraints or e.g. locks, so race conditions (which is what you have) don't happen. – AKX Oct 15 '22 at 12:00
  • Can you tell me more about it? how can I do that? is it called lock? – sahaaari Oct 15 '22 at 12:03
  • An unique constraint is likely what you're actually looking for. Please add the definition for your reservation model into your question and we can take a look. – AKX Oct 15 '22 at 12:06
  • You can see my model in the question. Some people told me I need transaction atomic. So I thought if I add @transaction.atomic decorator to the save method in views, this problem will fix. but I couldn't test to make sure. So I asked my question this way. – sahaaari Oct 15 '22 at 12:27
  • Atomic won't necessarily help at all. Please also add the code creating the booking - and please describe when the car shouldn't be bookable. – AKX Oct 15 '22 at 14:11
  • perform_create inside its views is creating the booking. We don't let users book a car at a time that another user booked it. But we didn't consider when they both booked at the same time. – sahaaari Oct 16 '22 at 11:10
  • Actually I think my problem is that I don't know about "Multiprocessing Lock in Python" – sahaaari Oct 16 '22 at 11:11
  • Multiprocessing also has _nothing_ to do with this. But since you don't have any constraints on your model, it's impossible to guarantee there aren't race conditions unless you use a lock (e.g. `select_for_update()` on the queryset, but that will lock the entire table). Please also add the logic with "doesn't let users book a car at the same time". – AKX Oct 16 '22 at 16:42
  • We have constraints. The model was too big and I couldn't show you all of it. I thought you only wanted to see the DB fields. – sahaaari Oct 16 '22 at 20:23
  • Well, your constraint only constrains one active booking per user, not that the same car couldn't be booked over the same period of time... – AKX Oct 16 '22 at 21:51
  • Sorry, I asked too many questions. I'm kind of a beginner at these things. How can I add another constraint to this one? (how add the same car couldn't be booked over the same period of time to this constraint? – sahaaari Oct 16 '22 at 22:24
  • How are you doing "We don't let users book a car at a time that another user booked it." currently? If you're not doing it with a constraint or a lock, it's not going to be waterproof. – AKX Oct 17 '22 at 07:29

0 Answers0