fedi-feed-router/tests/Unit/Services/ArticleFetcherRssTest.php
myrmidex d2919758f5
All checks were successful
CI / ci (push) Successful in 5m52s
CI / ci (pull_request) Successful in 5m46s
Build and Push Docker Image / build (push) Successful in 4m6s
Fix Pint 1.29.0 lint issues and update CI workflow
2026-03-18 20:01:25 +01:00

211 lines
6.8 KiB
PHP

<?php
namespace Tests\Unit\Services;
use App\Models\Article;
use App\Models\Feed;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Mockery;
use Tests\TestCase;
use Tests\Traits\CreatesArticleFetcher;
class ArticleFetcherRssTest extends TestCase
{
use CreatesArticleFetcher, RefreshDatabase;
private string $sampleRss;
protected function setUp(): void
{
parent::setUp();
$this->sampleRss = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>The Guardian - International</title>
<link>https://www.theguardian.com/international</link>
<item>
<title>First Article Title</title>
<link>https://www.theguardian.com/world/2026/mar/08/first-article</link>
<description>First article description</description>
<pubDate>Sun, 08 Mar 2026 12:00:00 GMT</pubDate>
</item>
<item>
<title>Second Article Title</title>
<link>https://www.theguardian.com/world/2026/mar/08/second-article</link>
<description>Second article description</description>
<pubDate>Sun, 08 Mar 2026 11:00:00 GMT</pubDate>
</item>
</channel>
</rss>
XML;
}
public function test_get_articles_from_rss_feed_returns_collection(): void
{
Http::fake(['*' => Http::response($this->sampleRss, 200)]);
$feed = Feed::factory()->create([
'type' => 'rss',
'provider' => 'guardian',
'url' => 'https://www.theguardian.com/international/rss',
]);
$fetcher = $this->createArticleFetcher();
$result = $fetcher->getArticlesFromFeed($feed);
$this->assertInstanceOf(Collection::class, $result);
}
public function test_get_articles_from_rss_feed_creates_articles(): void
{
Http::fake(['*' => Http::response($this->sampleRss, 200)]);
$feed = Feed::factory()->create([
'type' => 'rss',
'provider' => 'guardian',
'url' => 'https://www.theguardian.com/international/rss',
]);
$fetcher = $this->createArticleFetcher();
$result = $fetcher->getArticlesFromFeed($feed);
$this->assertCount(2, $result);
$this->assertDatabaseHas('articles', [
'url' => 'https://www.theguardian.com/world/2026/mar/08/first-article',
'feed_id' => $feed->id,
]);
$this->assertDatabaseHas('articles', [
'url' => 'https://www.theguardian.com/world/2026/mar/08/second-article',
'feed_id' => $feed->id,
]);
}
public function test_get_articles_from_rss_feed_does_not_duplicate_existing(): void
{
Http::fake(['*' => Http::response($this->sampleRss, 200)]);
$feed = Feed::factory()->create([
'type' => 'rss',
'provider' => 'guardian',
'url' => 'https://www.theguardian.com/international/rss',
]);
Article::factory()->create([
'url' => 'https://www.theguardian.com/world/2026/mar/08/first-article',
'feed_id' => $feed->id,
]);
$fetcher = $this->createArticleFetcher();
$result = $fetcher->getArticlesFromFeed($feed);
$this->assertCount(2, $result);
$this->assertEquals(1, Article::where('url', 'https://www.theguardian.com/world/2026/mar/08/first-article')->count());
}
public function test_get_articles_from_rss_feed_handles_invalid_xml(): void
{
Http::fake(['*' => Http::response('this is not xml', 200)]);
$feed = Feed::factory()->create([
'type' => 'rss',
'provider' => 'guardian',
'url' => 'https://www.theguardian.com/international/rss',
]);
$fetcher = $this->createArticleFetcher();
$result = $fetcher->getArticlesFromFeed($feed);
$this->assertInstanceOf(Collection::class, $result);
$this->assertEmpty($result);
}
public function test_get_articles_from_rss_feed_handles_empty_channel(): void
{
Http::fake([
'*' => Http::response('<?xml version="1.0"?><rss><channel><title>Empty</title></channel></rss>', 200),
]);
$feed = Feed::factory()->create([
'type' => 'rss',
'provider' => 'guardian',
'url' => 'https://www.theguardian.com/international/rss',
]);
$fetcher = $this->createArticleFetcher();
$result = $fetcher->getArticlesFromFeed($feed);
$this->assertEmpty($result);
}
public function test_get_articles_from_rss_feed_handles_http_failure(): void
{
Http::fake(['*' => Http::response('Server Error', 500)]);
$feed = Feed::factory()->create([
'type' => 'rss',
'provider' => 'guardian',
'url' => 'https://www.theguardian.com/international/rss',
]);
$fetcher = $this->createArticleFetcher();
$result = $fetcher->getArticlesFromFeed($feed);
$this->assertEmpty($result);
}
public function test_get_articles_from_belga_rss_feed_creates_articles(): void
{
$belgaRss = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Belga News Agency</title>
<link>https://www.belganewsagency.eu</link>
<item>
<title>Belgium announces new climate plan</title>
<link>https://www.belganewsagency.eu/belgium-announces-new-climate-plan</link>
<description>Belgium has unveiled a comprehensive climate strategy.</description>
<pubDate>Sun, 08 Mar 2026 10:00:00 GMT</pubDate>
</item>
<item>
<title>EU summit concludes in Brussels</title>
<link>https://www.belganewsagency.eu/eu-summit-concludes-in-brussels</link>
<description>European leaders reached agreement on key issues.</description>
<pubDate>Sun, 08 Mar 2026 09:00:00 GMT</pubDate>
</item>
</channel>
</rss>
XML;
Http::fake(['*' => Http::response($belgaRss, 200)]);
$feed = Feed::factory()->create([
'type' => 'rss',
'provider' => 'belga',
'url' => 'https://www.belganewsagency.eu/feed',
]);
$fetcher = $this->createArticleFetcher();
$result = $fetcher->getArticlesFromFeed($feed);
$this->assertCount(2, $result);
$this->assertDatabaseHas('articles', [
'url' => 'https://www.belganewsagency.eu/belgium-announces-new-climate-plan',
'feed_id' => $feed->id,
]);
$this->assertDatabaseHas('articles', [
'url' => 'https://www.belganewsagency.eu/eu-summit-concludes-in-brussels',
'feed_id' => $feed->id,
]);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
}