storing analytics data
This commit is contained in:
147
tests/Feature/Discovery/WindowedStatsTest.php
Normal file
147
tests/Feature/Discovery/WindowedStatsTest.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Services\ArtworkStatsService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
beforeEach(function () {
|
||||
config(['scout.driver' => 'null']);
|
||||
});
|
||||
|
||||
// ── Helper: ensure a stats row exists ────────────────────────────────────────
|
||||
|
||||
function seedStats(int $artworkId, array $overrides = []): void
|
||||
{
|
||||
DB::table('artwork_stats')->insertOrIgnore(array_merge([
|
||||
'artwork_id' => $artworkId,
|
||||
'views' => 0,
|
||||
'views_24h' => 0,
|
||||
'views_7d' => 0,
|
||||
'downloads' => 0,
|
||||
'downloads_24h' => 0,
|
||||
'downloads_7d' => 0,
|
||||
'favorites' => 0,
|
||||
'rating_avg' => 0,
|
||||
'rating_count' => 0,
|
||||
], $overrides));
|
||||
}
|
||||
|
||||
// ── ArtworkStatsService ───────────────────────────────────────────────────────
|
||||
|
||||
it('incrementViews updates views, views_24h, and views_7d', function () {
|
||||
$artwork = Artwork::factory()->create(['is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay()]);
|
||||
seedStats($artwork->id);
|
||||
|
||||
app(ArtworkStatsService::class)->incrementViews($artwork->id, 3, defer: false);
|
||||
|
||||
$row = DB::table('artwork_stats')->where('artwork_id', $artwork->id)->first();
|
||||
expect((int) $row->views)->toBe(3);
|
||||
expect((int) $row->views_24h)->toBe(3);
|
||||
expect((int) $row->views_7d)->toBe(3);
|
||||
});
|
||||
|
||||
it('incrementDownloads updates downloads, downloads_24h, and downloads_7d', function () {
|
||||
$artwork = Artwork::factory()->create(['is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay()]);
|
||||
seedStats($artwork->id);
|
||||
|
||||
app(ArtworkStatsService::class)->incrementDownloads($artwork->id, 2, defer: false);
|
||||
|
||||
$row = DB::table('artwork_stats')->where('artwork_id', $artwork->id)->first();
|
||||
expect((int) $row->downloads)->toBe(2);
|
||||
expect((int) $row->downloads_24h)->toBe(2);
|
||||
expect((int) $row->downloads_7d)->toBe(2);
|
||||
});
|
||||
|
||||
it('multiple view increments accumulate across all three columns', function () {
|
||||
$artwork = Artwork::factory()->create(['is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay()]);
|
||||
seedStats($artwork->id);
|
||||
|
||||
$svc = app(ArtworkStatsService::class);
|
||||
$svc->incrementViews($artwork->id, 1, defer: false);
|
||||
$svc->incrementViews($artwork->id, 1, defer: false);
|
||||
$svc->incrementViews($artwork->id, 1, defer: false);
|
||||
|
||||
$row = DB::table('artwork_stats')->where('artwork_id', $artwork->id)->first();
|
||||
expect((int) $row->views)->toBe(3);
|
||||
expect((int) $row->views_24h)->toBe(3);
|
||||
expect((int) $row->views_7d)->toBe(3);
|
||||
});
|
||||
|
||||
// ── ResetWindowedStatsCommand ─────────────────────────────────────────────────
|
||||
|
||||
it('reset-windowed-stats --period=24h zeros views_24h', function () {
|
||||
$artwork = Artwork::factory()->create(['is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay()]);
|
||||
seedStats($artwork->id, ['views_24h' => 50, 'views_7d' => 200]);
|
||||
|
||||
$this->artisan('skinbase:reset-windowed-stats', ['--period' => '24h'])
|
||||
->assertExitCode(0);
|
||||
|
||||
$row = DB::table('artwork_stats')->where('artwork_id', $artwork->id)->first();
|
||||
expect((int) $row->views_24h)->toBe(0);
|
||||
// 7d column is NOT touched by a 24h reset
|
||||
expect((int) $row->views_7d)->toBe(200);
|
||||
});
|
||||
|
||||
it('reset-windowed-stats --period=7d zeros views_7d', function () {
|
||||
$artwork = Artwork::factory()->create(['is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay()]);
|
||||
seedStats($artwork->id, ['views_24h' => 50, 'views_7d' => 200]);
|
||||
|
||||
$this->artisan('skinbase:reset-windowed-stats', ['--period' => '7d'])
|
||||
->assertExitCode(0);
|
||||
|
||||
$row = DB::table('artwork_stats')->where('artwork_id', $artwork->id)->first();
|
||||
expect((int) $row->views_7d)->toBe(0);
|
||||
// 24h column is NOT touched by a 7d reset
|
||||
expect((int) $row->views_24h)->toBe(50);
|
||||
});
|
||||
|
||||
it('reset-windowed-stats recomputes downloads_24h from artwork_downloads log', function () {
|
||||
$artwork = Artwork::factory()->create(['is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay()]);
|
||||
seedStats($artwork->id, ['downloads_24h' => 99]); // stale value
|
||||
|
||||
// Insert 3 downloads within the last 24 hours
|
||||
$ip = inet_pton('127.0.0.1');
|
||||
DB::table('artwork_downloads')->insert([
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subHours(1)],
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subHours(6)],
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subHours(12)],
|
||||
]);
|
||||
|
||||
// Insert 2 old downloads outside the 24h window
|
||||
DB::table('artwork_downloads')->insert([
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subDays(2)],
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subDays(5)],
|
||||
]);
|
||||
|
||||
$this->artisan('skinbase:reset-windowed-stats', ['--period' => '24h'])
|
||||
->assertExitCode(0);
|
||||
|
||||
$row = DB::table('artwork_stats')->where('artwork_id', $artwork->id)->first();
|
||||
// Should equal exactly the 3 recent downloads, not the stale 99
|
||||
expect((int) $row->downloads_24h)->toBe(3);
|
||||
});
|
||||
|
||||
it('reset-windowed-stats recomputes downloads_7d including all downloads in 7-day window', function () {
|
||||
$artwork = Artwork::factory()->create(['is_public' => true, 'is_approved' => true, 'published_at' => now()->subDays(10)]);
|
||||
seedStats($artwork->id, ['downloads_7d' => 0]);
|
||||
|
||||
$ip = inet_pton('127.0.0.1');
|
||||
DB::table('artwork_downloads')->insert([
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subDays(1)],
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subDays(5)],
|
||||
['artwork_id' => $artwork->id, 'user_id' => null, 'ip' => $ip, 'user_agent' => null, 'created_at' => now()->subDays(8)], // outside 7d
|
||||
]);
|
||||
|
||||
$this->artisan('skinbase:reset-windowed-stats', ['--period' => '7d'])
|
||||
->assertExitCode(0);
|
||||
|
||||
$row = DB::table('artwork_stats')->where('artwork_id', $artwork->id)->first();
|
||||
expect((int) $row->downloads_7d)->toBe(2);
|
||||
});
|
||||
|
||||
it('reset-windowed-stats returns failure for invalid period', function () {
|
||||
$this->artisan('skinbase:reset-windowed-stats', ['--period' => 'bad'])
|
||||
->assertExitCode(1);
|
||||
});
|
||||
Reference in New Issue
Block a user