Add open student registration with email confirmation and approval
CI / Tests (PHP 8.1) (pull_request) Successful in 1m18s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m18s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 3m20s
CI / Coding Standards (pull_request) Successful in 3m25s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m33s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 1m18s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m18s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 3m20s
CI / Coding Standards (pull_request) Successful in 3m25s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m33s
CI / Build Plugin Zip (pull_request) Skipped
Students could previously join by invite only. Add an optional self-approval mode, toggled from Studio Settings → Registration: anyone may sign up on the existing [us_student_register] page, confirm their email via a tokenised link, and then be approved by a studio admin before the account is usable. - Enabling the toggle mirrors WordPress's own membership settings (users_can_register + default_role = us_student) and snapshots their previous values so disabling restores them. - WordPress's native registration form is blocked while open registration is on (login_init redirect + registration_errors fail-safe + register_url) so it cannot bypass signup policy acceptance. - Pending accounts: unconfirmed email cannot log in; confirmed but unapproved can log in but the booking capability is withheld and the booking page shows an "awaiting approval" screen. - Approve/reject from Students → Pending Students; reject hard-deletes the account so the email is freed to re-apply. - Invite registration is unchanged; both modes coexist. Account lifecycle lives in user meta (RegistrationStatus); no new tables. Closes #63 Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Unsupervised\Schedular\Tests\Unit\Auth;
|
||||
|
||||
use Brain\Monkey\Functions;
|
||||
use Mockery;
|
||||
use Unsupervised\Schedular\Auth\RegistrationLoginGate;
|
||||
use Unsupervised\Schedular\Auth\RegistrationStatus;
|
||||
use Unsupervised\Schedular\Auth\RoleManager;
|
||||
use Unsupervised\Schedular\Tests\Unit\TestCase;
|
||||
|
||||
class RegistrationLoginGateTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Stub get_user_meta to describe a single account's pending state.
|
||||
*/
|
||||
private function stubMeta(string $awaiting, string $confirmed): void
|
||||
{
|
||||
Functions\when('get_user_meta')->alias(static function (int $id, string $key) use ($awaiting, $confirmed) {
|
||||
if ($key === RegistrationStatus::META_AWAITING_APPROVAL) {
|
||||
return $awaiting;
|
||||
}
|
||||
if ($key === RegistrationStatus::META_EMAIL_CONFIRMED) {
|
||||
return $confirmed;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
private function user(int $id): \WP_User
|
||||
{
|
||||
$user = Mockery::mock(\WP_User::class);
|
||||
$user->ID = $id;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function testBlocksLoginWhileEmailUnconfirmed(): void
|
||||
{
|
||||
$this->stubMeta('1', '');
|
||||
|
||||
$result = (new RegistrationLoginGate())->blockUnconfirmed($this->user(7));
|
||||
|
||||
self::assertInstanceOf(\WP_Error::class, $result);
|
||||
self::assertSame('us_email_unconfirmed', $result->get_error_code());
|
||||
}
|
||||
|
||||
public function testAllowsLoginWhenConfirmedButAwaitingApproval(): void
|
||||
{
|
||||
$this->stubMeta('1', '1');
|
||||
|
||||
$user = $this->user(7);
|
||||
|
||||
self::assertSame($user, (new RegistrationLoginGate())->blockUnconfirmed($user));
|
||||
}
|
||||
|
||||
public function testAllowsLoginForApprovedStudent(): void
|
||||
{
|
||||
$this->stubMeta('', '1');
|
||||
|
||||
$user = $this->user(7);
|
||||
|
||||
self::assertSame($user, (new RegistrationLoginGate())->blockUnconfirmed($user));
|
||||
}
|
||||
|
||||
public function testIgnoresUsersWithNoPendingMeta(): void
|
||||
{
|
||||
// Invite/admin-created students carry no meta at all.
|
||||
$this->stubMeta('', '');
|
||||
|
||||
$user = $this->user(7);
|
||||
|
||||
self::assertSame($user, (new RegistrationLoginGate())->blockUnconfirmed($user));
|
||||
}
|
||||
|
||||
public function testPassesThroughAnEarlierError(): void
|
||||
{
|
||||
$error = new \WP_Error('some_earlier_error', 'nope');
|
||||
|
||||
self::assertSame($error, (new RegistrationLoginGate())->blockUnconfirmed($error));
|
||||
}
|
||||
|
||||
public function testWithholdsBookingCapWhileAwaitingApproval(): void
|
||||
{
|
||||
$this->stubMeta('1', '1');
|
||||
|
||||
$allcaps = [ RoleManager::CAP_BOOK_LESSON => true, 'read' => true ];
|
||||
|
||||
$result = (new RegistrationLoginGate())
|
||||
->withholdBookingWhilePending($allcaps, [], [], $this->user(7));
|
||||
|
||||
self::assertArrayNotHasKey(RoleManager::CAP_BOOK_LESSON, $result);
|
||||
self::assertTrue($result['read']);
|
||||
}
|
||||
|
||||
public function testKeepsBookingCapForApprovedStudent(): void
|
||||
{
|
||||
$this->stubMeta('', '1');
|
||||
|
||||
$allcaps = [ RoleManager::CAP_BOOK_LESSON => true ];
|
||||
|
||||
$result = (new RegistrationLoginGate())
|
||||
->withholdBookingWhilePending($allcaps, [], [], $this->user(7));
|
||||
|
||||
self::assertTrue($result[RoleManager::CAP_BOOK_LESSON]);
|
||||
}
|
||||
|
||||
public function testLeavesCapsUntouchedForNonUserArg(): void
|
||||
{
|
||||
$allcaps = [ RoleManager::CAP_BOOK_LESSON => true ];
|
||||
|
||||
$result = (new RegistrationLoginGate())
|
||||
->withholdBookingWhilePending($allcaps, [], [], null);
|
||||
|
||||
self::assertSame($allcaps, $result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user