bookings = Mockery::mock(BookingRepository::class); $this->availability = Mockery::mock(AvailabilityRepository::class); $this->enrollments = Mockery::mock(EnrollmentRepository::class); $this->payments = Mockery::mock(PaymentService::class); $this->cleanup = new DeletedUserCleanup( $this->bookings, $this->availability, $this->enrollments, $this->payments ); } public function testHooksBothSingleSiteAndNetworkDeletion(): void { Actions\expectAdded('delete_user')->once(); Actions\expectAdded('wpmu_delete_user')->once(); $this->cleanup->register(); } public function testEachUpcomingLessonIsCancelledItsSlotFreedAndItsPendingPaymentVoided(): void { $this->bookings->shouldReceive('findUpcomingForStudent')->with(5)->andReturn([ new Lesson(slotId: 7, studentId: 5, instructorId: 3, status: Lesson::STATUS_PENDING, paymentId: 40, id: 12), new Lesson(slotId: 8, studentId: 5, instructorId: 3, status: Lesson::STATUS_CONFIRMED, paymentId: null, id: 13), ]); $this->enrollments->shouldReceive('findByStudent')->with(5)->andReturn([]); $this->bookings->shouldReceive('updateStatus')->once()->with(12, Lesson::STATUS_CANCELLED)->andReturn(true); $this->bookings->shouldReceive('updateStatus')->once()->with(13, Lesson::STATUS_CANCELLED)->andReturn(true); // The point of the whole exercise: the times go back on sale. $this->availability->shouldReceive('release')->once()->with(7)->andReturn(true); $this->availability->shouldReceive('release')->once()->with(8)->andReturn(true); $this->payments->shouldReceive('voidPending')->once()->with(40); $this->payments->shouldReceive('voidPending')->once()->with(null); $this->cleanup->releaseBookings(5); } /** * A paid lesson is not credited back. The credit could only ever be spent on * the account being deleted, so writing one would be book-keeping nobody can * act on — a refund is the studio's call to make and record. */ public function testNoCreditIsIssuedForAPaidLesson(): void { $this->bookings->shouldReceive('findUpcomingForStudent')->with(5)->andReturn([ new Lesson(slotId: 7, studentId: 5, instructorId: 3, status: Lesson::STATUS_CONFIRMED, paymentId: 40, id: 12), ]); $this->enrollments->shouldReceive('findByStudent')->with(5)->andReturn([]); $this->bookings->shouldReceive('updateStatus')->andReturn(true); $this->availability->shouldReceive('release')->andReturn(true); $this->payments->shouldReceive('voidPending'); $this->payments->shouldNotReceive('creditForCancelledLesson'); $this->cleanup->releaseBookings(5); } public function testActiveEnrolmentsAreCancelledAndTheirPendingPaymentsVoided(): void { $this->bookings->shouldReceive('findUpcomingForStudent')->with(5)->andReturn([]); $this->enrollments->shouldReceive('findByStudent')->with(5)->andReturn([ new Enrollment(offeringId: 8, studentId: 5, instructorId: 3, paymentId: 41, id: 40), new Enrollment( offeringId: 9, studentId: 5, instructorId: 3, status: Enrollment::STATUS_CANCELLED, paymentId: 42, id: 41, ), ]); // Only the active one: a withdrawn enrolment is already holding nothing. $this->enrollments->shouldReceive('updateStatus')->once()->with(40, Enrollment::STATUS_CANCELLED)->andReturn(true); $this->payments->shouldReceive('voidPending')->once()->with(41); $this->cleanup->releaseBookings(5); } /** * Past lessons happened and may have been paid for, so they stay exactly as * they are — `findUpcomingForStudent` is what draws that line. */ public function testNothingHappensWhenTheAccountHasNothingBookedAhead(): void { $this->bookings->shouldReceive('findUpcomingForStudent')->with(5)->andReturn([]); $this->enrollments->shouldReceive('findByStudent')->with(5)->andReturn([]); $this->bookings->shouldNotReceive('updateStatus'); $this->availability->shouldNotReceive('release'); $this->enrollments->shouldNotReceive('updateStatus'); $this->cleanup->releaseBookings(5); } public function testAnInvalidUserIdIsIgnored(): void { $this->bookings->shouldNotReceive('findUpcomingForStudent'); $this->enrollments->shouldNotReceive('findByStudent'); $this->cleanup->releaseBookings(0); } }