store/tests/Feature/Admin/Account/ProfileTest.php

317 lines
12 KiB
PHP

<?php
use App\Enums\Permission as PermissionEnum;
use App\Models\User;
use App\Models\UserProfile;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(RolePermissionSeeder::class);
$disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public');
Storage::fake($disk);
Storage::fake('public');
});
function createUserWithProfile(): User
{
$user = User::factory()->create();
$user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value);
$user->forgetCachedPermissions();
UserProfile::factory()->create([
'user_id' => $user->id,
'full_name' => fake()->name(),
'phone_number' => fake()->numerify('08##########'),
'gender' => fake()->randomElement(['male', 'female']),
]);
return $user;
}
function createUserWithoutProfile(): User
{
$user = User::factory()->create();
$user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value);
$user->forgetCachedPermissions();
return $user;
}
function validProfilePayload(): array
{
return [
'email' => 'updated@example.com',
'username' => 'updated_user',
'full_name' => 'Nama Updated',
'phone_number' => '081234567890',
'gender' => 'male',
'birth_date' => '1995-01-15',
'address' => 'Jl. Merdeka No. 1',
];
}
// ─── Profile Edit ──────────────────────────────────────────
describe('Profile Edit', function () {
test('authenticated user can view profile edit form', function () {
$user = createUserWithProfile();
$this->actingAs($user)
->get(route('admin.account.profile'))
->assertOk();
});
test('guest is redirected to login', function () {
$this->get(route('admin.account.profile'))
->assertRedirect(route('login'));
});
});
// ─── Profile Update ────────────────────────────────────────
describe('Profile Update', function () {
test('authenticated user can update profile', function () {
$user = createUserWithProfile();
$this->actingAs($user)
->put(route('admin.account.profile.update'), validProfilePayload())
->assertRedirect(route('admin.account.profile'));
$user->refresh();
expect($user->email)->toBe('updated@example.com');
expect($user->username)->toBe('updated_user');
expect($user->profile->full_name)->toBe('Nama Updated');
expect($user->profile->phone_number)->toBe('081234567890');
expect($user->profile->gender->value)->toBe('male');
expect($user->profile->birth_date->format('Y-m-d'))->toBe('1995-01-15');
expect($user->profile->address)->toBe('Jl. Merdeka No. 1');
});
test('guest cannot update profile', function () {
$this->put(route('admin.account.profile.update'), validProfilePayload())
->assertRedirect(route('login'));
});
test('profile is created when user has no profile', function () {
$user = createUserWithoutProfile();
$this->actingAs($user)
->put(route('admin.account.profile.update'), validProfilePayload())
->assertRedirect(route('admin.account.profile'));
$user->refresh();
expect($user->profile)->not->toBeNull();
expect($user->profile->full_name)->toBe('Nama Updated');
});
test('required fields are validated', function () {
$user = createUserWithProfile();
$this->actingAs($user)
->put(route('admin.account.profile.update'), [])
->assertSessionHasErrors(['email', 'username', 'full_name']);
});
test('email must be unique on update (ignoring self)', function () {
$user = createUserWithProfile();
$payload = validProfilePayload();
$payload['email'] = $user->email;
$payload['username'] = $user->username;
$this->actingAs($user)
->put(route('admin.account.profile.update'), $payload)
->assertSessionHasNoErrors('email');
});
test('email must be unique among other users', function () {
$user = createUserWithProfile();
$other = User::factory()->create(['email' => 'taken@example.com']);
$payload = validProfilePayload();
$payload['email'] = 'taken@example.com';
$this->actingAs($user)
->put(route('admin.account.profile.update'), $payload)
->assertSessionHasErrors('email');
});
test('username must be unique on update (ignoring self)', function () {
$user = createUserWithProfile();
$payload = validProfilePayload();
$payload['email'] = $user->email;
$payload['username'] = $user->username;
$this->actingAs($user)
->put(route('admin.account.profile.update'), $payload)
->assertSessionHasNoErrors('username');
});
test('nullable fields can be null', function () {
$user = createUserWithProfile();
$payload = validProfilePayload();
$payload['phone_number'] = null;
$payload['gender'] = null;
$payload['birth_date'] = null;
$payload['address'] = null;
$this->actingAs($user)
->put(route('admin.account.profile.update'), $payload)
->assertRedirect(route('admin.account.profile'));
$user->refresh();
expect($user->profile->phone_number)->toBeNull();
expect($user->profile->gender)->toBeNull();
expect($user->profile->birth_date)->toBeNull();
expect($user->profile->address)->toBeNull();
});
test('updating profile with s3 key links the photo', function () {
$user = createUserWithProfile();
$disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public');
$imageContent = UploadedFile::fake()->image('avatar.jpg')->get();
Storage::disk($disk)->put('fake-s3-key.jpg', $imageContent);
$payload = validProfilePayload();
$payload['profile_s3_key'] = 'fake-s3-key.jpg';
$this->actingAs($user)
->put(route('admin.account.profile.update'), $payload);
$user->refresh();
expect($user->profile->getMedia('profile_photo')->count())->toBe(1);
});
test('updating profile can upload new photo and delete existing photo', function () {
$user = createUserWithProfile();
$imageContent = UploadedFile::fake()->image('initial.jpg')->get();
Storage::disk('public')->put('initial.jpg', $imageContent);
$user->profile->addMediaFromDisk('initial.jpg', 'public')->toMediaCollection('profile_photo');
expect($user->profile->fresh()->getMedia('profile_photo')->count())->toBe(1);
$mediaId = $user->profile->fresh()->getFirstMedia('profile_photo')->id;
$disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public');
$imageContent2 = UploadedFile::fake()->image('updated.jpg')->get();
Storage::disk($disk)->put('updated.jpg', $imageContent2);
$payload = validProfilePayload();
$payload['profile_s3_key'] = 'updated.jpg';
$payload['remove_profile_photo_ids'] = [$mediaId];
$this->actingAs($user)
->put(route('admin.account.profile.update'), $payload);
$freshProfile = $user->fresh()->profile;
expect($freshProfile->getMedia('profile_photo')->count())->toBe(1);
expect($freshProfile->getFirstMedia('profile_photo')->id)->not->toBe($mediaId);
});
test('removing profile photo without uploading new one results in no photo', function () {
$user = createUserWithProfile();
$imageContent = UploadedFile::fake()->image('initial.jpg')->get();
Storage::disk('public')->put('initial.jpg', $imageContent);
$user->profile->addMediaFromDisk('initial.jpg', 'public')->toMediaCollection('profile_photo');
$mediaId = $user->profile->fresh()->getFirstMedia('profile_photo')->id;
$payload = validProfilePayload();
$payload['remove_profile_photo_ids'] = [$mediaId];
$this->actingAs($user)
->put(route('admin.account.profile.update'), $payload);
$user->refresh();
expect($user->profile->getMedia('profile_photo')->count())->toBe(0);
});
});
// ─── Password Edit ─────────────────────────────────────────
describe('Password Edit', function () {
test('authenticated user can view password edit form', function () {
$user = createUserWithProfile();
$this->actingAs($user)
->get(route('admin.account.password'))
->assertOk();
});
test('guest is redirected to login', function () {
$this->get(route('admin.account.password'))
->assertRedirect(route('login'));
});
});
// ─── Password Update ───────────────────────────────────────
describe('Password Update', function () {
test('authenticated user can update password', function () {
$user = User::factory()->create(['password' => Hash::make('old-password')]);
$user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value);
$user->forgetCachedPermissions();
$this->actingAs($user)
->put(route('admin.account.password.update'), [
'current_password' => 'old-password',
'password' => 'new-secure-password',
'password_confirmation' => 'new-secure-password',
])
->assertRedirect(route('admin.account.password'));
$this->assertTrue(Hash::check('new-secure-password', $user->fresh()->password));
});
test('guest cannot update password', function () {
$this->put(route('admin.account.password.update'), [
'current_password' => 'old-password',
'password' => 'new-secure-password',
'password_confirmation' => 'new-secure-password',
])->assertRedirect(route('login'));
});
test('current password must be correct', function () {
$user = User::factory()->create(['password' => Hash::make('old-password')]);
$user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value);
$user->forgetCachedPermissions();
$this->actingAs($user)
->put(route('admin.account.password.update'), [
'current_password' => 'wrong-password',
'password' => 'new-secure-password',
'password_confirmation' => 'new-secure-password',
])
->assertSessionHasErrors('current_password');
});
test('password confirmation must match', function () {
$user = User::factory()->create(['password' => Hash::make('old-password')]);
$user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value);
$user->forgetCachedPermissions();
$this->actingAs($user)
->put(route('admin.account.password.update'), [
'current_password' => 'old-password',
'password' => 'new-secure-password',
'password_confirmation' => 'different-password',
])
->assertSessionHasErrors('password');
});
test('required fields are validated', function () {
$user = createUserWithoutProfile();
$this->actingAs($user)
->put(route('admin.account.password.update'), [])
->assertSessionHasErrors(['current_password', 'password']);
});
});