Guard hashtag follow against null profile for soft-deleted accounts

pull/7119/head
Your Name 2 weeks ago
parent c40814d9b3
commit 8258a5a5f8

@ -23,13 +23,16 @@ class HashtagFollowController extends Controller
$user = $request->user(); $user = $request->user();
$profile = $user->profile; $profile = $user->profile;
abort_if(! $profile, 422, 'Profile not available for this account.');
$tag = $request->input('name'); $tag = $request->input('name');
$hashtag = Hashtag::whereName($tag)->firstOrFail(); $hashtag = Hashtag::whereName($tag)->firstOrFail();
$hashtagFollow = HashtagFollow::firstOrCreate([ $hashtagFollow = HashtagFollow::firstOrCreate([
'user_id' => $user->id, 'user_id' => $user->id,
'profile_id' => $user->profile_id ?? $user->profile->id, 'profile_id' => $user->profile_id ?? $profile->id,
'hashtag_id' => $hashtag->id, 'hashtag_id' => $hashtag->id,
]); ]);

@ -0,0 +1,49 @@
<?php
use App\Models\Hashtag;
use App\Models\User;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| HashtagFollowController profile guard
|--------------------------------------------------------------------------
|
| store() dereferences $user->profile->id when calling HashtagService. A user
| whose profile has been soft-deleted (e.g. via admin account deletion) keeps a
| live session but has a null profile relationship, so the endpoint must fail
| deterministically (422) instead of throwing a 500.
|
*/
it('follows a hashtag for a normal authenticated user', function () {
$user = User::factory()->create();
$user->refresh();
Hashtag::create(['name' => 'landscape', 'slug' => 'landscape']);
$this->actingAs($user)
->postJson('/api/local/discover/tag/subscribe', ['name' => 'landscape'])
->assertOk()
->assertJson(['state' => 'created']);
});
it('returns 422 instead of 500 when the profile is soft-deleted', function () {
$user = User::factory()->create();
$user->refresh();
Hashtag::create(['name' => 'landscape', 'slug' => 'landscape']);
// Soft-delete the profile, as the admin account-deletion pipeline does,
// while leaving the still-authenticated session intact. Unset the cached
// relation so the controller re-queries and sees null, matching what a
// fresh request resolves after the pipeline runs in another request.
App\Models\Profile::whereUserId($user->id)->delete();
$user->unsetRelation('profile');
$this->actingAs($user)
->postJson('/api/local/discover/tag/subscribe', ['name' => 'landscape'])
->assertStatus(422);
});
Loading…
Cancel
Save