184 lines
6.5 KiB
PHP
184 lines
6.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Post;
|
|
|
|
use App\Post\Post;
|
|
use App\Post\PostServiceInterface;
|
|
use App\Post\RssController;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use Tests\ControllerTestCase;
|
|
|
|
/**
|
|
* Tests unitaires pour RssController.
|
|
*
|
|
* Couvre feed() :
|
|
* - Content-Type application/rss+xml
|
|
* - Structure XML valide (balises channel obligatoires)
|
|
* - Articles inclus dans le flux (titre, lien, guid)
|
|
* - Flux vide : XML minimal valide
|
|
* - Appel à getRecentPosts() avec la constante FEED_LIMIT (20)
|
|
*/
|
|
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
|
|
final class RssControllerTest extends ControllerTestCase
|
|
{
|
|
/** @var PostServiceInterface&MockObject */
|
|
private PostServiceInterface $postService;
|
|
|
|
private RssController $controller;
|
|
|
|
private const APP_URL = 'https://example.com';
|
|
private const APP_NAME = 'Mon Blog';
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->postService = $this->createMock(PostServiceInterface::class);
|
|
|
|
$this->controller = new RssController(
|
|
$this->postService,
|
|
self::APP_URL,
|
|
self::APP_NAME,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* feed() doit retourner un Content-Type application/rss+xml.
|
|
*/
|
|
public function testFeedReturnsRssContentType(): void
|
|
{
|
|
$this->postService->method('getRecentPosts')->willReturn([]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
|
|
$this->assertStringContainsString('application/rss+xml', $res->getHeaderLine('Content-Type'));
|
|
}
|
|
|
|
/**
|
|
* feed() doit retourner un XML valide même si aucun article n'existe.
|
|
*/
|
|
public function testFeedReturnsValidXmlWhenNoPostsExist(): void
|
|
{
|
|
$this->postService->method('getRecentPosts')->willReturn([]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
$body = (string) $res->getBody();
|
|
|
|
$xml = simplexml_load_string($body);
|
|
$this->assertNotFalse($xml, 'Le corps de la réponse doit être du XML valide');
|
|
$this->assertSame('2.0', (string) $xml['version']);
|
|
}
|
|
|
|
/**
|
|
* feed() doit inclure les balises channel obligatoires (title, link, description).
|
|
*/
|
|
public function testFeedIncludesRequiredChannelElements(): void
|
|
{
|
|
$this->postService->method('getRecentPosts')->willReturn([]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
$xml = simplexml_load_string((string) $res->getBody());
|
|
|
|
$this->assertNotFalse($xml);
|
|
$channel = $xml->channel;
|
|
$this->assertSame(self::APP_NAME, (string) $channel->title);
|
|
$this->assertNotEmpty((string) $channel->link);
|
|
$this->assertNotEmpty((string) $channel->description);
|
|
}
|
|
|
|
/**
|
|
* feed() doit inclure un item par article avec title, link et guid.
|
|
*/
|
|
public function testFeedIncludesOneItemPerPost(): void
|
|
{
|
|
$post = new Post(1, 'Titre test', 'Contenu de test', 'titre-test', 1, 'alice');
|
|
$this->postService->method('getRecentPosts')->willReturn([$post]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
$xml = simplexml_load_string((string) $res->getBody());
|
|
|
|
$this->assertNotFalse($xml);
|
|
$items = $xml->channel->item;
|
|
$this->assertCount(1, $items);
|
|
$this->assertSame('Titre test', (string) $items[0]->title);
|
|
$this->assertStringContainsString('titre-test', (string) $items[0]->link);
|
|
$this->assertSame((string) $items[0]->link, (string) $items[0]->guid);
|
|
}
|
|
|
|
/**
|
|
* feed() doit tronquer le contenu à 300 caractères dans la description.
|
|
*/
|
|
public function testFeedTruncatesLongContentTo300Chars(): void
|
|
{
|
|
$longContent = str_repeat('a', 400);
|
|
$post = new Post(1, 'Titre', $longContent, 'titre', 1);
|
|
$this->postService->method('getRecentPosts')->willReturn([$post]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
$xml = simplexml_load_string((string) $res->getBody());
|
|
|
|
$this->assertNotFalse($xml);
|
|
$description = (string) $xml->channel->item[0]->description;
|
|
// 300 caractères + '…' = 301 octets UTF-8 pour le contenu visible
|
|
$this->assertLessThanOrEqual(302, mb_strlen($description));
|
|
$this->assertStringEndsWith('…', $description);
|
|
}
|
|
|
|
/**
|
|
* feed() doit appeler getRecentPosts() avec la limite de 20 articles.
|
|
*/
|
|
public function testFeedRequestsTwentyRecentPosts(): void
|
|
{
|
|
$this->postService->expects($this->once())
|
|
->method('getRecentPosts')
|
|
->with(20)
|
|
->willReturn([]);
|
|
|
|
$this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
}
|
|
|
|
/**
|
|
* feed() doit inclure la balise author si l'article a un auteur.
|
|
*/
|
|
public function testFeedIncludesAuthorWhenPresent(): void
|
|
{
|
|
$post = new Post(1, 'Titre', 'Contenu', 'titre', 1, 'alice');
|
|
$this->postService->method('getRecentPosts')->willReturn([$post]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
$xml = simplexml_load_string((string) $res->getBody());
|
|
|
|
$this->assertNotFalse($xml);
|
|
$this->assertSame('alice', (string) $xml->channel->item[0]->author);
|
|
}
|
|
|
|
/**
|
|
* feed() ne doit pas inclure la balise author si l'auteur est null.
|
|
*/
|
|
public function testFeedOmitsAuthorWhenNull(): void
|
|
{
|
|
$post = new Post(1, 'Titre', 'Contenu', 'titre', null, null);
|
|
$this->postService->method('getRecentPosts')->willReturn([$post]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
$xml = simplexml_load_string((string) $res->getBody());
|
|
|
|
$this->assertNotFalse($xml);
|
|
$this->assertCount(0, $xml->channel->item[0]->author);
|
|
}
|
|
|
|
/**
|
|
* feed() doit construire les URLs des articles en utilisant APP_URL.
|
|
*/
|
|
public function testFeedBuildsPostUrlsWithAppUrl(): void
|
|
{
|
|
$post = new Post(1, 'Titre', 'Contenu', 'mon-slug', 1);
|
|
$this->postService->method('getRecentPosts')->willReturn([$post]);
|
|
|
|
$res = $this->controller->feed($this->makeGet('/rss.xml'), $this->makeResponse());
|
|
$xml = simplexml_load_string((string) $res->getBody());
|
|
|
|
$this->assertNotFalse($xml);
|
|
$this->assertStringStartsWith(self::APP_URL, (string) $xml->channel->item[0]->link);
|
|
}
|
|
}
|