Files
SkinbaseNova/tests/Feature/Admin/FeedPerformanceReportTest.php
2026-02-14 15:14:12 +01:00

105 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\Artwork;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
uses(RefreshDatabase::class);
it('admin report returns feed performance breakdown and top clicked artworks', function () {
$admin = User::factory()->create(['role' => 'admin']);
$artworkA = Artwork::factory()->create(['title' => 'Feed Artwork A']);
$artworkB = Artwork::factory()->create(['title' => 'Feed Artwork B']);
$metricDate = now()->subDay()->toDateString();
DB::table('feed_daily_metrics')->insert([
[
'metric_date' => $metricDate,
'algo_version' => 'clip-cosine-v1',
'source' => 'personalized',
'impressions' => 10,
'clicks' => 4,
'saves' => 2,
'ctr' => 0.4,
'save_rate' => 0.5,
'dwell_0_5' => 1,
'dwell_5_30' => 1,
'dwell_30_120' => 1,
'dwell_120_plus' => 1,
'created_at' => now(),
'updated_at' => now(),
],
]);
DB::table('feed_events')->insert([
[
'event_date' => $metricDate,
'event_type' => 'feed_impression',
'user_id' => $admin->id,
'artwork_id' => $artworkA->id,
'position' => 1,
'algo_version' => 'clip-cosine-v1',
'source' => 'personalized',
'dwell_seconds' => null,
'occurred_at' => now()->subDay(),
'created_at' => now(),
'updated_at' => now(),
],
[
'event_date' => $metricDate,
'event_type' => 'feed_click',
'user_id' => $admin->id,
'artwork_id' => $artworkA->id,
'position' => 1,
'algo_version' => 'clip-cosine-v1',
'source' => 'personalized',
'dwell_seconds' => 12,
'occurred_at' => now()->subDay(),
'created_at' => now(),
'updated_at' => now(),
],
[
'event_date' => $metricDate,
'event_type' => 'feed_click',
'user_id' => $admin->id,
'artwork_id' => $artworkB->id,
'position' => 2,
'algo_version' => 'clip-cosine-v1',
'source' => 'personalized',
'dwell_seconds' => 7,
'occurred_at' => now()->subDay(),
'created_at' => now(),
'updated_at' => now(),
],
]);
$response = $this->actingAs($admin)->getJson('/api/admin/reports/feed-performance?from=' . $metricDate . '&to=' . $metricDate);
$response->assertOk();
$response->assertJsonPath('meta.from', $metricDate);
$response->assertJsonPath('meta.to', $metricDate);
$rows = collect($response->json('by_algo_source'));
expect($rows->count())->toBe(1);
expect($rows->first()['algo_version'])->toBe('clip-cosine-v1');
expect($rows->first()['source'])->toBe('personalized');
expect((float) $rows->first()['ctr'])->toBe(0.4);
$top = collect($response->json('top_clicked_artworks'));
expect($top->isNotEmpty())->toBeTrue();
expect((int) $top->first()['artwork_id'])->toBe($artworkA->id);
});
it('non-admin is denied feed performance report endpoint', function () {
$user = User::factory()->create(['role' => 'user']);
$response = $this->actingAs($user)->getJson('/api/admin/reports/feed-performance');
$response->assertStatus(403);
});