fedi-feed-router/backend/tests/Unit/Services/ArticleFetcherTest.php

236 lines
8.1 KiB
PHP
Raw Normal View History

2025-08-02 03:48:06 +02:00
<?php
namespace Tests\Unit\Services;
use App\Services\Article\ArticleFetcher;
use App\Models\Feed;
use App\Models\Article;
2025-08-06 21:49:13 +02:00
use App\Services\Http\HttpFetcher;
use App\Services\Factories\HomepageParserFactory;
use App\Services\Factories\ArticleParserFactory;
use App\Services\Log\LogSaver;
2025-08-02 03:48:06 +02:00
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
2025-08-06 21:49:13 +02:00
use Mockery;
2025-08-02 03:48:06 +02:00
class ArticleFetcherTest extends TestCase
{
use RefreshDatabase;
public function test_get_articles_from_feed_returns_collection(): void
{
$feed = Feed::factory()->create([
'type' => 'rss',
'url' => 'https://example.com/feed.rss'
]);
$result = ArticleFetcher::getArticlesFromFeed($feed);
$this->assertInstanceOf(\Illuminate\Support\Collection::class, $result);
}
public function test_get_articles_from_rss_feed_returns_empty_collection(): void
{
$feed = Feed::factory()->create([
'type' => 'rss',
'url' => 'https://example.com/feed.rss'
]);
$result = ArticleFetcher::getArticlesFromFeed($feed);
// RSS parsing is not implemented yet, should return empty collection
$this->assertEmpty($result);
}
public function test_get_articles_from_website_feed_handles_no_parser(): void
{
$feed = Feed::factory()->create([
'type' => 'website',
'url' => 'https://unsupported-site.com/'
]);
$result = ArticleFetcher::getArticlesFromFeed($feed);
// Should return empty collection when no parser is available
$this->assertInstanceOf(\Illuminate\Support\Collection::class, $result);
$this->assertEmpty($result);
}
public function test_get_articles_from_unsupported_feed_type(): void
{
$feed = Feed::factory()->create([
'type' => 'website', // Use valid type but with unsupported URL
'url' => 'https://unsupported-feed-type.com/feed'
]);
$result = ArticleFetcher::getArticlesFromFeed($feed);
$this->assertInstanceOf(\Illuminate\Support\Collection::class, $result);
$this->assertEmpty($result);
}
public function test_fetch_article_data_returns_array(): void
{
$article = Article::factory()->create([
'url' => 'https://example.com/article'
]);
$result = ArticleFetcher::fetchArticleData($article);
$this->assertIsArray($result);
// Will be empty array due to unsupported URL in test
$this->assertEmpty($result);
}
public function test_fetch_article_data_handles_invalid_url(): void
{
$article = Article::factory()->create([
'url' => 'invalid-url'
]);
$result = ArticleFetcher::fetchArticleData($article);
$this->assertIsArray($result);
$this->assertEmpty($result);
}
2025-08-06 21:49:13 +02:00
public function test_get_articles_from_feed_with_null_feed_type(): void
{
// Create feed with valid type first, then manually set to invalid value
$feed = Feed::factory()->create([
'type' => 'website',
'url' => 'https://example.com/feed'
]);
// Use reflection to set an invalid type that bypasses enum validation
$reflection = new \ReflectionClass($feed);
$property = $reflection->getProperty('attributes');
$property->setAccessible(true);
$attributes = $property->getValue($feed);
$attributes['type'] = 'invalid_type';
$property->setValue($feed, $attributes);
$result = ArticleFetcher::getArticlesFromFeed($feed);
$this->assertInstanceOf(\Illuminate\Support\Collection::class, $result);
$this->assertEmpty($result);
}
public function test_get_articles_from_website_feed_with_supported_parser(): void
{
$feed = Feed::factory()->create([
'type' => 'website',
'url' => 'https://www.vrt.be/vrtnws/nl/'
]);
// Test actual behavior - VRT parser should be available
$result = ArticleFetcher::getArticlesFromFeed($feed);
$this->assertInstanceOf(\Illuminate\Support\Collection::class, $result);
// Result might be empty due to HTTP call failure in test environment, but should not error
}
public function test_get_articles_from_website_feed_handles_invalid_url(): void
{
$feed = Feed::factory()->create([
'type' => 'website',
'url' => 'https://invalid-domain-that-does-not-exist-12345.com/'
]);
$result = ArticleFetcher::getArticlesFromFeed($feed);
$this->assertInstanceOf(\Illuminate\Support\Collection::class, $result);
$this->assertEmpty($result);
}
public function test_fetch_article_data_with_supported_parser(): void
{
$article = Article::factory()->create([
'url' => 'https://www.vrt.be/vrtnws/nl/test-article'
]);
// Test actual behavior - VRT parser should be available
$result = ArticleFetcher::fetchArticleData($article);
$this->assertIsArray($result);
// Result might be empty due to HTTP call failure in test environment, but should not error
}
public function test_fetch_article_data_handles_unsupported_domain(): void
{
$article = Article::factory()->create([
'url' => 'https://unsupported-domain.com/article'
]);
$result = ArticleFetcher::fetchArticleData($article);
$this->assertIsArray($result);
$this->assertEmpty($result);
}
public function test_save_article_creates_new_article_when_not_exists(): void
{
$feed = Feed::factory()->create();
$url = 'https://example.com/unique-article';
// Ensure article doesn't exist
$this->assertDatabaseMissing('articles', ['url' => $url]);
// Use reflection to access private method for testing
$reflection = new \ReflectionClass(ArticleFetcher::class);
$saveArticleMethod = $reflection->getMethod('saveArticle');
$saveArticleMethod->setAccessible(true);
$article = $saveArticleMethod->invoke(null, $url, $feed->id);
$this->assertInstanceOf(Article::class, $article);
$this->assertEquals($url, $article->url);
$this->assertEquals($feed->id, $article->feed_id);
$this->assertDatabaseHas('articles', ['url' => $url, 'feed_id' => $feed->id]);
}
public function test_save_article_returns_existing_article_when_exists(): void
{
$feed = Feed::factory()->create();
$existingArticle = Article::factory()->create([
'url' => 'https://example.com/existing-article',
'feed_id' => $feed->id
]);
// Use reflection to access private method for testing
$reflection = new \ReflectionClass(ArticleFetcher::class);
$saveArticleMethod = $reflection->getMethod('saveArticle');
$saveArticleMethod->setAccessible(true);
$article = $saveArticleMethod->invoke(null, $existingArticle->url, $feed->id);
$this->assertEquals($existingArticle->id, $article->id);
$this->assertEquals($existingArticle->url, $article->url);
// Ensure no duplicate was created
$this->assertEquals(1, Article::where('url', $existingArticle->url)->count());
}
public function test_save_article_without_feed_id(): void
{
$url = 'https://example.com/article-without-feed';
// Use reflection to access private method for testing
$reflection = new \ReflectionClass(ArticleFetcher::class);
$saveArticleMethod = $reflection->getMethod('saveArticle');
$saveArticleMethod->setAccessible(true);
$article = $saveArticleMethod->invoke(null, $url, null);
$this->assertInstanceOf(Article::class, $article);
$this->assertEquals($url, $article->url);
$this->assertNull($article->feed_id);
$this->assertDatabaseHas('articles', ['url' => $url, 'feed_id' => null]);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
2025-08-02 03:48:06 +02:00
}