{$link}"; } return "{$items}"; } private function feed(): Feed { return Feed::factory()->create([ 'type' => 'rss', 'url' => 'https://example.com/feed.rss', ]); } public function test_it_saves_an_article_per_rss_item(): void { $feed = $this->feed(); Http::fake([$feed->url => Http::response( $this->rss('https://example.com/one', 'https://example.com/two'), 200 )]); $result = $this->action()->execute($feed); $this->assertCount(2, $result); $this->assertDatabaseHas('articles', ['url' => 'https://example.com/one', 'feed_id' => $feed->id]); $this->assertDatabaseHas('articles', ['url' => 'https://example.com/two', 'feed_id' => $feed->id]); } public function test_it_skips_items_with_an_empty_link(): void { $feed = $this->feed(); Http::fake([$feed->url => Http::response( $this->rss('https://example.com/one', ''), 200 )]); $result = $this->action()->execute($feed); $this->assertCount(1, $result); $this->assertSame(1, Article::count()); } public function test_it_returns_empty_for_malformed_xml(): void { $feed = $this->feed(); Http::fake([$feed->url => Http::response('assertEmpty($this->action()->execute($feed)); $this->assertSame(0, Article::count()); } public function test_it_returns_empty_when_the_feed_has_no_items(): void { $feed = $this->feed(); Http::fake([$feed->url => Http::response('', 200)]); $this->assertEmpty($this->action()->execute($feed)); } public function test_it_returns_empty_when_the_fetch_throws(): void { $feed = $this->feed(); Http::fake(fn () => throw new \RuntimeException('connection refused')); $this->assertEmpty($this->action()->execute($feed)); } public function test_it_does_not_duplicate_articles_across_runs(): void { $feed = $this->feed(); Http::fake([$feed->url => Http::response($this->rss('https://example.com/one'), 200)]); $this->action()->execute($feed); $this->action()->execute($feed); $this->assertSame(1, Article::count()); } }