From 9a19ebd0af52f4488e364fe374b5d5e8f890764e Mon Sep 17 00:00:00 2001 From: Christopher Gammie Date: Thu, 8 Aug 2024 20:11:53 +0100 Subject: [PATCH] tests: minor improvements to cursor pagination test --- .../Pagination/CursorPaginationTest.php | 237 ++++++++++++------ 1 file changed, 158 insertions(+), 79 deletions(-) diff --git a/tests/lib/Acceptance/Pagination/CursorPaginationTest.php b/tests/lib/Acceptance/Pagination/CursorPaginationTest.php index 0b6b975..07aa18f 100644 --- a/tests/lib/Acceptance/Pagination/CursorPaginationTest.php +++ b/tests/lib/Acceptance/Pagination/CursorPaginationTest.php @@ -90,11 +90,19 @@ public function testDefaultPagination(): void { $this->posts->method('defaultPagination')->willReturn(['limit' => 10]); + $posts = Post::factory()->count(4)->create(); + $meta = [ - 'from' => $this->encodeCursor(["id" => "4"], pointsToNextItems: false), + 'from' => $this->encodeCursor( + ["id" => (string) $posts[3]->getKey()], + pointsToNextItems: false, + ), 'hasMore' => false, 'perPage' => 10, - 'to' => $this->encodeCursor(["id" => "1"], pointsToNextItems: true), + 'to' => $this->encodeCursor( + ["id" => (string) $posts[0]->getKey()], + pointsToNextItems: true, + ), ]; $links = [ @@ -105,8 +113,6 @@ public function testDefaultPagination(): void ], ]; - $posts = Post::factory()->count(4)->create(); - $page = $this->posts ->repository() ->queryAll() @@ -211,7 +217,7 @@ public function testNoPages(): void $links = [ 'first' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ - 'page' => ['limit' => '3'] + 'page' => ['limit' => '3'], ]), ], ]; @@ -231,10 +237,16 @@ public function testWithoutCursor(): void $posts = Post::factory()->count(4)->create(); $meta = [ - 'from' => $this->encodeCursor(["id" => "4"], pointsToNextItems: false), + 'from' => $this->encodeCursor( + ["id" => (string) $posts[3]->getKey()], + pointsToNextItems: false, + ), 'hasMore' => true, 'perPage' => 3, - 'to' => $this->encodeCursor(["id" => "2"], pointsToNextItems: true), + 'to' => $this->encodeCursor( + ["id" => (string) $posts[1]->getKey()], + pointsToNextItems: true, + ), ]; $links = [ @@ -246,7 +258,10 @@ public function testWithoutCursor(): void 'next' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ - 'after' => $this->encodeCursor(["id" => "2"], pointsToNextItems: true), + 'after' => $this->encodeCursor( + ["id" => (string) $posts[1]->getKey()], + pointsToNextItems: true, + ), 'limit' => '3', ], ]), @@ -270,10 +285,16 @@ public function testWithAscending(): void $posts = Post::factory()->count(4)->create(); $meta = [ - 'from' => $this->encodeCursor(["id" => "1"], pointsToNextItems: false), + 'from' => $this->encodeCursor( + ["id" => (string) $posts[0]->getKey()], + pointsToNextItems: false, + ), 'hasMore' => true, 'perPage' => 3, - 'to' => $this->encodeCursor(["id" => "3"], pointsToNextItems: true), + 'to' => $this->encodeCursor( + ["id" => (string) $posts[2]->getKey()], + pointsToNextItems: true, + ), ]; $links = [ @@ -285,7 +306,10 @@ public function testWithAscending(): void 'next' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ - 'after' => $this->encodeCursor(["id" => "3"], pointsToNextItems: true), + 'after' => $this->encodeCursor( + ["id" => (string) $posts[2]->getKey()], + pointsToNextItems: true, + ), 'limit' => '3', ], ]), @@ -309,10 +333,16 @@ public function testAfter(): void $this->paginator->withCamelCaseMeta(); $meta = [ - 'from' => $this->encodeCursor(["id" => "1"], pointsToNextItems: false), + 'from' => $this->encodeCursor( + ["id" => (string) $posts[0]->getKey()], + pointsToNextItems: false, + ), 'hasMore' => false, 'perPage' => 3, - 'to' => $this->encodeCursor(["id" => "1"], pointsToNextItems: true), + 'to' => $this->encodeCursor( + ["id" => (string) $posts[0]->getKey()], + pointsToNextItems: true, + ), ]; $links = [ @@ -324,7 +354,10 @@ public function testAfter(): void 'prev' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ - 'before' => $this->encodeCursor(["id" => "1"], pointsToNextItems: false), + 'before' => $this->encodeCursor( + ["id" => (string) $posts[0]->getKey()], + pointsToNextItems: false, + ), 'limit' => '3', ], ]), @@ -332,7 +365,10 @@ public function testAfter(): void ]; $page = $this->posts->repository()->queryAll()->paginate([ - 'after' => $this->encodeCursor(["id" => "2"], pointsToNextItems: true), + 'after' => $this->encodeCursor( + ["id" => (string) $posts[1]->getKey()], + pointsToNextItems: true, + ), 'limit' => '3', ]); @@ -354,12 +390,12 @@ public function testAfterWithIdEncoding(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => 'TEST-7', + "id" => 'TEST-' . $posts[6]->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => 3, 'to' => $this->encodeCursor([ - "id" => 'TEST-5', + "id" => 'TEST-' . $posts[4]->getKey(), ], pointsToNextItems: true), ]; @@ -373,7 +409,7 @@ public function testAfterWithIdEncoding(): void 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'after' => $this->encodeCursor([ - "id" => "TEST-5", + "id" => "TEST-" . $posts[4]->getKey(), ], pointsToNextItems: true), 'limit' => '3', ] @@ -383,7 +419,7 @@ public function testAfterWithIdEncoding(): void 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'before' => $this->encodeCursor([ - "id" => "TEST-7", + "id" => "TEST-" . $posts[6]->getKey(), ], pointsToNextItems: false), 'limit' => '3', ], @@ -393,7 +429,7 @@ public function testAfterWithIdEncoding(): void $page = $this->posts->repository()->queryAll()->paginate([ 'after' => $this->encodeCursor([ - "id" => 'TEST-8', + "id" => 'TEST-' . $posts[7]->getKey(), ], pointsToNextItems: true), 'limit' => 3, ]); @@ -413,12 +449,12 @@ public function testBefore(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => "4", + "id" => (string) $posts[3]->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => 3, 'to' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), ]; @@ -432,7 +468,7 @@ public function testBefore(): void 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'before' => $this->encodeCursor([ - "id" => "4", + "id" => (string) $posts[3]->getKey(), ], pointsToNextItems: false), 'limit' => '3', ] @@ -442,7 +478,7 @@ public function testBefore(): void $page = $this->posts->repository()->queryAll()->paginate([ 'before' => $this->encodeCursor([ - "id" => "1", + "id" => (string) $posts[0]->getKey(), ], pointsToNextItems: false), 'limit' => '3', ]); @@ -465,12 +501,12 @@ public function testBeforeWithIdEncoding(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => 'TEST-7', + "id" => 'TEST-' . $posts[6]->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => 3, 'to' => $this->encodeCursor([ - "id" => 'TEST-5', + "id" => 'TEST-' . $posts[4]->getKey(), ], pointsToNextItems: true), ]; @@ -484,7 +520,7 @@ public function testBeforeWithIdEncoding(): void 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'before' => $this->encodeCursor([ - "id" => "TEST-7", + "id" => "TEST-" . $posts[6]->getKey(), ], pointsToNextItems: false), 'limit' => '3', ], @@ -494,7 +530,7 @@ public function testBeforeWithIdEncoding(): void $page = $this->posts->repository()->queryAll()->paginate([ 'before' => $this->encodeCursor([ - "id" => 'TEST-4', + "id" => 'TEST-' . $posts[3]->getKey(), ], pointsToNextItems: false), 'limit' => 3, ]); @@ -513,12 +549,12 @@ public function testItUsesModelDefaultPerPage(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => (string) ($expected + 1), + "id" => (string) $posts->last()->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => $expected, 'to' => $this->encodeCursor([ - "id" => '2', + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), ]; @@ -532,7 +568,7 @@ public function testItUsesModelDefaultPerPage(): void 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'after' => $this->encodeCursor([ - "id" => '2', + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'limit' => $expected, ], @@ -560,12 +596,12 @@ public function testItUsesDefaultPerPage(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => (string) ($expected + 1), + "id" => (string) $posts->last()->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => $expected, 'to' => $this->encodeCursor([ - "id" => '2', + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), ]; @@ -579,7 +615,7 @@ public function testItUsesDefaultPerPage(): void 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'after' => $this->encodeCursor([ - "id" => '2', + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'limit' => $expected, ], @@ -631,7 +667,7 @@ public function testPageWithReverseKeyWhenAscending(): void * * @see https://github.com/cloudcreativity/laravel-json-api/issues/313 */ - public function testDeterministicOrder(): void + public function testDeterministicOrder1(): void { $first = Video::factory()->create([ 'created_at' => Carbon::now()->subWeek(), @@ -656,6 +692,28 @@ public function testDeterministicOrder(): void ->paginate(['limit' => '3']); $this->assertPage([$first, $fourth, $third], $page); + } + + /** + * @return void + */ + public function testDeterministicOrder2(): void + { + Video::factory()->create([ + 'created_at' => Carbon::now()->subWeek(), + ]); + + $second = Video::factory()->create([ + 'created_at' => Carbon::now()->subHour(), + ]); + + $third = Video::factory()->create([ + 'created_at' => $second->created_at, + ]); + + $fourth = Video::factory()->create([ + 'created_at' => $second->created_at, + ]); $page = $this->videos->repository()->queryAll() ->sort('-createdAt') @@ -667,7 +725,7 @@ public function testDeterministicOrder(): void /** * @return void */ - public function testMultipleSorts(): void + public function testMultipleSorts1(): void { $first = Video::factory()->create([ 'title' => 'b', @@ -679,7 +737,7 @@ public function testMultipleSorts(): void 'created_at' => Carbon::now()->subHour(), ]); - $third = Video::factory()->create([ + Video::factory()->create([ 'title' => 'b', 'created_at' => $second->created_at, ]); @@ -694,6 +752,32 @@ public function testMultipleSorts(): void ->paginate(['limit' => '3']); $this->assertPage([$fourth, $second, $first], $page); + } + + /** + * @return void + */ + public function testMultipleSorts2(): void + { + Video::factory()->create([ + 'title' => 'b', + 'created_at' => Carbon::now()->subWeek(), + ]); + + $second = Video::factory()->create([ + 'title' => 'a', + 'created_at' => Carbon::now()->subHour(), + ]); + + $third = Video::factory()->create([ + 'title' => 'b', + 'created_at' => $second->created_at, + ]); + + $fourth = Video::factory()->create([ + 'title' => 'a', + 'created_at' => $second->created_at, + ]); $page = $this->videos->repository()->queryAll() ->sort('title,-createdAt') @@ -717,7 +801,7 @@ public function testWithoutKeySort(): void 'title' => 'a', ]); - $third = Video::factory()->create([ + Video::factory()->create([ 'title' => 'c', ]); @@ -730,14 +814,6 @@ public function testWithoutKeySort(): void ->paginate(['limit' => '3']); $this->assertPage([$first, $second, $fourth], $page); - - $this->paginator->withKeySort(); - - $page = $this->videos->repository()->queryAll() - ->sort('title') - ->paginate(['limit' => '3']); - - $this->assertPage([$second, $first, $fourth], $page); } /** @@ -745,21 +821,21 @@ public function testWithoutKeySort(): void */ public function testCustomPageKeys(): void { - Post::factory()->count(4)->create(); + $posts = Post::factory()->count(4)->create(); $this->paginator->withAfterKey('next')->withBeforeKey('prev')->withLimitKey('perPage'); $links = [ 'first' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ - 'page' => ['perPage' => '3'] - ]), + 'page' => ['perPage' => '3'] + ]), ], 'next' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'next' => $this->encodeCursor( - ["id" => "2"], + ["id" => (string) $posts[1]->getKey()], pointsToNextItems: true, ), 'perPage' => '3', @@ -776,15 +852,15 @@ public function testCustomPageKeys(): void $links = [ 'first' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ - 'page' => ['perPage' => '3'] - ]), + 'page' => ['perPage' => '3'] + ]), ], 'prev' => [ 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'perPage' => '3', 'prev' => $this->encodeCursor( - ["id" => "1"], + ["id" => (string) $posts[0]->getKey()], pointsToNextItems: false, ), ], @@ -793,7 +869,10 @@ public function testCustomPageKeys(): void ]; $page = $this->posts->repository()->queryAll()->paginate([ - 'next' => $this->encodeCursor(["id" => "2"], pointsToNextItems: true), + 'next' => $this->encodeCursor( + ["id" => (string) $posts[1]->getKey()], + pointsToNextItems: true, + ), 'perPage' => '3', ]); @@ -811,12 +890,12 @@ public function testSnakeCaseMetaAndCustomMetaKey(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => "4", + "id" => (string) $posts[3]->getKey(), ], pointsToNextItems: false), 'has_more' => true, 'per_page' => 3, 'to' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), ]; @@ -837,12 +916,12 @@ public function testDashCaseMeta(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => "4", + "id" => (string) $posts[3]->getKey(), ], pointsToNextItems: false), 'has-more' => true, 'per-page' => 3, 'to' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), ]; @@ -863,12 +942,12 @@ public function testMetaNotNested(): void $meta = [ 'from' => $this->encodeCursor([ - "id" => "4", + "id" => (string) $posts[3]->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => 3, 'to' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), ]; @@ -897,7 +976,7 @@ public function testItCanRemoveMeta(): void 'href' => 'http://localhost/api/v1/posts?' . Arr::query([ 'page' => [ 'after' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'limit' => '3', ], @@ -939,7 +1018,7 @@ public function testUrlsIncludeOtherQueryParameters(): void 'include' => 'author', 'page' => [ 'after' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'limit' => '3', ], @@ -968,20 +1047,20 @@ public function testWithTotal(): void { $this->paginator->withTotal(); + $posts = Post::factory()->count(4)->create(); + $meta = [ 'from' => $this->encodeCursor([ - "id" => "4", + "id" => (string) $posts[3]->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => 3, 'to' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'total' => 4, ]; - Post::factory()->count(4)->create(); - $page = $this->posts ->repository() ->queryAll() @@ -995,7 +1074,7 @@ public function testWithTotal(): void ->queryAll() ->paginate([ 'after' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'limit' => 3, ]); @@ -1010,24 +1089,24 @@ public function testWithTotal(): void /** * @return void */ - public function testWithTotalOnFirstPage() + public function testWithTotalOnFirstPage(): void { $this->paginator->withTotalOnFirstPage(); + $posts = Post::factory()->count(4)->create(); + $meta = [ 'from' => $this->encodeCursor([ - "id" => "4", + "id" => (string) $posts[3]->getKey(), ], pointsToNextItems: false), 'hasMore' => true, 'perPage' => 3, 'to' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'total' => 4, ]; - Post::factory()->count(4)->create(); - $page = $this->posts ->repository() ->queryAll() @@ -1041,7 +1120,7 @@ public function testWithTotalOnFirstPage() ->queryAll() ->paginate([ 'after' => $this->encodeCursor([ - "id" => "2", + "id" => (string) $posts[1]->getKey(), ], pointsToNextItems: true), 'limit' => 3, ]); @@ -1054,13 +1133,13 @@ public function testWithTotalOnFirstPage() /** * Assert that the pages match. * - * @param $expected - * @param $actual + * @param iterable $expected + * @param iterable $actual */ - private function assertPage($expected, $actual): void + private function assertPage(iterable $expected, iterable $actual): void { - $expected = (new Collection($expected))->modelKeys(); - $actual = (new Collection($actual))->modelKeys(); + $expected = Collection::make($expected)->modelKeys(); + $actual = Collection::make($actual)->modelKeys(); $this->assertSame(array_values($expected), array_values($actual)); } @@ -1071,7 +1150,7 @@ private function assertPage($expected, $actual): void private function withIdEncoding(): void { $this->paginator = CursorPagination::make( - $this->encodedId = new EncodedId() + $this->encodedId = new EncodedId(), ); }