feat: followers and following
This commit is contained in:
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Resources;
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
/** @mixin User */
|
||||||
class FollowResource extends JsonResource
|
class FollowResource extends JsonResource
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +20,9 @@ class FollowResource extends JsonResource
|
|||||||
return [
|
return [
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
|
'bio' => $this->bio,
|
||||||
'avatarUrl' => $this->avatar_url ? asset(Storage::url($this->avatar_url)) : null,
|
'avatarUrl' => $this->avatar_url ? asset(Storage::url($this->avatar_url)) : null,
|
||||||
|
'accountVerified' => $this->account_verified_at !== null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\FollowStatus;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Laravel\Sanctum\Sanctum;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
it('lists only accepted followers with the profile fields required by the mobile app', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$acceptedFollower = User::factory()->create([
|
||||||
|
'account_verified_at' => now(),
|
||||||
|
'bio' => 'Cuisine équilibrée.',
|
||||||
|
]);
|
||||||
|
$pendingFollower = User::factory()->create();
|
||||||
|
|
||||||
|
$acceptedFollower->following()->attach($user, ['status' => FollowStatus::Accepted]);
|
||||||
|
$pendingFollower->following()->attach($user, ['status' => FollowStatus::Pending]);
|
||||||
|
|
||||||
|
Sanctum::actingAs($user);
|
||||||
|
|
||||||
|
$this->getJson("/api/users/{$user->id}/followers?per_page=10")
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonCount(1, 'data')
|
||||||
|
->assertJsonPath('data.0.id', $acceptedFollower->id)
|
||||||
|
->assertJsonPath('data.0.bio', 'Cuisine équilibrée.')
|
||||||
|
->assertJsonPath('data.0.accountVerified', true)
|
||||||
|
->assertJsonPath('meta.total', 1)
|
||||||
|
->assertJsonMissing(['id' => $pendingFollower->id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lists only accepted followed accounts and exposes the pagination total', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$accepted = User::factory()->count(3)->create();
|
||||||
|
$pending = User::factory()->create();
|
||||||
|
|
||||||
|
$accepted->each(
|
||||||
|
fn (User $followed): mixed => $user->following()->attach(
|
||||||
|
$followed,
|
||||||
|
['status' => FollowStatus::Accepted],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$user->following()->attach($pending, ['status' => FollowStatus::Pending]);
|
||||||
|
|
||||||
|
Sanctum::actingAs($user);
|
||||||
|
|
||||||
|
$this->getJson("/api/users/{$user->id}/following?per_page=2")
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonCount(2, 'data')
|
||||||
|
->assertJsonPath('meta.total', 3)
|
||||||
|
->assertJsonPath('meta.per_page', 2)
|
||||||
|
->assertJsonMissing(['id' => $pending->id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not expose another users social lists', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$otherUser = User::factory()->create();
|
||||||
|
|
||||||
|
Sanctum::actingAs($user);
|
||||||
|
|
||||||
|
$this->getJson("/api/users/{$otherUser->id}/followers")->assertForbidden();
|
||||||
|
$this->getJson("/api/users/{$otherUser->id}/following")->assertForbidden();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user