fix(avatar): change avatar URL when an existing avatar is replaced

This commit is contained in:
Andras Bacsai
2026-10-29 19:06:11 +01:00
parent 69bfb24ab2
commit 28c5af9b18
4 changed files with 36 additions and 5 deletions
+2 -2
View File
@@ -28,11 +28,11 @@ class AvatarStorageService
$oldS3StorageId = $user->avatar_s3_storage_id;
$oldPath = $user->avatar_path;
$user->update([
$user->fill([
'avatar_path' => $path,
'avatar_storage_type' => $storageType,
'avatar_s3_storage_id' => $s3StorageId,
]);
])->touch();
if ($oldPath && ($oldStorageType !== $storageType || $oldS3StorageId !== $s3StorageId)) {
$this->disk($oldStorageType ?? 'local', $oldS3StorageId)->delete($oldPath);
+1 -1
View File
@@ -28,7 +28,7 @@ class TeamAvatarStorageService extends AvatarStorageService
'avatar_path' => $path,
'avatar_storage_type' => $storageType,
'avatar_s3_storage_id' => $s3StorageId,
])->save();
])->touch();
if ($oldPath && ($oldStorageType !== $storageType || $oldS3StorageId !== $s3StorageId)) {
$this->disk($oldStorageType ?? 'local', $oldS3StorageId)->delete($oldPath);
+15
View File
@@ -47,6 +47,21 @@ it('compresses and stores an uploaded profile picture on the configured local st
->and(Storage::disk('images')->size($user->avatar_path))->toBeLessThan(100_000);
})->skip(! extension_loaded('gd'), 'Requires the GD extension to generate and compress the test image.');
it('changes the profile picture URL when an existing picture is replaced', function () {
Storage::fake('images');
$user = User::factory()->create();
$this->actingAs($user);
$upload = fn () => UploadedFile::fake()->createWithContent('avatar.jpg', file_get_contents(base_path('tests/Fixtures/project-icon.jpg')));
app(AvatarStorageService::class)->store($user, $upload());
$oldUrl = profile_avatar_url($user->fresh());
$this->travel(1)->minutes();
app(AvatarStorageService::class)->store($user->fresh(), $upload());
expect(profile_avatar_url($user->fresh()))->not->toBe($oldUrl);
});
it('stores an already compressed browser JPEG without server image extensions', function () {
Storage::fake('images');
$user = User::factory()->create(['name' => 'Test User']);
+18 -2
View File
@@ -29,7 +29,7 @@ beforeEach(function () {
function giveTeamStoredAvatar(Team $team): string
{
$path = "team-avatars/{$team->id}/avatar.jpg";
$team->forceFill(['avatar_path' => $path, 'avatar_storage_type' => 'local'])->save();
$team->forceFill(['avatar_path' => $path, 'avatar_storage_type' => 'local'])->saveQuietly();
Storage::disk('images')->put($path, 'avatar-content');
return $path;
@@ -53,6 +53,22 @@ it('stores a team avatar using the instance image storage setting', function ()
Storage::disk('images')->assertExists($this->team->avatar_path);
});
it('changes the team avatar URL when an existing avatar is replaced', function () {
Storage::fake('images');
giveTeamStoredAvatar($this->team);
$oldUrl = team_avatar_url($this->team->fresh());
$upload = UploadedFile::fake()->createWithContent('team.jpg', file_get_contents(base_path('tests/Fixtures/project-icon.jpg')));
$this->travel(1)->minutes();
Livewire::test(Index::class)
->set('avatar', $upload)
->call('uploadAvatar')
->assertHasNoErrors();
expect(team_avatar_url($this->team->fresh()))->not->toBe($oldUrl);
});
it('does not let a team member change the team avatar', function () {
Storage::fake('images');
$path = giveTeamStoredAvatar($this->team);
@@ -123,7 +139,7 @@ it('deletes the avatar file when the team is deleted', function () {
it('shows team avatars in the team switcher', function () {
$otherTeam = Team::factory()->create(['name' => 'Zeta']);
$this->user->teams()->attach($otherTeam, ['role' => 'member']);
$otherTeam->forceFill(['avatar_path' => "team-avatars/{$otherTeam->id}/avatar.jpg", 'avatar_storage_type' => 'local'])->save();
$otherTeam->forceFill(['avatar_path' => "team-avatars/{$otherTeam->id}/avatar.jpg", 'avatar_storage_type' => 'local'])->saveQuietly();
$this->user->load('teams');
Livewire::test(SwitchTeam::class)