alias(static fn ($v) => $v); Functions\when('sanitize_key')->alias(static fn ($v) => $v); } protected function tearDown(): void { unset($_REQUEST['action']); $_GET = []; parent::tearDown(); } /** * Stub everything maybeConfirm() needs for a valid token belonging to user * 9, with wp_safe_redirect throwing so the redirect URL can be asserted. * * @param bool $autoApprove Whether user 9 carries the auto-approve marker. */ private function stubConfirmContext(bool $autoApprove): void { $this->stubMode(StudioSettings::MODE_INVITE); Functions\when('is_admin')->justReturn(false); Functions\when('sanitize_text_field')->returnArg(); Functions\when('get_permalink')->justReturn('http://wp/register/'); Functions\when('add_query_arg')->alias(static fn (string $k, string $v, string $u): string => $u . '?' . $k . '=' . $v); Functions\when('get_users')->justReturn([9]); Functions\when('get_user_meta')->alias(static function (int $id, string $key) use ($autoApprove) { if ($key === RegistrationStatus::META_CONFIRM_EXPIRES) { return '2030-01-01 00:00:00'; } if ($key === RegistrationStatus::META_AUTO_APPROVE) { return $autoApprove ? '1' : ''; } return ''; }); Functions\when('update_user_meta')->justReturn(true); Functions\when('delete_user_meta')->justReturn(true); Functions\when('wp_safe_redirect')->alias(static function (string $url): void { throw new \RuntimeException('redirect:' . $url); }); $_GET['us_confirm'] = 'rawtoken'; } public function testConfirmAutoApprovesGroupLinkSignupWithoutAdminReview(): void { $this->stubConfirmContext(true); $user = Mockery::mock(\WP_User::class); Functions\when('get_user_by')->justReturn($user); $mailer = Mockery::mock(RegistrationMailer::class); $mailer->shouldReceive('sendApproved')->once()->with($user)->andReturn(true); $mailer->shouldNotReceive('notifyAdminsPending'); $handler = new EmailConfirmationHandler(new StudioSettings(), $mailer); try { $handler->maybeConfirm(); self::fail('Expected a redirect'); } catch (\RuntimeException $e) { self::assertStringContainsString('us_confirmed=ready', $e->getMessage()); } } public function testConfirmWithoutAutoApproveNotifiesAdminsAndStaysPending(): void { $this->stubConfirmContext(false); $user = Mockery::mock(\WP_User::class); Functions\when('get_user_by')->justReturn($user); $mailer = Mockery::mock(RegistrationMailer::class); $mailer->shouldReceive('notifyAdminsPending')->once()->with($user)->andReturn(true); $mailer->shouldNotReceive('sendApproved'); $handler = new EmailConfirmationHandler(new StudioSettings(), $mailer); try { $handler->maybeConfirm(); self::fail('Expected a redirect'); } catch (\RuntimeException $e) { self::assertStringContainsString('us_confirmed=1', $e->getMessage()); } } private function handler(): EmailConfirmationHandler { return new EmailConfirmationHandler(new StudioSettings(), new RegistrationMailer()); } private function stubMode(string $mode): void { Functions\when('get_option')->alias(static function (string $name, $default = false) use ($mode) { if ($name === StudioSettings::OPT_REGISTRATION_MODE) { return $mode; } if ($name === RegistrationController::OPTION_PAGE) { return 5; } return $default; }); } public function testRegisterUrlPassesThroughWhenClosed(): void { $this->stubMode(StudioSettings::MODE_INVITE); self::assertSame('http://wp/register', $this->handler()->registerUrl('http://wp/register')); } public function testRegisterUrlPointsAtRegistrationPageWhenOpen(): void { $this->stubMode(StudioSettings::MODE_SELF_APPROVAL); Functions\when('get_permalink')->justReturn('http://studio.test/register'); self::assertSame('http://studio.test/register', $this->handler()->registerUrl('http://wp/register')); } public function testBlockNativeRegistrationIsNoOpWhenClosed(): void { $this->stubMode(StudioSettings::MODE_INVITE); $_REQUEST['action'] = 'register'; // Must not redirect/exit when open registration is off. Functions\expect('wp_safe_redirect')->never(); $this->handler()->blockNativeRegistration(); unset($_REQUEST['action']); self::assertTrue(true); } public function testBlockNativeRegistrationIgnoresOtherActions(): void { $this->stubMode(StudioSettings::MODE_SELF_APPROVAL); $_REQUEST['action'] = 'lostpassword'; Functions\expect('wp_safe_redirect')->never(); $this->handler()->blockNativeRegistration(); unset($_REQUEST['action']); self::assertTrue(true); } public function testBlockRegistrationErrorsRejectsWhenOpen(): void { $this->stubMode(StudioSettings::MODE_SELF_APPROVAL); $errors = $this->handler()->blockRegistrationErrors(new \WP_Error()); self::assertSame('us_registration_redirect', $errors->get_error_code()); } public function testBlockRegistrationErrorsPassesThroughWhenClosed(): void { $this->stubMode(StudioSettings::MODE_INVITE); $errors = $this->handler()->blockRegistrationErrors(new \WP_Error()); self::assertSame('', $errors->get_error_code()); } }