11from django .test import TestCase
22from django .contrib .auth .models import User
33from django .utils .timezone import now , timedelta
4+ from django .core .exceptions import ValidationError
45from apps .users .models import TutorProfile , Profile
56from apps .bookings .models import Booking , Availability , Review
67from rest_framework .test import APITestCase , APIClient
@@ -69,6 +70,65 @@ def test_booking_cancellation(self):
6970 self .assertEqual (booking .booking_status , Booking .CANCELLED )
7071 self .assertFalse (updated_availability .is_booked )
7172
73+ def test_clean_raises_without_availability (self ):
74+ booking = Booking (
75+ student = self .student_user ,
76+ session_price = 20.00 ,
77+ availability = None
78+ )
79+ with self .assertRaises (ValidationError ) as ctx :
80+ booking .clean ()
81+ self .assertIn ("must be linked to an availability" , str (ctx .exception ))
82+
83+ def test_clean_raises_if_availability_already_booked (self ):
84+ self .availability .is_booked = True
85+ self .availability .save ()
86+
87+ booking = Booking (
88+ student = self .student_user ,
89+ session_price = 20.00 ,
90+ availability = self .availability
91+ )
92+ with self .assertRaises (ValidationError ) as ctx :
93+ booking .clean ()
94+ self .assertIn ("already booked" , str (ctx .exception ))
95+
96+ def test_save_does_not_book_if_cancelled (self ):
97+ booking = Booking .objects .create (
98+ student = self .student_user ,
99+ session_price = 25.00 ,
100+ availability = self .availability ,
101+ booking_status = Booking .CANCELLED
102+ )
103+ booking .refresh_from_db ()
104+ self .availability .refresh_from_db ()
105+ self .assertFalse (self .availability .is_booked )
106+
107+ def test_save_marks_availability_as_booked_unless_cancelled (self ):
108+ booking = Booking .objects .create (
109+ student = self .student_user ,
110+ session_price = 25.00 ,
111+ availability = self .availability ,
112+ booking_status = Booking .APPROVED
113+ )
114+ self .availability .refresh_from_db ()
115+ self .assertTrue (self .availability .is_booked )
116+
117+ def test_booking_detail_serializer_includes_read_only_booking (self ):
118+ from apps .bookings .serializers import BookingDetailSerializer
119+
120+ booking = Booking .objects .create (
121+ student = self .student_user ,
122+ session_price = 30.00 ,
123+ availability = self .availability
124+ )
125+
126+ serializer = BookingDetailSerializer (instance = booking )
127+ data = serializer .data
128+ self .assertIn ("student" , data )
129+ self .assertEqual (data ["student_username" ], self .student_user .username )
130+
131+
72132class BookingAPITestCase (APITestCase ):
73133 def setUp (self ):
74134 # Create tutor and tutor profile
@@ -137,4 +197,62 @@ def test_get_availability_by_tutor_and_date(self):
137197 date_str = self .availability .start_time .strftime ("%Y-%m-%d" )
138198 url = f"/bookings/availability/?tutor_id={ self .tutor .id } &date={ date_str } "
139199 response = self .client .get (url )
140- self .assertEqual (response .status_code , status .HTTP_200_OK )
200+ self .assertEqual (response .status_code , status .HTTP_200_OK )
201+
202+ def test_patch_booking_status (self ):
203+ new_availability = Availability .objects .create (
204+ tutor = self .tutor ,
205+ start_time = now () + timedelta (days = 2 ),
206+ end_time = now () + timedelta (days = 2 , hours = 1 ),
207+ is_booked = False
208+ )
209+
210+ booking = Booking .objects .create (
211+ student = self .student_user ,
212+ session_price = 30.00 ,
213+ availability = new_availability
214+ )
215+
216+ # Reset to unbooked for PATCH to
217+
218+ def test_put_booking (self ):
219+ new_availability = Availability .objects .create (
220+ tutor = self .tutor ,
221+ start_time = now () + timedelta (days = 3 ),
222+ end_time = now () + timedelta (days = 3 , hours = 1 ),
223+ is_booked = False
224+ )
225+
226+ booking = Booking .objects .create (
227+ student = self .student_user ,
228+ session_price = 25.00 ,
229+ availability = new_availability
230+ )
231+
232+ # Reset booking state for validation to pass
233+ new_availability .is_booked = False
234+ new_availability .save ()
235+
236+ url = f"/bookings/bookings_list/{ booking .payment_gateway_ref } /"
237+ data = {
238+ "session_price" : "35.00" ,
239+ "availability" : new_availability .id ,
240+ "booking_status" : Booking .FULFILLED
241+ }
242+
243+ response = self .client .put (url , data , format = "json" )
244+ booking .refresh_from_db ()
245+ self .assertEqual (response .status_code , status .HTTP_200_OK )
246+ self .assertEqual (float (booking .session_price ), 35.00 )
247+ self .assertEqual (booking .booking_status , Booking .FULFILLED )
248+
249+ def test_delete_booking (self ):
250+ booking = Booking .objects .create (
251+ student = self .student_user ,
252+ session_price = 30.00 ,
253+ availability = self .availability
254+ )
255+ url = f"/bookings/bookings_list/{ booking .payment_gateway_ref } /"
256+ response = self .client .delete (url )
257+ self .assertEqual (response .status_code , status .HTTP_204_NO_CONTENT )
258+ self .assertFalse (Booking .objects .filter (pk = booking .payment_gateway_ref ).exists ())
0 commit comments