now = new DateTimeImmutable( '2026-05-06T12:00:00', new DateTimeZone('UTC') ); $this->postRepo = new FakePostRepository; $this->useCase = new ListRecentPosts($this->postRepo); } private function seedPost(int $userId, string $title, string $offset): void { $this->postRepo->create(new CreatePostDto( userId: $userId, title: $title, body: 'body', createdAt: $this->now->modify($offset), )); } public function test_zero_limit_throws_bad_request(): void { $this->expectException(BadRequestException::class); $this->useCase->execute(new ListRecentPostsRequest(limit: 0)); } public function test_negative_limit_throws_bad_request(): void { $this->expectException(BadRequestException::class); $this->useCase->execute(new ListRecentPostsRequest(limit: -1)); } public function test_empty_repo_returns_empty_array(): void { $posts = $this->useCase->execute( new ListRecentPostsRequest(limit: 10) ); $this->assertSame([], $posts); } public function test_returns_posts_ordered_newest_first(): void { $this->seedPost(1, 'oldest', '-2 days'); $this->seedPost(2, 'middle', '-1 day'); $this->seedPost(3, 'newest', '-1 hour'); $posts = $this->useCase->execute( new ListRecentPostsRequest(limit: 10) ); $this->assertCount(3, $posts); $this->assertSame('newest', $posts[0]->getTitle()); $this->assertSame('middle', $posts[1]->getTitle()); $this->assertSame('oldest', $posts[2]->getTitle()); } public function test_respects_limit(): void { $this->seedPost(1, 'first', '-3 days'); $this->seedPost(2, 'second', '-2 days'); $this->seedPost(3, 'third', '-1 day'); $this->seedPost(4, 'fourth', '-1 hour'); $posts = $this->useCase->execute( new ListRecentPostsRequest(limit: 2) ); $this->assertCount(2, $posts); $this->assertSame('fourth', $posts[0]->getTitle()); $this->assertSame('third', $posts[1]->getTitle()); } }