complete story 1.3 with qa test & added future recommendations to the dev

This commit is contained in:
Naser Mansour
2025-12-26 14:04:24 +02:00
parent 84d9c2f66a
commit ebb6841ed0
29 changed files with 760 additions and 101 deletions
+92 -11
View File
@@ -9,19 +9,34 @@ test('login screen can be rendered', function () {
$response->assertStatus(200);
});
test('users can authenticate using the login screen', function () {
$user = User::factory()->create();
test('admin user redirects to admin dashboard after login', function () {
$admin = User::factory()->admin()->create();
$response = $this->post(route('login.store'), [
'email' => $user->email,
'email' => $admin->email,
'password' => 'password',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('dashboard', absolute: false));
->assertRedirect('/admin/dashboard');
$this->assertAuthenticated();
$this->assertAuthenticatedAs($admin);
});
test('client user redirects to client dashboard after login', function () {
$client = User::factory()->individual()->create();
$response = $this->post(route('login.store'), [
'email' => $client->email,
'password' => 'password',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect('/client/dashboard');
$this->assertAuthenticatedAs($client);
});
test('users can not authenticate with invalid password', function () {
@@ -32,21 +47,54 @@ test('users can not authenticate with invalid password', function () {
'password' => 'wrong-password',
]);
$response->assertSessionHasErrorsIn('email');
$this->assertGuest();
});
test('nonexistent user shows error', function () {
$response = $this->post(route('login.store'), [
'email' => 'nonexistent@example.com',
'password' => 'password',
]);
$this->assertGuest();
});
test('deactivated user cannot login', function () {
$user = User::factory()->deactivated()->create();
$response = $this->post(route('login.store'), [
'email' => $user->email,
'password' => 'password',
]);
$this->assertGuest();
});
test('rate limiting blocks after five attempts', function () {
$user = User::factory()->create();
// Make 5 failed attempts
for ($i = 0; $i < 5; $i++) {
$this->post(route('login.store'), [
'email' => $user->email,
'password' => 'wrong-password',
]);
}
// 6th attempt should be throttled
$response = $this->post(route('login.store'), [
'email' => $user->email,
'password' => 'wrong-password',
]);
$response->assertStatus(429);
});
test('users with two factor enabled are redirected to two factor challenge', function () {
if (! Features::canManageTwoFactorAuthentication()) {
$this->markTestSkipped('Two-factor authentication is not enabled.');
}
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
$user = User::factory()->withTwoFactor()->create();
$response = $this->post(route('login.store'), [
@@ -67,3 +115,36 @@ test('users can logout', function () {
$this->assertGuest();
});
test('logout clears session', function () {
$user = User::factory()->create();
$this->actingAs($user);
$this->assertAuthenticated();
$this->post(route('logout'));
$this->assertGuest();
});
test('authenticated user cannot access login page', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route('login'));
$response->assertRedirect();
});
test('failed login attempts are logged', function () {
$user = User::factory()->create();
$this->post(route('login.store'), [
'email' => $user->email,
'password' => 'wrong-password',
]);
$this->assertDatabaseHas('admin_logs', [
'action' => 'failed_login',
'target_type' => 'user',
]);
});
+65
View File
@@ -0,0 +1,65 @@
<?php
use App\Models\User;
test('admin can access admin routes', function () {
$admin = User::factory()->admin()->create();
$response = $this->actingAs($admin)->get('/admin/dashboard');
$response->assertStatus(200);
});
test('client cannot access admin routes', function () {
$client = User::factory()->individual()->create();
$response = $this->actingAs($client)->get('/admin/dashboard');
$response->assertForbidden();
});
test('company client cannot access admin routes', function () {
$client = User::factory()->company()->create();
$response = $this->actingAs($client)->get('/admin/dashboard');
$response->assertForbidden();
});
test('unauthenticated user redirected to login', function () {
$response = $this->get('/admin/dashboard');
$response->assertRedirect(route('login'));
});
test('unauthenticated user redirected to login for client routes', function () {
$response = $this->get('/client/dashboard');
$response->assertRedirect(route('login'));
});
test('client can access client routes', function () {
$client = User::factory()->individual()->create();
$response = $this->actingAs($client)->get('/client/dashboard');
$response->assertStatus(200);
});
test('admin can access client routes', function () {
$admin = User::factory()->admin()->create();
$response = $this->actingAs($admin)->get('/client/dashboard');
$response->assertStatus(200);
});
test('deactivated user logged out on request', function () {
$user = User::factory()->deactivated()->create();
// Simulate an authenticated session with deactivated user
$response = $this->actingAs($user)->get('/client/dashboard');
$response->assertRedirect(route('login'));
$this->assertGuest();
});
+5 -3
View File
@@ -29,7 +29,8 @@ test('email can be verified', function () {
Event::assertDispatched(Verified::class);
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
// Redirects to client dashboard since factory creates individual users
$response->assertRedirect('/client/dashboard?verified=1');
});
test('email is not verified with invalid hash', function () {
@@ -60,8 +61,9 @@ test('already verified user visiting verification link is redirected without fir
);
$this->actingAs($user)->get($verificationUrl)
->assertRedirect(route('dashboard', absolute: false).'?verified=1');
// Redirects to client dashboard since factory creates individual users
->assertRedirect('/client/dashboard?verified=1');
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
Event::assertNotDispatched(Verified::class);
});
});
+7 -15
View File
@@ -1,22 +1,14 @@
<?php
test('registration screen can be rendered', function () {
$response = $this->get(route('register'));
use Laravel\Fortify\Features;
$response->assertStatus(200);
test('registration is disabled', function () {
// Registration is disabled per AC 7 - admin creates all accounts
expect(Features::enabled(Features::registration()))->toBeFalse();
});
test('new users can register', function () {
$response = $this->post(route('register.store'), [
'full_name' => 'John Doe',
'email' => 'test@example.com',
'phone' => '+1234567890',
'password' => 'password',
'password_confirmation' => 'password',
]);
test('registration route returns 404', function () {
$response = $this->get('/register');
$response->assertSessionHasNoErrors()
->assertRedirect(route('dashboard', absolute: false));
$this->assertAuthenticated();
$response->assertNotFound();
});
+19 -6
View File
@@ -2,15 +2,28 @@
use App\Models\User;
test('guests are redirected to the login page', function () {
$response = $this->get(route('dashboard'));
test('guests are redirected to the login page for admin dashboard', function () {
$response = $this->get('/admin/dashboard');
$response->assertRedirect(route('login'));
});
test('authenticated users can visit the dashboard', function () {
$user = User::factory()->create();
test('guests are redirected to the login page for client dashboard', function () {
$response = $this->get('/client/dashboard');
$response->assertRedirect(route('login'));
});
test('authenticated admin can visit admin dashboard', function () {
$user = User::factory()->admin()->create();
$this->actingAs($user);
$response = $this->get(route('dashboard'));
$response = $this->get('/admin/dashboard');
$response->assertStatus(200);
});
});
test('authenticated client can visit client dashboard', function () {
$user = User::factory()->individual()->create();
$this->actingAs($user);
$response = $this->get('/client/dashboard');
$response->assertStatus(200);
});
+12
View File
@@ -66,3 +66,15 @@ test('user active scope returns only active users', function () {
expect(User::active()->count())->toBe(2);
});
test('user isActive returns true for active user', function () {
$user = User::factory()->create();
expect($user->isActive())->toBeTrue();
});
test('user isActive returns false for deactivated user', function () {
$user = User::factory()->deactivated()->create();
expect($user->isActive())->toBeFalse();
});