forked from fa/breadcrumb-the-shire
refactor(audit): extract audit domain into self-contained module
Move the entire audit subsystem (system audit, API audit, import audit, user lifecycle audit, frontend telemetry) from core into modules/audit/. Core decoupling via interface-based injection: - AuditRecorderInterface replaces SystemAuditService in 10+ core services - UserLifecycleAuditInterface / ImportAuditInterface for specialized flows - NullAuditRecorder fallback when audit module is disabled - ApiBootstrap/ApiResponse use null-safe callable resolvers Module structure (modules/audit/): - Manifest with routes, permissions, scheduler jobs, authorization policy - 9 services, 8 repositories, 6 domain enums, 4 job handlers - 33 page files, 4 JS files, 8 test files, migration scripts, i18n Core cleanup: - OperationsAuthorizationPolicy, UiCapabilityMap, PermissionService surgically cleaned of audit-specific constants - Sidebar template cleared of hardcoded audit navigation - AuditModuleIsolationContractTest ensures no future core→module coupling All quality gates pass: 1346 tests (19,276 assertions), PHPStan level 5 clean, architecture contracts verified. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace MintyPHP\Tests\Module\Notifications\Service;
|
||||
|
||||
use MintyPHP\Module\Notifications\Repository\NotificationRepositoryInterface;
|
||||
use MintyPHP\Module\Notifications\Service\NotificationMessage;
|
||||
use MintyPHP\Module\Notifications\Service\NotificationService;
|
||||
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
@@ -282,4 +283,95 @@ class NotificationServiceTest extends TestCase
|
||||
$result = $this->service->createForUser(5, 1, 'system', 'System', null, null, ['user_id' => 33]);
|
||||
$this->assertSame(56, $result);
|
||||
}
|
||||
|
||||
public function testCreateFromMessagePersistsLocalizationMetadata(): void
|
||||
{
|
||||
$message = NotificationMessage::localized(
|
||||
'user.created',
|
||||
'New user: %s',
|
||||
['Alice'],
|
||||
null,
|
||||
[],
|
||||
'admin/users/edit/abc',
|
||||
['user_id' => 33]
|
||||
);
|
||||
|
||||
$this->notifRepo->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->callback(function (array $data): bool {
|
||||
if (!isset($data['data']['__message']) || !is_array($data['data']['__message'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $data['type'] === 'user.created'
|
||||
&& $data['data']['__message']['title_key'] === 'New user: %s'
|
||||
&& ($data['data']['__message']['title_params'] ?? []) === ['Alice']
|
||||
&& $data['data']['user_id'] === 33;
|
||||
}))
|
||||
->willReturn(57);
|
||||
|
||||
$result = $this->service->createFromMessage(5, 1, $message);
|
||||
$this->assertSame(57, $result);
|
||||
}
|
||||
|
||||
public function testCreateForTenantUsersFromMessageCreatesForAllEligibleUsers(): void
|
||||
{
|
||||
$message = NotificationMessage::plain('system', 'Hello', null, null, []);
|
||||
|
||||
$this->utRepo->expects($this->once())
|
||||
->method('listActiveUserIdsByTenantId')
|
||||
->with(1)
|
||||
->willReturn([10, 20, 30]);
|
||||
|
||||
$this->notifRepo->expects($this->exactly(2))
|
||||
->method('create')
|
||||
->willReturn(1);
|
||||
|
||||
$result = $this->service->createForTenantUsersFromMessage(1, $message, 20);
|
||||
$this->assertSame(2, $result);
|
||||
}
|
||||
|
||||
public function testCreateForTenantAdminUsersFromMessageRejectsEmptyAllowedSet(): void
|
||||
{
|
||||
$message = NotificationMessage::plain('system', 'Hello', null, null, []);
|
||||
|
||||
$this->utRepo->expects($this->never())->method('listActiveUserIdsByTenantId');
|
||||
$this->notifRepo->expects($this->never())->method('create');
|
||||
|
||||
$result = $this->service->createForTenantAdminUsersFromMessage(1, $message, null, []);
|
||||
$this->assertSame(0, $result);
|
||||
}
|
||||
|
||||
public function testListForUserLocalizesFromStoredMessageMetadata(): void
|
||||
{
|
||||
$this->notifRepo->expects($this->once())
|
||||
->method('listByUser')
|
||||
->with(3, null, 50, 0)
|
||||
->willReturn([
|
||||
[
|
||||
'id' => 1,
|
||||
'type' => 'user.created',
|
||||
'title' => 'fallback',
|
||||
'body' => null,
|
||||
'link' => null,
|
||||
'data' => json_encode([
|
||||
'user_id' => 10,
|
||||
'__message' => [
|
||||
'title_key' => 'New user: %s',
|
||||
'title_params' => ['Alice'],
|
||||
'body_key' => '',
|
||||
'body_params' => [],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
'is_read' => 0,
|
||||
'created' => '2026-03-19 10:00:00',
|
||||
],
|
||||
]);
|
||||
|
||||
$result = $this->service->listForUser(3);
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertSame(t('New user: %s', 'Alice'), $result[0]['title']);
|
||||
$this->assertSame(['user_id' => 10], $result[0]['data']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user