diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index b4b050799..f205dcd53 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -887,7 +887,7 @@ class ApiV1Controller extends Controller $maxId = (int) $request->input('max_id'); if ($maxId > 0) { - $query->where('id', '<=', $maxId); + $query->where('id', '<', $maxId); } } diff --git a/tests/Feature/Api/AccountTest.php b/tests/Feature/Api/AccountTest.php index f4b4243ec..53168cc2a 100644 --- a/tests/Feature/Api/AccountTest.php +++ b/tests/Feature/Api/AccountTest.php @@ -67,6 +67,36 @@ describe('GET /api/v1/accounts/{id}/statuses', function () { ->assertOk() ->assertJsonIsArray(); }); + + it('does not duplicate the boundary status across max_id pages', function () { + $user = User::factory()->create(); + $user->refresh(); + $targetUser = User::factory()->create(); + $targetUser->refresh(); + Status::factory()->count(25)->create([ + 'profile_id' => $targetUser->profile_id, + 'type' => 'photo', + 'scope' => 'public', + ]); + Passport::actingAs($user, ['read']); + + $pageOneIds = collect( + $this->getJson("/api/v1/accounts/{$targetUser->profile_id}/statuses?limit=20") + ->assertOk() + ->json() + )->pluck('id')->all(); + + $lastId = end($pageOneIds); + + $pageTwoIds = collect( + $this->getJson("/api/v1/accounts/{$targetUser->profile_id}/statuses?limit=20&max_id={$lastId}") + ->assertOk() + ->json() + )->pluck('id')->all(); + + expect($pageTwoIds[0] ?? null)->not->toBe($lastId) + ->and(array_intersect($pageOneIds, $pageTwoIds))->toBeEmpty(); + }); }); describe('GET /api/v1/accounts/{id}/followers', function () {