You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pixelfed/tests/Feature/SeasonalAggregationTest.php

82 lines
2.6 KiB
PHTML

<?php
use App\Models\Status;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\DB;
2 weeks ago
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| Seasonal "Year in Review" aggregation
|--------------------------------------------------------------------------
|
| SeasonalController::getData previously computed the average number of
| posts per profile by loading every grouped row into PHP and calling
| ->pluck('count')->avg(). This verifies the SQL-side replacement:
| AVG over a subquery of per-profile counts returns the same value
| without materialising every group in memory.
|
*/
2 weeks ago
/**
* Mirrors the aggregation used in SeasonalController::getData for
* average posts per profile.
*/
function averagePostsPerProfile(string $epochStart, string $epochEnd): float
{
2 weeks ago
return (float) DB::query()->fromSub(
Status::query()
->whereNull('uri')
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->where('created_at', '>', $epochStart)
->where('created_at', '<', $epochEnd)
->groupBy('profile_id')
->selectRaw('count(*) as count'),
'per_profile'
)->avg('count');
}
2 weeks ago
it('averages matching posts per profile in sql', function () {
$epochStart = '2020-01-01 00:00:00';
$epochEnd = '2020-12-31 23:59:59';
$inRange = '2020-06-01 12:00:00';
2 weeks ago
// Profile 1: 2 matching photos. Profile 2: 4 matching photos.
// Average per profile = 3.
Status::factory()->count(2)->photo()->create([
'profile_id' => 1001,
'created_at' => $inRange,
]);
Status::factory()->count(4)->photo()->create([
'profile_id' => 1002,
'created_at' => $inRange,
]);
2 weeks ago
// Noise that must be excluded from the average:
// remote (uri set), wrong type, and out-of-range date.
Status::factory()->photo()->create([
'profile_id' => 1003,
'uri' => 'https://remote.example/statuses/1',
'created_at' => $inRange,
]);
Status::factory()->create([
'profile_id' => 1003,
'type' => 'text',
'created_at' => $inRange,
]);
Status::factory()->photo()->create([
'profile_id' => 1004,
'created_at' => '2019-01-01 00:00:00',
]);
2 weeks ago
expect(averagePostsPerProfile($epochStart, $epochEnd))->toBe(3.0);
});
2 weeks ago
it('returns zero when no posts match', function () {
$average = averagePostsPerProfile('2020-01-01 00:00:00', '2020-12-31 23:59:59');
2 weeks ago
// No rows -> AVG returns null -> cast to 0.0
expect($average)->toBe(0.0);
});