feat: add updateProfile, updatePassword, deleteAccount API endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ovidiu U
2026-04-11 13:02:23 +01:00
parent 94d695d637
commit e90078d39e
3 changed files with 195 additions and 0 deletions

View File

@@ -1,6 +1,7 @@
<?php
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Laravel\Sanctum\Sanctum;
it('returns user preferences for authenticated user', function (): void {
@@ -65,3 +66,135 @@ it('rejects unauthenticated requests to user endpoints', function (): void {
$this->getJson('/api/user/preferences')->assertUnauthorized();
$this->getJson('/api/user/saved-stations')->assertUnauthorized();
});
// --- Profile update ---
it('updates user profile name and email', function (): void {
$user = User::factory()->create(['name' => 'Old Name', 'email' => 'old@example.com']);
Sanctum::actingAs($user);
$this->putJson('/api/user/profile', ['name' => 'New Name', 'email' => 'new@example.com'])
->assertOk()
->assertJsonFragment(['name' => 'New Name', 'email' => 'new@example.com']);
expect($user->fresh()->name)->toBe('New Name');
expect($user->fresh()->email)->toBe('new@example.com');
});
it('nulls email_verified_at when email changes', function (): void {
$user = User::factory()->create(['email_verified_at' => now()]);
Sanctum::actingAs($user);
$this->putJson('/api/user/profile', ['name' => $user->name, 'email' => 'changed@example.com'])
->assertOk();
expect($user->fresh()->email_verified_at)->toBeNull();
});
it('does not null email_verified_at when email is unchanged', function (): void {
$user = User::factory()->create(['email_verified_at' => now()]);
Sanctum::actingAs($user);
$this->putJson('/api/user/profile', ['name' => 'New Name', 'email' => $user->email])
->assertOk();
expect($user->fresh()->email_verified_at)->not->toBeNull();
});
it('rejects profile update with duplicate email', function (): void {
User::factory()->create(['email' => 'taken@example.com']);
$user = User::factory()->create();
Sanctum::actingAs($user);
$this->putJson('/api/user/profile', ['name' => 'Name', 'email' => 'taken@example.com'])
->assertUnprocessable()
->assertJsonValidationErrors(['email']);
});
it('rejects profile update with missing name', function (): void {
$user = User::factory()->create();
Sanctum::actingAs($user);
$this->putJson('/api/user/profile', ['email' => 'new@example.com'])
->assertUnprocessable()
->assertJsonValidationErrors(['name']);
});
// --- Password update ---
it('updates user password', function (): void {
$user = User::factory()->create(['password' => Hash::make('old-password')]);
Sanctum::actingAs($user);
$this->putJson('/api/user/password', [
'current_password' => 'old-password',
'password' => 'new-password',
'password_confirmation' => 'new-password',
])->assertOk();
expect(Hash::check('new-password', $user->fresh()->password))->toBeTrue();
});
it('rejects wrong current password', function (): void {
$user = User::factory()->create(['password' => Hash::make('correct-password')]);
Sanctum::actingAs($user);
$this->putJson('/api/user/password', [
'current_password' => 'wrong-password',
'password' => 'new-password',
'password_confirmation' => 'new-password',
])->assertUnprocessable()
->assertJsonValidationErrors(['current_password']);
});
it('rejects mismatched password confirmation', function (): void {
$user = User::factory()->create(['password' => Hash::make('correct-password')]);
Sanctum::actingAs($user);
$this->putJson('/api/user/password', [
'current_password' => 'correct-password',
'password' => 'new-password',
'password_confirmation' => 'different',
])->assertUnprocessable()
->assertJsonValidationErrors(['password']);
});
// --- Delete account ---
it('deletes user account with correct password', function (): void {
$user = User::factory()->create(['password' => Hash::make('my-password')]);
Sanctum::actingAs($user);
$this->deleteJson('/api/user', ['password' => 'my-password'])
->assertNoContent();
expect(User::find($user->id))->toBeNull();
});
it('revokes sanctum tokens on account deletion', function (): void {
$user = User::factory()->create(['password' => Hash::make('my-password')]);
$user->createToken('test');
Sanctum::actingAs($user);
$this->deleteJson('/api/user', ['password' => 'my-password'])
->assertNoContent();
expect($user->tokens()->count())->toBe(0);
});
it('rejects account deletion with wrong password', function (): void {
$user = User::factory()->create(['password' => Hash::make('correct-password')]);
Sanctum::actingAs($user);
$this->deleteJson('/api/user', ['password' => 'wrong-password'])
->assertUnprocessable()
->assertJsonValidationErrors(['password']);
expect(User::find($user->id))->not->toBeNull();
});
it('rejects unauthenticated requests to profile, password, and delete endpoints', function (): void {
$this->putJson('/api/user/profile', [])->assertUnauthorized();
$this->putJson('/api/user/password', [])->assertUnauthorized();
$this->deleteJson('/api/user', [])->assertUnauthorized();
});