CI / Coding Standards (pull_request) Successful in 27s
CI / Tests (PHP 8.1) (pull_request) Successful in 37s
CI / No Debug Code (pull_request) Successful in 9s
CI / Tests (PHP 8.3) (pull_request) Successful in 36s
CI / Tests (PHP 8.5) (pull_request) Successful in 40s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Static Analysis (pull_request) Successful in 48s
CI / Build Plugin Zip (pull_request) Skipped
Add an opt-in, per-instructor email notice sent when someone books one of their lessons or enrols in one of their group classes. Off by default and set from My Availability → Notifications; covers both the student/guardian REST flows and the studio's wp-admin "book/add for a student" forms. The opt-in check lives in InstructorNotificationMailer so no booking path can drift on who is mailed; lessons fire from LessonBooker::settle (the one step both booking paths reach), enrolments from EnrollmentEndpoint::enroll and GroupClassController::addDirect. The notice is a courtesy and never fails a booking or enrolment that otherwise succeeded. Co-authored-by: anthropic/claude-opus-4-8
115 lines
4.2 KiB
PHP
115 lines
4.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Unsupervised\Schedular\Tests\Unit\Auth;
|
|
|
|
use Brain\Monkey\Functions;
|
|
use Mockery;
|
|
use Unsupervised\Schedular\Auth\InstructorNotificationMailer;
|
|
use Unsupervised\Schedular\Auth\InstructorNotificationPref;
|
|
use Unsupervised\Schedular\Tests\Unit\TestCase;
|
|
|
|
class InstructorNotificationMailerTest extends TestCase
|
|
{
|
|
private InstructorNotificationPref&Mockery\MockInterface $pref;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->pref = Mockery::mock(InstructorNotificationPref::class);
|
|
}
|
|
|
|
private function mailer(): InstructorNotificationMailer
|
|
{
|
|
return new InstructorNotificationMailer($this->pref);
|
|
}
|
|
|
|
private function instructor(string $email): \WP_User
|
|
{
|
|
$user = Mockery::mock(\WP_User::class);
|
|
$user->user_email = $email;
|
|
|
|
return $user;
|
|
}
|
|
|
|
public function testLessonNoticeGoesToAnOptedInInstructor(): void
|
|
{
|
|
$this->pref->shouldReceive('wants')->with(9)->andReturn(true);
|
|
Functions\when('get_userdata')->justReturn($this->instructor('[email protected]'));
|
|
|
|
Functions\expect('wp_mail')
|
|
->once()
|
|
->with(
|
|
'[email protected]',
|
|
Mockery::on(static fn (string $subject): bool => str_contains($subject, '30 min piano')),
|
|
Mockery::on(static fn (string $body): bool => str_contains($body, 'Ada') && str_contains($body, 'Jul 1'))
|
|
)
|
|
->andReturn(true);
|
|
|
|
self::assertTrue($this->mailer()->notifyLessonBooked(9, 'Ada', '30 min piano', 'Jul 1, 2026 10:00 AM'));
|
|
}
|
|
|
|
public function testWeeklyLessonNoticeCountsTheOccurrences(): void
|
|
{
|
|
$this->pref->shouldReceive('wants')->with(9)->andReturn(true);
|
|
Functions\when('get_userdata')->justReturn($this->instructor('[email protected]'));
|
|
|
|
Functions\expect('wp_mail')
|
|
->once()
|
|
->with(
|
|
'[email protected]',
|
|
Mockery::type('string'),
|
|
Mockery::on(static fn (string $body): bool => str_contains($body, '3 weekly lessons'))
|
|
)
|
|
->andReturn(true);
|
|
|
|
self::assertTrue($this->mailer()->notifyLessonBooked(9, 'Ada', '30 min piano', 'Jul 1, 2026 10:00 AM', 3));
|
|
}
|
|
|
|
public function testEnrollmentNoticeGoesToAnOptedInInstructor(): void
|
|
{
|
|
$this->pref->shouldReceive('wants')->with(9)->andReturn(true);
|
|
Functions\when('get_userdata')->justReturn($this->instructor('[email protected]'));
|
|
|
|
Functions\expect('wp_mail')
|
|
->once()
|
|
->with(
|
|
'[email protected]',
|
|
Mockery::on(static fn (string $subject): bool => str_contains($subject, 'Choir')),
|
|
Mockery::on(static fn (string $body): bool => str_contains($body, 'Ada') && str_contains($body, 'Choir'))
|
|
)
|
|
->andReturn(true);
|
|
|
|
self::assertTrue($this->mailer()->notifyEnrollment(9, 'Ada', 'Choir'));
|
|
}
|
|
|
|
public function testSendsNothingWhenTheInstructorHasNotOptedIn(): void
|
|
{
|
|
$this->pref->shouldReceive('wants')->with(9)->andReturn(false);
|
|
// Not even a user lookup: the preference is the first gate.
|
|
Functions\expect('get_userdata')->never();
|
|
Functions\expect('wp_mail')->never();
|
|
|
|
self::assertFalse($this->mailer()->notifyLessonBooked(9, 'Ada', '30 min piano', 'Jul 1, 2026 10:00 AM'));
|
|
self::assertFalse($this->mailer()->notifyEnrollment(9, 'Ada', 'Choir'));
|
|
}
|
|
|
|
public function testSendsNothingWhenTheInstructorAccountIsGone(): void
|
|
{
|
|
$this->pref->shouldReceive('wants')->with(9)->andReturn(true);
|
|
Functions\when('get_userdata')->justReturn(false);
|
|
Functions\expect('wp_mail')->never();
|
|
|
|
self::assertFalse($this->mailer()->notifyEnrollment(9, 'Ada', 'Choir'));
|
|
}
|
|
|
|
public function testSendsNothingWhenTheInstructorHasNoEmail(): void
|
|
{
|
|
$this->pref->shouldReceive('wants')->with(9)->andReturn(true);
|
|
Functions\when('get_userdata')->justReturn($this->instructor(''));
|
|
Functions\expect('wp_mail')->never();
|
|
|
|
self::assertFalse($this->mailer()->notifyLessonBooked(9, 'Ada', '30 min piano', 'Jul 1, 2026 10:00 AM'));
|
|
}
|
|
}
|