diff --git a/app/Services/AvatarStorageService.php b/app/Services/AvatarStorageService.php index 983b144b94..889293d668 100644 --- a/app/Services/AvatarStorageService.php +++ b/app/Services/AvatarStorageService.php @@ -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); diff --git a/app/Services/TeamAvatarStorageService.php b/app/Services/TeamAvatarStorageService.php index ee66f8bff7..6ead014a29 100644 --- a/app/Services/TeamAvatarStorageService.php +++ b/app/Services/TeamAvatarStorageService.php @@ -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); diff --git a/tests/Feature/ProfileAvatarTest.php b/tests/Feature/ProfileAvatarTest.php index f668a58372..ceecc02cf2 100644 --- a/tests/Feature/ProfileAvatarTest.php +++ b/tests/Feature/ProfileAvatarTest.php @@ -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']); diff --git a/tests/Feature/TeamAvatarTest.php b/tests/Feature/TeamAvatarTest.php index f2c91172ae..1b91491acf 100644 --- a/tests/Feature/TeamAvatarTest.php +++ b/tests/Feature/TeamAvatarTest.php @@ -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)