get(route('admin.login'))->assertOk() ->assertDontSee(route('register')) ->assertDontSee(__('auth.sign_up')); }); it('signs an operator in on the operator guard', function () { $operator = Operator::factory()->role('Owner')->create(['password' => 'richtig-langes-pw']); Livewire::test(OperatorLogin::class) ->set('email', $operator->email) ->set('password', 'richtig-langes-pw') ->call('authenticate') ->assertRedirect(AdminArea::home()); expect(Auth::guard('operator')->check())->toBeTrue() ->and(Auth::guard('web')->check())->toBeFalse(); }); it('refuses a portal account at the console door', function () { $user = User::factory()->create(['email' => 'kunde@clupilot.test', 'password' => 'kunden-passwort']); Livewire::test(OperatorLogin::class) ->set('email', 'kunde@clupilot.test') ->set('password', 'kunden-passwort') ->call('authenticate') ->assertHasErrors('email'); expect(Auth::guard('operator')->check())->toBeFalse(); }); it('refuses a disabled operator', function () { $operator = Operator::factory()->role('Owner')->create([ 'password' => 'richtig-langes-pw', 'disabled_at' => now(), ]); Livewire::test(OperatorLogin::class) ->set('email', $operator->email) ->set('password', 'richtig-langes-pw') ->call('authenticate') ->assertHasErrors('email'); expect(Auth::guard('operator')->check())->toBeFalse(); }); it('sends an operator with confirmed two-factor to the challenge instead of the console', function () { $operator = Operator::factory()->role('Owner')->create([ 'password' => 'richtig-langes-pw', 'two_factor_secret' => encrypt('JBSWY3DPEHPK3PXP'), 'two_factor_confirmed_at' => now(), ]); Livewire::test(OperatorLogin::class) ->set('email', $operator->email) ->set('password', 'richtig-langes-pw') ->call('authenticate') ->assertRedirect(route('admin.two-factor')); // Not signed in yet — the challenge is not decoration. expect(Auth::guard('operator')->check())->toBeFalse(); }); it('throttles repeated failures', function () { $operator = Operator::factory()->role('Owner')->create(['password' => 'richtig-langes-pw']); $component = Livewire::test(OperatorLogin::class)->set('email', $operator->email)->set('password', 'falsch'); foreach (range(1, 5) as $ignored) { $component->call('authenticate'); } $component->call('authenticate')->assertHasErrors('email'); expect(session()->has('login.id'))->toBeFalse(); }); it('refuses to complete a login for an operator who no longer qualifies', function () { // completeLogin() is the one place both the direct exit and the // two-factor challenge complete a sign-in — re-checked here so a // revocation in the window between the password step and a pending // two-factor code cannot slip through the challenge, which only ever // confirmed the row still existed. $disabled = Operator::factory()->role('Owner')->create(['disabled_at' => now()]); $roleless = Operator::factory()->create(); // no role synced at all expect(OperatorLogin::completeLogin($disabled, false))->toBeFalse() ->and(Auth::guard('operator')->check())->toBeFalse(); expect(OperatorLogin::completeLogin($roleless, false))->toBeFalse() ->and(Auth::guard('operator')->check())->toBeFalse(); }); it('records when the operator last signed in', function () { $operator = Operator::factory()->role('Owner')->create(['password' => 'richtig-langes-pw']); Livewire::test(OperatorLogin::class) ->set('email', $operator->email)->set('password', 'richtig-langes-pw') ->call('authenticate'); expect($operator->fresh()->last_login_at)->not->toBeNull(); });