2026-08-01 16:05:45 +02:00
|
|
|
<?php
|
|
|
|
|
|
2026-08-14 00:07:08 +02:00
|
|
|
namespace Tests\Unit\Actions;
|
2026-08-01 16:05:45 +02:00
|
|
|
|
|
|
|
|
use App\Models\Feed;
|
|
|
|
|
use App\Models\Language;
|
|
|
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
|
use Mockery;
|
|
|
|
|
use Tests\TestCase;
|
2026-08-14 00:07:08 +02:00
|
|
|
use Tests\Traits\CreatesFetchActions;
|
2026-08-01 16:05:45 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Belga discovery runs through the website path against a JSON API rather than
|
|
|
|
|
* a rendered homepage. This lives in its own file because ArticleFetcherTest
|
|
|
|
|
* registers a catch-all Http::fake in setUp() that a per-test fake cannot
|
|
|
|
|
* override.
|
|
|
|
|
*/
|
2026-08-14 00:07:08 +02:00
|
|
|
class FetchWebsiteArticlesBelgaTest extends TestCase
|
2026-08-01 16:05:45 +02:00
|
|
|
{
|
2026-08-14 00:07:08 +02:00
|
|
|
use CreatesFetchActions, RefreshDatabase;
|
2026-08-01 16:05:45 +02:00
|
|
|
|
|
|
|
|
private function apiResponse(): string
|
|
|
|
|
{
|
2026-08-02 00:08:14 +02:00
|
|
|
$contents = file_get_contents(__DIR__.'/../../Fixtures/belga-pressreleases.json');
|
|
|
|
|
|
|
|
|
|
$this->assertNotFalse($contents, 'Belga fixture could not be read.');
|
|
|
|
|
|
|
|
|
|
return $contents;
|
2026-08-01 16:05:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function belgaFeed(): Feed
|
|
|
|
|
{
|
|
|
|
|
$language = Language::factory()->create(['short_code' => 'en']);
|
|
|
|
|
|
|
|
|
|
return Feed::factory()->create([
|
|
|
|
|
'type' => 'website',
|
|
|
|
|
'provider' => 'belga',
|
|
|
|
|
'language_id' => $language->id,
|
|
|
|
|
'url' => config('feed.providers.belga.languages.en.url'),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_creates_articles_from_belga_api_response(): void
|
|
|
|
|
{
|
|
|
|
|
Http::fake(['*' => Http::response($this->apiResponse(), 200)]);
|
|
|
|
|
|
2026-08-14 00:07:08 +02:00
|
|
|
$result = $this->createFeedFetcher()->execute($this->belgaFeed());
|
2026-08-01 16:05:45 +02:00
|
|
|
|
|
|
|
|
$this->assertCount(6, $result);
|
|
|
|
|
$this->assertDatabaseHas('articles', [
|
|
|
|
|
'url' => 'https://www.belganewsagency.eu/press-releases/35285/',
|
|
|
|
|
]);
|
|
|
|
|
$this->assertDatabaseHas('articles', [
|
|
|
|
|
'url' => 'https://www.belganewsagency.eu/press-releases/35269/',
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_associates_created_articles_with_the_feed(): void
|
|
|
|
|
{
|
|
|
|
|
Http::fake(['*' => Http::response($this->apiResponse(), 200)]);
|
|
|
|
|
|
|
|
|
|
$feed = $this->belgaFeed();
|
|
|
|
|
|
2026-08-14 00:07:08 +02:00
|
|
|
$this->createFeedFetcher()->execute($feed);
|
2026-08-01 16:05:45 +02:00
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('articles', [
|
|
|
|
|
'url' => 'https://www.belganewsagency.eu/press-releases/35285/',
|
|
|
|
|
'feed_id' => $feed->id,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_returns_empty_collection_when_api_returns_no_articles(): void
|
|
|
|
|
{
|
|
|
|
|
// A well-formed envelope with nothing in it is legitimate (quiet day),
|
|
|
|
|
// and must be a no-op rather than an error.
|
|
|
|
|
Http::fake(['*' => Http::response('{"data":[],"_meta":{"total":0}}', 200)]);
|
|
|
|
|
|
2026-08-14 00:07:08 +02:00
|
|
|
$result = $this->createFeedFetcher()->execute($this->belgaFeed());
|
2026-08-01 16:05:45 +02:00
|
|
|
|
|
|
|
|
$this->assertEmpty($result);
|
|
|
|
|
$this->assertDatabaseCount('articles', 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_returns_empty_collection_when_api_returns_an_error_page(): void
|
|
|
|
|
{
|
|
|
|
|
// The failure mode that caused #115: a 404 HTML body reaching the parser.
|
|
|
|
|
Http::fake(['*' => Http::response('<html><body>404 Not Found</body></html>', 200)]);
|
|
|
|
|
|
2026-08-14 00:07:08 +02:00
|
|
|
$result = $this->createFeedFetcher()->execute($this->belgaFeed());
|
2026-08-01 16:05:45 +02:00
|
|
|
|
|
|
|
|
$this->assertEmpty($result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function tearDown(): void
|
|
|
|
|
{
|
|
|
|
|
Mockery::close();
|
|
|
|
|
parent::tearDown();
|
|
|
|
|
}
|
|
|
|
|
}
|