set('services.turnstile.site_key', '0x4AAAAAAAsitekey'); config()->set('services.turnstile.secret_key', '0x4AAAAAAAsecretkeyforthetests'); config()->set('services.turnstile.fail_open', true); } function registrationForm(string $name = 'Paula Presse'): Testable { return Livewire::test('auth.register') ->set('name', $name) ->set('email', 'paula@example.com') ->set('password', 'sicheres-passwort-2026') ->set('password_confirmation', 'sicheres-passwort-2026') ->set('terms_accepted', true); } test('registration with a confirmed turnstile token creates the account', function () { /** @var TestCase $this */ Notification::fake(); enableRegistrationTurnstile(); Http::fake(['challenges.cloudflare.com/*' => Http::response(['success' => true, 'action' => 'register'])]); registrationForm() ->set('turnstileToken', 'token-aus-dem-widget') ->call('register') ->assertHasNoErrors() ->assertRedirect(route('verification.notice', absolute: false)); expect(User::query()->where('email', 'paula@example.com')->exists())->toBeTrue() ->and(ActivityEvent::query()->where('type', ActivityType::BotCheckUnavailable->value)->count())->toBe(0); }); test('registration without a confirmed token creates no account and sends no mail', function (string $token, array $siteverify) { /** @var TestCase $this */ Notification::fake(); enableRegistrationTurnstile(); Http::fake(['challenges.cloudflare.com/*' => Http::response($siteverify)]); registrationForm() ->set('turnstileToken', $token) ->call('register') ->assertHasErrors('turnstile') ->assertDispatched('turnstile-reset') ->assertSet('turnstileToken', ''); expect(User::query()->where('email', 'paula@example.com')->exists())->toBeFalse(); Notification::assertNothingSent(); })->with([ 'no token' => ['', ['success' => true, 'action' => 'register']], 'rejected token' => ['token', ['success' => false, 'error-codes' => ['invalid-input-response']]], 'token for the contact form' => ['token', ['success' => true, 'action' => 'contact']], ]); test('if cloudflare is unreachable the account is created and marked for review', function () { /** @var TestCase $this */ Notification::fake(); enableRegistrationTurnstile(); Http::fake(['challenges.cloudflare.com/*' => Http::failedConnection()]); registrationForm() ->set('turnstileToken', 'token-aus-dem-widget') ->call('register') ->assertHasNoErrors(); $user = User::query()->where('email', 'paula@example.com')->sole(); $event = ActivityEvent::query()->where('type', ActivityType::BotCheckUnavailable->value)->sole(); expect($event->user_id)->toBe($user->id) ->and($event->meta)->toMatchArray(['form' => 'register', 'reason' => 'siteverify_unreachable']); }); test('if the widget could not load in the browser the account is created and marked', function () { /** @var TestCase $this */ Notification::fake(); enableRegistrationTurnstile(); Http::fake(); registrationForm() ->set('turnstileUnavailable', true) ->call('register') ->assertHasNoErrors(); Http::assertNothingSent(); expect(ActivityEvent::query()->where('type', ActivityType::BotCheckUnavailable->value)->sole()->meta['reason']) ->toBe('widget_not_loaded'); }); test('random bot names are rejected', function (string $name) { /** @var TestCase $this */ Notification::fake(); registrationForm($name)->call('register')->assertHasErrors('name'); expect(User::query()->count())->toBe(0); })->with(['ruLrlotwaCoVStmZetSxD', 'VJxlVxkqQzgMfJPaM', 'GeCWsvfRZNHeNXubB']); test('real names pass the name check', function (string $name) { /** @var TestCase $this */ Notification::fake(); registrationForm($name)->call('register')->assertHasNoErrors(); })->with(['Paula Presse', 'Anna-Lena Müller-Lüdenscheidt', 'Dr. Jabena B. McNair', 'MacGregor', 'Sky']); test('the registration page renders the widget only with keys', function () { /** @var TestCase $this */ // Ohne Keys kein Widget (der Livewire-Snapshot nennt die Properties trotzdem). $this->get('/register')->assertSuccessful()->assertDontSee('pkTurnstileReady', false); enableRegistrationTurnstile(); $this->get('/register') ->assertSuccessful() ->assertSee('0x4AAAAAAAsitekey', false) ->assertSee('pkTurnstileReady', false); });