Artwork::factory()->create(array_merge([ 'is_approved' => true, 'is_public' => true, 'published_at' => now()->subDays(40), 'deleted_at' => null, ], $attrs))); } function leaderboardSnapshot(int $artworkId, $hour, int $views): void { ArtworkMetricSnapshotHourly::create([ 'artwork_id' => $artworkId, 'bucket_hour' => $hour, 'views_count' => $views, 'downloads_count' => 0, 'favourites_count' => 0, 'comments_count' => 0, 'shares_count' => 0, ]); } it('scores monthly artwork leaderboards from period view deltas not cumulative sums', function () { $old = leaderboardArtwork(); $bornInMonth = leaderboardArtwork([ 'published_at' => now()->subDays(4), 'created_at' => now()->subDays(4), ]); leaderboardSnapshot($old->id, now()->subDays(20)->startOfHour(), 100); leaderboardSnapshot($old->id, now()->subHour()->startOfHour(), 140); leaderboardSnapshot($bornInMonth->id, now()->subDays(3)->startOfHour(), 25); leaderboardSnapshot($bornInMonth->id, now()->subHour()->startOfHour(), 55); app(LeaderboardService::class)->calculateArtworkLeaderboard(Leaderboard::PERIOD_MONTHLY); $rows = Leaderboard::query() ->where('type', Leaderboard::TYPE_ARTWORK) ->where('period', Leaderboard::PERIOD_MONTHLY) ->get() ->keyBy('entity_id'); expect((int) $rows[$old->id]->score)->toBe(40) ->and((int) $rows[$bornInMonth->id]->score)->toBe(55) ->and((int) $rows[$old->id]->score)->not->toBe(240); }); it('scores daily and weekly artwork leaderboards from the same delta rules', function () { $artwork = leaderboardArtwork(); leaderboardSnapshot($artwork->id, now()->subDays(7)->subHour()->startOfHour(), 10); leaderboardSnapshot($artwork->id, now()->subDays(3)->startOfHour(), 18); leaderboardSnapshot($artwork->id, now()->subDay()->subHour()->startOfHour(), 18); leaderboardSnapshot($artwork->id, now()->subHours(2)->startOfHour(), 21); $service = app(LeaderboardService::class); $service->calculateArtworkLeaderboard(Leaderboard::PERIOD_WEEKLY); $service->calculateArtworkLeaderboard(Leaderboard::PERIOD_DAILY); $weekly = Leaderboard::query() ->where('type', Leaderboard::TYPE_ARTWORK) ->where('period', Leaderboard::PERIOD_WEEKLY) ->where('entity_id', $artwork->id) ->value('score'); $daily = Leaderboard::query() ->where('type', Leaderboard::TYPE_ARTWORK) ->where('period', Leaderboard::PERIOD_DAILY) ->where('entity_id', $artwork->id) ->value('score'); expect((int) $weekly)->toBe(11) ->and((int) $daily)->toBe(3); });