Files
netslim-blog/tests/Site/SiteControllerTest.php
2026-03-20 22:16:20 +01:00

177 lines
7.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Site;
use App\Site\UI\Http\SiteController;
use Netig\Netslim\AuditLog\Contracts\AuditEntryView;
use Netig\Netslim\AuditLog\Contracts\AuditLoggerInterface;
use Netig\Netslim\AuditLog\Contracts\AuditLogReaderInterface;
use Netig\Netslim\Identity\Application\AuthorizationServiceInterface;
use Netig\Netslim\Identity\Domain\Policy\Permission;
use Netig\Netslim\Kernel\Http\Application\Flash\FlashServiceInterface;
use Netig\Netslim\Kernel\Http\Application\Session\SessionManagerInterface;
use Netig\Netslim\Notifications\Application\NotificationServiceInterface;
use Netig\Netslim\Notifications\Contracts\NotificationDispatchView;
use Netig\Netslim\Settings\Application\SettingsServiceInterface;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\ControllerTestBase;
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
final class SiteControllerTest extends ControllerTestBase
{
private \Slim\Views\Twig $view;
private MockObject&SettingsServiceInterface $settings;
private AuditLoggerInterface&MockObject $auditLogger;
private AuditLogReaderInterface&MockObject $auditLogReader;
private MockObject&NotificationServiceInterface $notifications;
private AuthorizationServiceInterface&MockObject $authorization;
private MockObject&SessionManagerInterface $sessionManager;
private FlashServiceInterface&MockObject $flash;
private SiteController $controller;
protected function setUp(): void
{
$this->view = $this->makeTwigMock();
$this->settings = $this->createMock(SettingsServiceInterface::class);
$this->auditLogger = $this->createMock(AuditLoggerInterface::class);
$this->auditLogReader = $this->createMock(AuditLogReaderInterface::class);
$this->notifications = $this->createMock(NotificationServiceInterface::class);
$this->authorization = $this->createMock(AuthorizationServiceInterface::class);
$this->sessionManager = $this->createMock(SessionManagerInterface::class);
$this->flash = $this->createMock(FlashServiceInterface::class);
$this->authorization->method('canRole')->willReturnMap([
['admin', Permission::SETTINGS_MANAGE, true],
['admin', Permission::AUDIT_LOG_VIEW, true],
['admin', Permission::NOTIFICATIONS_SEND, true],
['admin', Permission::CONTENT_MANAGE, true],
['user', Permission::SETTINGS_MANAGE, false],
['user', Permission::AUDIT_LOG_VIEW, false],
['user', Permission::NOTIFICATIONS_SEND, false],
['user', Permission::CONTENT_MANAGE, false],
]);
$this->sessionManager->method('isAdmin')->willReturn(true);
$this->sessionManager->method('isEditor')->willReturn(false);
$this->sessionManager->method('getUserId')->willReturn(1);
$this->settings->method('getString')->willReturnMap([
['site.title', 'Netslim Blog', 'Netslim Blog'],
['site.tagline', '', 'Baseline'],
['site.meta_description', '', 'Description'],
['blog.home_intro', '', 'Accueil'],
['notifications.demo_recipient', '', 'demo@example.test'],
]);
$this->settings->method('getInt')->willReturnMap([
['blog.public_posts_per_page', 6, 6],
['blog.admin_posts_per_page', 12, 12],
]);
$this->auditLogReader->method('listRecent')->willReturn([
new AuditEntryView(1, 'settings.updated', 'settings', 'blog', 1, [], '2026-03-20T12:00:00+00:00'),
]);
$this->notifications->method('recent')->willReturn([
new NotificationDispatchView(1, 'demo@example.test', 'Sujet', '@Site/emails/demo-notification.twig', 'sent', 'site.demo', null, '2026-03-20T12:00:00+00:00', '2026-03-20T12:00:01+00:00'),
]);
$this->controller = new SiteController(
$this->view,
$this->settings,
$this->auditLogger,
$this->auditLogReader,
$this->notifications,
$this->authorization,
$this->sessionManager,
$this->flash,
);
}
public function testSettingsPageRedirectsWhenPermissionIsMissing(): void
{
$sessionManager = $this->createMock(SessionManagerInterface::class);
$sessionManager->method('isAdmin')->willReturn(false);
$sessionManager->method('isEditor')->willReturn(false);
$sessionManager->method('getUserId')->willReturn(1);
$authorization = $this->createMock(AuthorizationServiceInterface::class);
$authorization->expects($this->once())
->method('canRole')
->with('user', Permission::SETTINGS_MANAGE)
->willReturn(false);
$controller = new SiteController(
$this->view,
$this->settings,
$this->auditLogger,
$this->auditLogReader,
$this->notifications,
$authorization,
$sessionManager,
$this->flash,
);
$this->flash->expects($this->once())->method('set')->with('site_error', $this->stringContains('réglages'));
$response = $controller->settings($this->makeGet('/admin/settings'), $this->makeResponse());
$this->assertRedirectTo($response, '/admin');
}
public function testSaveSettingsPersistsValuesAndAuditsChange(): void
{
$this->settings->expects($this->exactly(7))->method('set');
$this->auditLogger->expects($this->once())->method('record')->with(
'settings.updated',
'settings',
'blog',
1,
$this->callback(fn (array $context): bool => isset($context['keys']) && count($context['keys']) === 7),
);
$this->flash->expects($this->once())->method('set')->with('site_success', $this->stringContains('réglages'));
$response = $this->controller->saveSettings($this->makePost('/admin/settings', [
'site_title' => 'Mon blog',
'site_tagline' => 'Baseline',
'site_meta_description' => 'Description',
'home_intro' => 'Accueil',
'public_posts_per_page' => '8',
'admin_posts_per_page' => '16',
'demo_recipient' => 'demo@example.test',
]), $this->makeResponse());
$this->assertRedirectTo($response, '/admin/settings');
}
public function testSendNotificationUsesNotificationServiceAndWritesAuditEntry(): void
{
$this->notifications->expects($this->once())->method('sendTemplate')->with(
'demo@example.test',
'Sujet',
'@Site/emails/demo-notification.twig',
$this->callback(static fn (mixed $context): bool => is_array($context)),
'site.demo-notification',
);
$this->auditLogger->expects($this->once())->method('record')->with(
'notification.sent',
'notification',
'demo@example.test',
1,
$this->callback(fn (array $context): bool => ($context['subject'] ?? null) === 'Sujet'),
);
$response = $this->controller->sendNotification($this->makePost('/admin/notifications/send', [
'recipient' => 'demo@example.test',
'subject' => 'Sujet',
]), $this->makeResponse());
$this->assertRedirectTo($response, '/admin/notifications');
}
}