now = new DateTimeImmutable( '2026-05-06T12:00:00', new DateTimeZone('UTC'), ); $this->postRepo = new FakePostRepository; $this->useCase = new ClearFeaturedPost($this->postRepo); } private function seedFeaturedPost(int $slot): Post { $post = $this->postRepo->create(new CreatePostDto( userId: 1, title: 'A', body: 'B', createdAt: $this->now, )); return $this->postRepo->update(new Post( id: $post->getId(), userId: $post->getUserId(), title: $post->getTitle(), body: $post->getBody(), createdAt: $post->getCreatedAt(), featureSlot: $slot, )); } public function test_non_admin_throws_forbidden(): void { $post = $this->seedFeaturedPost(1); $this->expectException(ForbiddenException::class); $this->useCase->execute(new ClearFeaturedPostRequest( postId: $post->getId(), requesterIsAdmin: false, )); } public function test_zero_post_id_throws_bad_request(): void { $this->expectException(BadRequestException::class); $this->useCase->execute(new ClearFeaturedPostRequest( postId: 0, requesterIsAdmin: true, )); } public function test_unknown_post_is_no_op(): void { $this->useCase->execute(new ClearFeaturedPostRequest( postId: 999, requesterIsAdmin: true, )); $this->assertNull($this->postRepo->find(999)); } public function test_admin_clears_feature_slot(): void { $post = $this->seedFeaturedPost(2); $this->useCase->execute(new ClearFeaturedPostRequest( postId: $post->getId(), requesterIsAdmin: true, )); $this->assertNull( $this->postRepo->find($post->getId())->getFeatureSlot(), ); } }