Http::response([ 'posts' => [ $this->lemmyPost( id: 42, apId: 'https://lemmy.world/post/42', name: 'My Great Post', body: 'Some body text', published: '2026-04-25T10:00:00.000000', ), ], ], 200), ]); $posts = (new LemmyClient)->fetchPostsSince($this->lemmyInstance(), null); $this->assertCount(1, $posts); $this->assertInstanceOf(FediversePost::class, $posts->first()); $this->assertSame('42', $posts->first()->cursorId); $this->assertSame('https://lemmy.world/post/42', $posts->first()->selfUrl); $this->assertSame('My Great Post', $posts->first()->title); $this->assertSame('Some body text', $posts->first()->body); $this->assertSame('2026-04-25T10:00:00.000000', $posts->first()->publishedAt); } public function test_url_field_is_appended_to_body(): void { Http::fake([ '*' => Http::response([ 'posts' => [ $this->lemmyPost( id: 42, apId: 'https://lemmy.world/post/42', url: 'https://example-garden.blog/post-42', body: 'Some original text.', ), ], ], 200), ]); $post = (new LemmyClient)->fetchPostsSince($this->lemmyInstance(), null)->first(); $this->assertStringContainsString('Some original text.', $post->body); $this->assertStringContainsString('https://example-garden.blog/post-42', $post->body); } public function test_url_absent_leaves_body_clean(): void { Http::fake([ '*' => Http::response([ 'posts' => [ $this->lemmyPost( id: 7, apId: 'https://lemmy.world/post/7', body: 'Just a regular post.', ), ], ], 200), ]); $post = (new LemmyClient)->fetchPostsSince($this->lemmyInstance(), null)->first(); $this->assertSame('Just a regular post.', $post->body); } public function test_it_handles_posts_without_a_body_key(): void { Http::fake([ '*' => Http::response([ 'posts' => [ [ 'post' => [ 'id' => 99, 'ap_id' => 'https://lemmy.world/post/99', 'url' => null, 'name' => 'Link-only post', 'published' => '2026-04-25T10:00:00.000000', // 'body' key intentionally absent — real Lemmy API omits it for link-only posts ], ], ], ], 200), ]); $post = (new LemmyClient)->fetchPostsSince($this->lemmyInstance(), null)->first(); $this->assertNull($post->body); } public function test_it_hits_the_post_list_endpoint_of_the_instance(): void { Http::fake([ 'lemmy.world/api/v3/post/list*' => Http::response(['posts' => []], 200), ]); (new LemmyClient)->fetchPostsSince($this->lemmyInstance(), null); Http::assertSent(fn ($request) => str_starts_with($request->url(), 'https://lemmy.world/api/v3/post/list') && $request->method() === 'GET' ); } private function lemmyInstance(): Instance { return new Instance([ 'type' => InstanceType::Lemmy, 'url' => 'https://lemmy.world', ]); } /** * @return array */ private function lemmyPost( int $id, string $apId, ?string $url = null, string $body = '', string $name = 'A post title', string $published = '2026-04-25T10:00:00.000000', ): array { return [ 'post' => [ 'id' => $id, 'ap_id' => $apId, 'url' => $url, 'body' => $body, 'name' => $name, 'published' => $published, ], ]; } }