release/v1.3.5 #122
7 changed files with 118 additions and 12 deletions
|
|
@ -16,8 +16,8 @@ public static function extractArticleUrls(string $json): array
|
||||||
|
|
||||||
return collect($decoded['data'])
|
return collect($decoded['data'])
|
||||||
->pluck('id')
|
->pluck('id')
|
||||||
->filter(fn($id) => is_int($id) || (is_string($id) && ctype_digit($id)))
|
->filter(fn ($id) => is_int($id) || (is_string($id) && ctype_digit($id)))
|
||||||
->map(fn($id) => sprintf(self::ARTICLE_URL_TEMPLATE, $id))
|
->map(fn ($id) => sprintf(self::ARTICLE_URL_TEMPLATE, $id))
|
||||||
->unique()
|
->unique()
|
||||||
->values()
|
->values()
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,11 @@
|
||||||
|
|
||||||
class BelgaHomepageParserAdapter implements HomepageParserInterface
|
class BelgaHomepageParserAdapter implements HomepageParserInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Must stay in sync with config('feed.providers.belga.languages.en.url').
|
||||||
|
* Nothing calls getHomepageUrl() at runtime — the fetcher uses $feed->url,
|
||||||
|
* seeded from that config value — so drift here would go unnoticed.
|
||||||
|
*/
|
||||||
private const API_URL = 'https://capi.belga.press/belgapress/api/public/pressreleases?offset=1&count=6&search=&start=&end=&newsroomId=70&language=EN';
|
private const API_URL = 'https://capi.belga.press/belgapress/api/public/pressreleases?offset=1&count=6&search=&start=&end=&newsroomId=70&language=EN';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
@ -24,7 +29,7 @@ public function extractArticleUrls(string $html): array
|
||||||
|
|
||||||
public function getHomepageUrl(): string
|
public function getHomepageUrl(): string
|
||||||
{
|
{
|
||||||
return str_replace('language=EN', 'language=' . strtoupper($this->language), self::API_URL);
|
return str_replace('language=EN', 'language='.strtoupper($this->language), self::API_URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSourceName(): string
|
public function getSourceName(): string
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use App\Services\Parsers\BelgaArticlePageParser;
|
use App\Services\Parsers\BelgaArticlePageParser;
|
||||||
use App\Services\Parsers\BelgaArticleParser;
|
use App\Services\Parsers\BelgaArticleParser;
|
||||||
|
use App\Services\Parsers\BelgaHomepageParserAdapter;
|
||||||
use App\Services\Parsers\GuardianArticlePageParser;
|
use App\Services\Parsers\GuardianArticlePageParser;
|
||||||
use App\Services\Parsers\GuardianArticleParser;
|
use App\Services\Parsers\GuardianArticleParser;
|
||||||
use App\Services\Parsers\VrtArticlePageParser;
|
use App\Services\Parsers\VrtArticlePageParser;
|
||||||
|
|
@ -41,12 +42,13 @@
|
||||||
'code' => 'belga',
|
'code' => 'belga',
|
||||||
'name' => 'Belga News Agency',
|
'name' => 'Belga News Agency',
|
||||||
'description' => 'Belgian national news agency',
|
'description' => 'Belgian national news agency',
|
||||||
'type' => 'rss',
|
'type' => 'website',
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
'languages' => [
|
'languages' => [
|
||||||
'en' => ['url' => 'https://www.belganewsagency.eu/feed'],
|
'en' => ['url' => 'https://capi.belga.press/belgapress/api/public/pressreleases?offset=1&count=6&search=&start=&end=&newsroomId=70&language=EN'],
|
||||||
],
|
],
|
||||||
'parsers' => [
|
'parsers' => [
|
||||||
|
'homepage' => BelgaHomepageParserAdapter::class,
|
||||||
'article' => BelgaArticleParser::class,
|
'article' => BelgaArticleParser::class,
|
||||||
'article_page' => BelgaArticlePageParser::class,
|
'article_page' => BelgaArticlePageParser::class,
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -92,22 +92,24 @@ public function test_store_creates_belga_feed_successfully(): void
|
||||||
|
|
||||||
$response = $this->postJson('/api/v1/feeds', $feedData);
|
$response = $this->postJson('/api/v1/feeds', $feedData);
|
||||||
|
|
||||||
|
$expectedUrl = config('feed.providers.belga.languages.en.url');
|
||||||
|
|
||||||
$response->assertStatus(201)
|
$response->assertStatus(201)
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Feed created successfully!',
|
'message' => 'Feed created successfully!',
|
||||||
'data' => [
|
'data' => [
|
||||||
'name' => 'Belga Test Feed',
|
'name' => 'Belga Test Feed',
|
||||||
'url' => 'https://www.belganewsagency.eu/feed',
|
'url' => $expectedUrl,
|
||||||
'type' => 'rss',
|
'type' => 'website',
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertDatabaseHas('feeds', [
|
$this->assertDatabaseHas('feeds', [
|
||||||
'name' => 'Belga Test Feed',
|
'name' => 'Belga Test Feed',
|
||||||
'url' => 'https://www.belganewsagency.eu/feed',
|
'url' => $expectedUrl,
|
||||||
'type' => 'rss',
|
'type' => 'website',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,9 @@ public function test_creates_belga_feed_with_correct_url(): void
|
||||||
|
|
||||||
$feed = $this->action->execute('Belga News', 'belga', $language->id);
|
$feed = $this->action->execute('Belga News', 'belga', $language->id);
|
||||||
|
|
||||||
$this->assertEquals('https://www.belganewsagency.eu/feed', $feed->url);
|
$this->assertStringStartsWith('https://capi.belga.press/belgapress/api/public/pressreleases?', $feed->url);
|
||||||
$this->assertEquals('rss', $feed->type);
|
$this->assertStringContainsString('newsroomId=70', $feed->url);
|
||||||
|
$this->assertEquals('website', $feed->type);
|
||||||
$this->assertEquals('belga', $feed->provider);
|
$this->assertEquals('belga', $feed->provider);
|
||||||
$this->assertNull($feed->description);
|
$this->assertNull($feed->description);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
96
tests/Unit/Services/ArticleFetcherBelgaTest.php
Normal file
96
tests/Unit/Services/ArticleFetcherBelgaTest.php
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\Services;
|
||||||
|
|
||||||
|
use App\Models\Feed;
|
||||||
|
use App\Models\Language;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Mockery;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use Tests\Traits\CreatesArticleFetcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
class ArticleFetcherBelgaTest extends TestCase
|
||||||
|
{
|
||||||
|
use CreatesArticleFetcher, RefreshDatabase;
|
||||||
|
|
||||||
|
private function apiResponse(): string
|
||||||
|
{
|
||||||
|
return file_get_contents(__DIR__.'/../../Fixtures/belga-pressreleases.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]);
|
||||||
|
|
||||||
|
$result = $this->createArticleFetcher()->getArticlesFromFeed($this->belgaFeed());
|
||||||
|
|
||||||
|
$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();
|
||||||
|
|
||||||
|
$this->createArticleFetcher()->getArticlesFromFeed($feed);
|
||||||
|
|
||||||
|
$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)]);
|
||||||
|
|
||||||
|
$result = $this->createArticleFetcher()->getArticlesFromFeed($this->belgaFeed());
|
||||||
|
|
||||||
|
$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)]);
|
||||||
|
|
||||||
|
$result = $this->createArticleFetcher()->getArticlesFromFeed($this->belgaFeed());
|
||||||
|
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ class BelgaHomepageParserTest extends TestCase
|
||||||
{
|
{
|
||||||
private function fixture(): string
|
private function fixture(): string
|
||||||
{
|
{
|
||||||
return file_get_contents(__DIR__ . '/../../../Fixtures/belga-pressreleases.json');
|
return file_get_contents(__DIR__.'/../../../Fixtures/belga-pressreleases.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_extracts_article_urls_from_api_json(): void
|
public function test_extracts_article_urls_from_api_json(): void
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue