Added tests

This commit is contained in:
Constantin Graf
2026-06-08 22:56:54 +02:00
committed by Constantin Graf
parent c5c129b239
commit 24775d2c47
4 changed files with 386 additions and 66 deletions

View File

@@ -5,12 +5,9 @@ declare(strict_types=1);
namespace Tests\Feature;
use App\Enums\Weekday;
use App\Mail\VerifyUpdatedEmailMail;
use App\Models\User;
use App\Service\TimezoneService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\URL;
use Tests\TestCase;
class ProfileInformationTest extends TestCase
@@ -39,66 +36,4 @@ class ProfileInformationTest extends TestCase
$user = $user->fresh();
$this->assertEquals($user->name, $user->name);
}
public function test_pending_email_verification_redirects_with_danger_banner_when_email_already_in_use(): void
{
// Arrange
User::factory()->create([
'email' => 'taken@example.com',
'is_placeholder' => false,
]);
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'taken@example.com',
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'taken@example.com',
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertRedirect(route('dashboard'));
$response->assertSessionHas('bannerStyle', 'danger');
$response->assertSessionHas('bannerText', 'The email address is already in use.');
$user = $user->fresh();
$this->assertEquals('current@example.com', $user->email);
$this->assertEquals('taken@example.com', $user->pending_email);
}
public function test_stale_pending_email_verification_link_is_rejected(): void
{
// Arrange
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'newer@example.com',
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'older@example.com',
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertForbidden();
$user = $user->fresh();
$this->assertEquals('current@example.com', $user->email);
$this->assertEquals('newer@example.com', $user->pending_email);
}
}

View File

@@ -0,0 +1,253 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Endpoint\Web;
use App\Http\Controllers\Web\UserController;
use App\Models\User;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use PHPUnit\Framework\Attributes\CoversClass;
#[CoversClass(UserController::class)]
class UserEndpointTest extends EndpointTestAbstract
{
public function test_pending_email_verification_updates_email_and_redirects_with_success_banner(): void
{
// Arrange
$this->travelTo(Carbon::parse('2024-01-02 12:00:00', 'UTC'));
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'new@example.com',
'email_verified_at' => null,
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'NEW@EXAMPLE.COM',
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertRedirect(route('dashboard'));
$response->assertSessionHas('bannerStyle', 'success');
$response->assertSessionHas('bannerText', 'Your email address has been updated successfully.');
$user->refresh();
$this->assertSame('new@example.com', $user->email);
$this->assertNull($user->pending_email);
$this->assertTrue(now()->equalTo($user->email_verified_at));
}
public function test_pending_email_verification_is_rejected_for_another_authenticated_user(): void
{
// Arrange
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'new@example.com',
]);
$this->actingAs(User::factory()->create());
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'new@example.com',
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertForbidden();
$user->refresh();
$this->assertSame('current@example.com', $user->email);
$this->assertSame('new@example.com', $user->pending_email);
}
public function test_pending_email_verification_without_email_is_rejected(): void
{
// Arrange
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'new@example.com',
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
['user' => $user->getKey()],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertForbidden();
$user->refresh();
$this->assertSame('current@example.com', $user->email);
$this->assertSame('new@example.com', $user->pending_email);
}
public function test_pending_email_verification_with_non_string_email_is_rejected(): void
{
// Arrange
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'new@example.com',
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => ['new@example.com'],
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertForbidden();
$user->refresh();
$this->assertSame('current@example.com', $user->email);
$this->assertSame('new@example.com', $user->pending_email);
}
public function test_stale_pending_email_verification_link_is_rejected(): void
{
// Arrange
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'newer@example.com',
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'older@example.com',
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertForbidden();
$user->refresh();
$this->assertSame('current@example.com', $user->email);
$this->assertSame('newer@example.com', $user->pending_email);
}
public function test_pending_email_verification_redirects_with_danger_banner_when_email_already_in_use(): void
{
// Arrange
User::factory()->create([
'email' => 'taken@example.com',
'is_placeholder' => false,
]);
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'taken@example.com',
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'taken@example.com',
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertRedirect(route('dashboard'));
$response->assertSessionHas('bannerStyle', 'danger');
$response->assertSessionHas('bannerText', 'The email address is already in use.');
$user->refresh();
$this->assertSame('current@example.com', $user->email);
$this->assertSame('taken@example.com', $user->pending_email);
}
public function test_pending_email_verification_ignores_placeholder_users_with_the_same_email(): void
{
// Arrange
User::factory()->placeholder()->create([
'email' => 'new@example.com',
]);
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'new@example.com',
'email_verified_at' => null,
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'new@example.com',
],
false
);
// Act
$response = $this->get($verificationUrl);
// Assert
$response->assertRedirect(route('dashboard'));
$response->assertSessionHas('bannerStyle', 'success');
$user->refresh();
$this->assertSame('new@example.com', $user->email);
$this->assertNull($user->pending_email);
$this->assertNotNull($user->email_verified_at);
}
public function test_pending_email_verification_with_invalid_signature_is_rejected(): void
{
// Arrange
$user = User::factory()->create([
'email' => 'current@example.com',
'pending_email' => 'new@example.com',
]);
$this->actingAs($user);
$verificationUrl = URL::temporarySignedRoute(
'users.verify-email-change',
now()->addMinutes(60),
[
'user' => $user->getKey(),
'email' => 'new@example.com',
],
false
);
// Act
$response = $this->get($verificationUrl.'&invalid');
// Assert
$response->assertForbidden();
$user->refresh();
$this->assertSame('current@example.com', $user->email);
$this->assertSame('new@example.com', $user->pending_email);
}
}

