bookings = Mockery::mock(BookingRepository::class); $this->availability = Mockery::mock(AvailabilityRepository::class); $this->enrollments = Mockery::mock(EnrollmentRepository::class); $this->payments = Mockery::mock(PaymentService::class); $this->actions = new StudentActions($this->bookings, $this->availability, $this->enrollments, $this->payments); } public function testCancelLessonCancelsFreesSlotAndVoidsPayment(): void { $this->bookings->shouldReceive('findById')->with(12) ->andReturn(new Lesson(7, 5, 3, status: Lesson::STATUS_PENDING, paymentId: 40, id: 12)); $this->bookings->shouldReceive('updateStatus')->once()->with(12, Lesson::STATUS_CANCELLED)->andReturn(true); $this->availability->shouldReceive('release')->once()->with(7)->andReturn(true); $this->payments->shouldReceive('voidPending')->once()->with(40); self::assertTrue($this->actions->cancelLesson(12, 5)); } public function testCancelLessonRefusesAnotherStudentsLesson(): void { $this->bookings->shouldReceive('findById')->with(12) ->andReturn(new Lesson(7, 6, 3, status: Lesson::STATUS_PENDING, id: 12)); $this->bookings->shouldNotReceive('updateStatus'); $this->availability->shouldNotReceive('release'); self::assertFalse($this->actions->cancelLesson(12, 5)); } public function testCancelLessonRefusesAlreadyCancelledLesson(): void { $this->bookings->shouldReceive('findById')->with(12) ->andReturn(new Lesson(7, 5, 3, status: Lesson::STATUS_CANCELLED, id: 12)); $this->bookings->shouldNotReceive('updateStatus'); self::assertFalse($this->actions->cancelLesson(12, 5)); } public function testCancelLessonRefusesMissingLesson(): void { $this->bookings->shouldReceive('findById')->with(99)->andReturn(null); self::assertFalse($this->actions->cancelLesson(99, 5)); } public function testWithdrawEnrollmentCancelsAndVoidsPayment(): void { $this->enrollments->shouldReceive('findById')->with(3) ->andReturn(new Enrollment(1, 5, 3, Enrollment::STATUS_ACTIVE, 41, 3)); $this->enrollments->shouldReceive('updateStatus')->once()->with(3, Enrollment::STATUS_CANCELLED)->andReturn(true); $this->payments->shouldReceive('voidPending')->once()->with(41); self::assertTrue($this->actions->withdrawEnrollment(3, 5)); } public function testWithdrawEnrollmentRefusesInactiveEnrolment(): void { $this->enrollments->shouldReceive('findById')->with(3) ->andReturn(new Enrollment(1, 5, 3, Enrollment::STATUS_CANCELLED, null, 3)); $this->enrollments->shouldNotReceive('updateStatus'); self::assertFalse($this->actions->withdrawEnrollment(3, 5)); } public function testWithdrawEnrollmentRefusesAnotherStudentsEnrolment(): void { $this->enrollments->shouldReceive('findById')->with(3) ->andReturn(new Enrollment(1, 6, 3, Enrollment::STATUS_ACTIVE, null, 3)); $this->enrollments->shouldNotReceive('updateStatus'); self::assertFalse($this->actions->withdrawEnrollment(3, 5)); } public function testUpdateAccountSavesNameAndEmail(): void { Functions\when('is_email')->justReturn(true); Functions\when('email_exists')->justReturn(false); Functions\expect('wp_update_user') ->once() ->with(['ID' => 5, 'display_name' => 'New Name', 'user_email' => 'new@example.com']) ->andReturn(5); self::assertTrue($this->actions->updateAccount(5, 'New Name', 'new@example.com')); } public function testUpdateAccountAllowsKeepingOwnEmail(): void { Functions\when('is_email')->justReturn(true); Functions\when('email_exists')->justReturn(5); Functions\when('wp_update_user')->justReturn(5); self::assertTrue($this->actions->updateAccount(5, 'Name', 'same@example.com')); } public function testUpdateAccountRejectsEmptyName(): void { $result = $this->actions->updateAccount(5, '', 'new@example.com'); self::assertInstanceOf(\WP_Error::class, $result); self::assertSame('empty_name', $result->get_error_code()); } public function testUpdateAccountRejectsInvalidEmail(): void { Functions\when('is_email')->justReturn(false); $result = $this->actions->updateAccount(5, 'Name', 'not-an-email'); self::assertInstanceOf(\WP_Error::class, $result); self::assertSame('invalid_email', $result->get_error_code()); } public function testUpdateAccountRejectsEmailOwnedByAnotherUser(): void { Functions\when('is_email')->justReturn(true); Functions\when('email_exists')->justReturn(9); $result = $this->actions->updateAccount(5, 'Name', 'taken@example.com'); self::assertInstanceOf(\WP_Error::class, $result); self::assertSame('email_taken', $result->get_error_code()); } public function testUpdateAccountPassesThroughWpError(): void { Functions\when('is_email')->justReturn(true); Functions\when('email_exists')->justReturn(false); Functions\when('wp_update_user')->justReturn(new \WP_Error('update_failed', 'nope')); $result = $this->actions->updateAccount(5, 'Name', 'new@example.com'); self::assertInstanceOf(\WP_Error::class, $result); self::assertSame('update_failed', $result->get_error_code()); } }