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']); } } }