View File

@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Service\Dto;
use App\Service\Dto\UserAgentDto;
use PHPUnit\Framework\Attributes\CoversClass;
use Tests\TestCase;
#[CoversClass(UserAgentDto::class)]
class UserAgentDtoTest extends TestCase
{
public function test_chrome_on_windows_is_detected_as_a_desktop_browser(): void
{
// Arrange
$userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
$agent = new UserAgentDto;
$agent->setUserAgent($userAgent);
// Act
$platform = $agent->platform();
$browser = $agent->browser();
$isDesktop = $agent->isDesktop();
// Assert
$this->assertSame('Windows', $platform);
$this->assertSame('Chrome', $browser);
$this->assertTrue($isDesktop);
}
public function test_edge_is_detected_before_chrome(): void
{
// Arrange
$userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0';
$agent = new UserAgentDto;
$agent->setUserAgent($userAgent);
// Act
$browser = $agent->browser();
// Assert
$this->assertSame('Edge', $browser);
}
public function test_iphone_safari_is_detected_as_a_non_desktop_browser(): void
{
// Arrange
$userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
$agent = new UserAgentDto;
$agent->setUserAgent($userAgent);
// Act
$platform = $agent->platform();
$browser = $agent->browser();
$isDesktop = $agent->isDesktop();
// Assert
$this->assertSame('iOS', $platform);
$this->assertSame('Safari', $browser);
$this->assertFalse($isDesktop);
}
public function test_ipad_is_detected_as_non_desktop(): void
{
// Arrange
$userAgent = 'Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
$agent = new UserAgentDto;
$agent->setUserAgent($userAgent);
// Act
$isDesktop = $agent->isDesktop();
// Assert
$this->assertFalse($isDesktop);
}
public function test_unknown_user_agent_has_no_platform_or_browser_and_is_a_desktop(): void
{
// Arrange
$agent = new UserAgentDto;
$agent->setUserAgent('CustomClient/1.0');
// Act
$platform = $agent->platform();
$browser = $agent->browser();
$isDesktop = $agent->isDesktop();
// Assert
$this->assertNull($platform);
$this->assertNull($browser);
$this->assertTrue($isDesktop);
}
public function test_cloudfront_desktop_header_is_detected_as_desktop(): void
{
// Arrange
$agent = new UserAgentDto;
$agent->setUserAgent('Amazon CloudFront');
$agent->setHttpHeaders([
'HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER' => 'true',
]);
// Act
$isDesktop = $agent->isDesktop();
// Assert
$this->assertTrue($isDesktop);
}
public function test_cached_values_are_resolved_for_the_current_user_agent(): void
{
// Arrange
$agent = new UserAgentDto;
$agent->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36');
$agent->platform();
$agent->browser();
$agent->isDesktop();
$agent->setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Version/17.0 Mobile/15E148 Safari/604.1');
// Act
$platform = $agent->platform();
$browser = $agent->browser();
$isDesktop = $agent->isDesktop();
// Assert
$this->assertSame('iOS', $platform);
$this->assertSame('Safari', $browser);
$this->assertFalse($isDesktop);
}
}