1
0
Files
breadcrumb-the-shire/tests/Service/System/SystemHealthServiceTest.php
fs 87652e55da refactor(system-info): reduce to Stripe-style status page
Reduce admin/system-info to a single focused status view: overall
status banner, components list from existing health checks, and an
environment meta table. Drops the Modules and Permissions tabs —
those were dev-diagnostics already available via CLI.

- Remove SystemInfoService + its test; wire the action directly to
  SystemHealthService and build meta inline
- Extend SystemHealthService with per-check label + description so
  the UI speaks in customer terms while the CLI doctor path stays
  compatible
- Rebuild the view on existing app-stats-table + app-empty-state
  primitives; add a small app-status-banner component for the
  headline signal
- Align help-center nav label and i18n keys (de/en)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:32:46 +02:00

204 lines
7.0 KiB
PHP

<?php
namespace MintyPHP\Tests\Service\System;
use MintyPHP\Repository\System\SystemHealthRepositoryInterface;
use MintyPHP\Service\System\SystemHealthService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class SystemHealthServiceTest extends TestCase
{
private SystemHealthRepositoryInterface&MockObject $repository;
private SystemHealthService $service;
protected function setUp(): void
{
$this->repository = $this->createMock(SystemHealthRepositoryInterface::class);
$this->service = new SystemHealthService($this->repository);
}
public function testCheckDatabaseReturnsOkWhenConnected(): void
{
$this->repository->expects($this->once())
->method('checkDatabaseConnectivity')
->willReturn(true);
$result = $this->service->checkDatabase();
self::assertSame('ok', $result['status']);
self::assertSame('Database connectivity', $result['name']);
self::assertSame('Database', $result['label']);
self::assertSame('Connection established', $result['description']);
}
public function testCheckDatabaseReturnsFailWhenDisconnected(): void
{
$this->repository->expects($this->once())
->method('checkDatabaseConnectivity')
->willReturn(false);
$result = $this->service->checkDatabase();
self::assertSame('fail', $result['status']);
self::assertSame('Database', $result['label']);
self::assertSame('Database did not return the expected response', $result['description']);
}
public function testCheckDatabaseReturnsFailOnException(): void
{
$this->repository->expects($this->once())
->method('checkDatabaseConnectivity')
->willThrowException(new \RuntimeException('Connection refused'));
$result = $this->service->checkDatabase();
self::assertSame('fail', $result['status']);
self::assertSame('check failed', $result['message']);
}
public function testCheckDatabaseSchemaReturnsOkWhenAllTablesPresent(): void
{
$this->repository->expects($this->once())
->method('listPresentTables')
->willReturn([
'users', 'roles', 'permissions', 'user_roles', 'role_permissions',
'tenants', 'departments', 'settings', 'scheduler_runtime_status',
]);
$result = $this->service->checkDatabaseSchema();
self::assertSame('ok', $result['status']);
}
public function testCheckDatabaseSchemaReturnsFailWhenTablesMissing(): void
{
$this->repository->expects($this->once())
->method('listPresentTables')
->willReturn(['users', 'roles']);
$result = $this->service->checkDatabaseSchema();
self::assertSame('fail', $result['status']);
self::assertStringContainsString('missing tables', $result['message']);
}
public function testCheckRbacBaselineReturnsOkWhenAllPermissionsActive(): void
{
$this->repository->expects($this->once())
->method('listActivePermissionKeys')
->willReturn([
'users.view', 'tenants.view', 'departments.view',
'roles.view', 'permissions.view', 'settings.view',
]);
$result = $this->service->checkRbacBaseline();
self::assertSame('ok', $result['status']);
}
public function testCheckRbacBaselineReturnsFailWhenPermissionsMissing(): void
{
$this->repository->expects($this->once())
->method('listActivePermissionKeys')
->willReturn(['users.view']);
$result = $this->service->checkRbacBaseline();
self::assertSame('fail', $result['status']);
self::assertStringContainsString('missing active permissions', $result['message']);
}
public function testCheckAdminRoleAssignmentReturnsOkWhenAdminsExist(): void
{
$this->repository->expects($this->once())
->method('countAdminUsers')
->willReturn(2);
$result = $this->service->checkAdminRoleAssignment();
self::assertSame('ok', $result['status']);
self::assertStringContainsString('2 admin user(s)', $result['message']);
}
public function testCheckAdminRoleAssignmentReturnsFailWhenNoAdmins(): void
{
$this->repository->expects($this->once())
->method('countAdminUsers')
->willReturn(0);
$result = $this->service->checkAdminRoleAssignment();
self::assertSame('fail', $result['status']);
}
public function testCheckSchedulerHeartbeatReturnsWarnWhenNoStatusRow(): void
{
$this->repository->expects($this->once())
->method('getSchedulerStatus')
->willReturn(null);
$result = $this->service->checkSchedulerHeartbeat();
self::assertSame('warn', $result['status']);
}
public function testCheckSchedulerHeartbeatReturnsOkWhenRecent(): void
{
$this->repository->expects($this->once())
->method('getSchedulerStatus')
->willReturn([
'last_heartbeat_at' => gmdate('Y-m-d H:i:s', time() - 10),
'last_result' => 'ok',
'last_error_code' => '',
]);
$result = $this->service->checkSchedulerHeartbeat();
self::assertSame('ok', $result['status']);
}
public function testCheckSchedulerHeartbeatReturnsWarnWhenStale(): void
{
$this->repository->expects($this->once())
->method('getSchedulerStatus')
->willReturn([
'last_heartbeat_at' => gmdate('Y-m-d H:i:s', time() - 600),
'last_result' => 'ok',
'last_error_code' => '',
]);
$result = $this->service->checkSchedulerHeartbeat();
self::assertSame('warn', $result['status']);
}
public function testRunAllReturnsAllSixChecks(): void
{
$this->repository->method('checkDatabaseConnectivity')->willReturn(true);
$this->repository->method('listPresentTables')->willReturn([
'users', 'roles', 'permissions', 'user_roles', 'role_permissions',
'tenants', 'departments', 'settings', 'scheduler_runtime_status',
]);
$this->repository->method('listActivePermissionKeys')->willReturn([
'users.view', 'tenants.view', 'departments.view',
'roles.view', 'permissions.view', 'settings.view',
]);
$this->repository->method('countAdminUsers')->willReturn(1);
$this->repository->method('getSchedulerStatus')->willReturn(null);
$results = $this->service->runAll();
self::assertCount(6, $results);
foreach ($results as $check) {
self::assertArrayHasKey('status', $check);
self::assertArrayHasKey('name', $check);
self::assertArrayHasKey('message', $check);
self::assertArrayHasKey('label', $check);
self::assertArrayHasKey('description', $check);
self::assertNotSame('', $check['label']);
self::assertNotSame('', $check['description']);
}
}
}