Allow the broader gender enum and add tests for country saves and discovery event recording.
185 lines
6.3 KiB
PHP
185 lines
6.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\ActivityEvent;
|
|
use App\Models\Artwork;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
// ── ActivityEvent::record() factory helper ────────────────────────────────────
|
|
|
|
it('creates a db row via record()', function () {
|
|
$user = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
ActivityEvent::record(
|
|
actorId: $user->id,
|
|
type: ActivityEvent::TYPE_FAVORITE,
|
|
targetType: ActivityEvent::TARGET_ARTWORK,
|
|
targetId: $artwork->id,
|
|
meta: ['source' => 'test'],
|
|
);
|
|
|
|
$this->assertDatabaseHas('activity_events', [
|
|
'actor_id' => $user->id,
|
|
'type' => 'favorite',
|
|
'target_type' => 'artwork',
|
|
'target_id' => $artwork->id,
|
|
]);
|
|
});
|
|
|
|
it('stores all five event types without error', function () {
|
|
$user = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
$events = [
|
|
[ActivityEvent::TYPE_UPLOAD, ActivityEvent::TARGET_ARTWORK, $artwork->id],
|
|
[ActivityEvent::TYPE_COMMENT, ActivityEvent::TARGET_ARTWORK, $artwork->id],
|
|
[ActivityEvent::TYPE_FAVORITE, ActivityEvent::TARGET_ARTWORK, $artwork->id],
|
|
[ActivityEvent::TYPE_AWARD, ActivityEvent::TARGET_ARTWORK, $artwork->id],
|
|
[ActivityEvent::TYPE_FOLLOW, ActivityEvent::TARGET_USER, $user->id],
|
|
];
|
|
|
|
foreach ($events as [$type, $targetType, $targetId]) {
|
|
ActivityEvent::record($user->id, $type, $targetType, $targetId);
|
|
}
|
|
|
|
expect(ActivityEvent::where('actor_id', $user->id)->count())->toBe(5);
|
|
});
|
|
|
|
it('created_at is populated on the returned instance', function () {
|
|
$user = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
$event = ActivityEvent::record(
|
|
$user->id,
|
|
ActivityEvent::TYPE_COMMENT,
|
|
ActivityEvent::TARGET_ARTWORK,
|
|
$artwork->id,
|
|
);
|
|
|
|
expect($event->created_at)->not->toBeNull();
|
|
});
|
|
|
|
it('actor relation resolves after record()', function () {
|
|
$user = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
$event = ActivityEvent::record($user->id, ActivityEvent::TYPE_UPLOAD, ActivityEvent::TARGET_ARTWORK, $artwork->id);
|
|
|
|
expect($event->actor->id)->toBe($user->id);
|
|
});
|
|
|
|
it('meta is null when empty array is passed', function () {
|
|
$user = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
$event = ActivityEvent::record($user->id, ActivityEvent::TYPE_UPLOAD, ActivityEvent::TARGET_ARTWORK, $artwork->id);
|
|
|
|
expect($event->meta)->toBeNull();
|
|
});
|
|
|
|
it('meta is stored when non-empty array is passed', function () {
|
|
$user = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
$event = ActivityEvent::record(
|
|
$user->id,
|
|
ActivityEvent::TYPE_AWARD,
|
|
ActivityEvent::TARGET_ARTWORK,
|
|
$artwork->id,
|
|
['medal' => 'gold'],
|
|
);
|
|
|
|
expect($event->meta)->toBe(['medal' => 'gold']);
|
|
});
|
|
|
|
// ── Community activity feed route ─────────────────────────────────────────────
|
|
|
|
it('global activity feed returns 200 for guests', function () {
|
|
$this->get('/community/activity')->assertStatus(200);
|
|
});
|
|
|
|
it('following tab returns 200 for users with no follows', function () {
|
|
$user = User::factory()->create();
|
|
|
|
$this->actingAs($user)
|
|
->get('/community/activity?filter=following')
|
|
->assertStatus(200);
|
|
});
|
|
|
|
it('following tab shows only events from followed users', function () {
|
|
$user = User::factory()->create();
|
|
$creator = User::factory()->create();
|
|
$other = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
// user_followers has no updated_at column
|
|
DB::table('user_followers')->insert([
|
|
'user_id' => $creator->id,
|
|
'follower_id' => $user->id,
|
|
'created_at' => now(),
|
|
]);
|
|
|
|
// Event from followed creator
|
|
ActivityEvent::record($creator->id, ActivityEvent::TYPE_UPLOAD, ActivityEvent::TARGET_ARTWORK, $artwork->id);
|
|
// Event from non-followed user (should not appear)
|
|
ActivityEvent::record($other->id, ActivityEvent::TYPE_UPLOAD, ActivityEvent::TARGET_ARTWORK, $artwork->id);
|
|
|
|
$response = $this->actingAs($user)->get('/community/activity?filter=following');
|
|
$response->assertStatus(200);
|
|
|
|
$props = $response->viewData('props');
|
|
$events = collect($props['initialActivities'] ?? []);
|
|
|
|
expect($events)->toHaveCount(1);
|
|
expect(data_get($events->first(), 'user.id'))->toBe($creator->id);
|
|
});
|
|
|
|
it('comments and replies feeds do not query unrelated activity sources', function () {
|
|
foreach (['comments', 'replies'] as $filter) {
|
|
DB::flushQueryLog();
|
|
DB::enableQueryLog();
|
|
|
|
$this->getJson('/api/activity?filter=' . $filter)
|
|
->assertOk()
|
|
->assertJsonPath('filter', $filter);
|
|
|
|
$sql = collect(DB::getQueryLog())
|
|
->pluck('query')
|
|
->map(fn (string $query): string => strtolower($query))
|
|
->implode("\n");
|
|
|
|
expect($sql)
|
|
->not->toContain('activity_events')
|
|
->not->toContain('comment_reactions')
|
|
->not->toContain('user_mentions');
|
|
}
|
|
});
|
|
|
|
it('my activity feed keeps only the authenticated actor activity', function () {
|
|
$user = User::factory()->create();
|
|
$other = User::factory()->create();
|
|
$artwork = Artwork::factory()->create();
|
|
|
|
ActivityEvent::record($user->id, ActivityEvent::TYPE_UPLOAD, ActivityEvent::TARGET_ARTWORK, $artwork->id);
|
|
ActivityEvent::record($other->id, ActivityEvent::TYPE_UPLOAD, ActivityEvent::TARGET_ARTWORK, $artwork->id);
|
|
|
|
$response = $this->actingAs($user)->getJson('/api/activity?filter=my');
|
|
|
|
$response->assertOk();
|
|
expect(collect($response->json('data')))
|
|
->toHaveCount(1)
|
|
->and(data_get($response->json('data.0'), 'user.id'))->toBe($user->id);
|
|
});
|
|
|
|
it('guest activity feed is capped to the latest page', function () {
|
|
$response = $this->getJson('/api/activity?page=2&per_page=50');
|
|
|
|
$response->assertOk();
|
|
expect($response->json('meta.current_page'))->toBe(1)
|
|
->and($response->json('meta.per_page'))->toBe(20)
|
|
->and(count($response->json('data')))->toBeLessThanOrEqual(20);
|
|
});
|