From 6da608ecf74315abbd08b447fab5f4a07ac3cab1 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:05:41 +0000 Subject: [PATCH 01/68] feat(utils): add SetupCheckUtils trait Adds trait with verifyResourceIntegrity and getErrorAndTipFromVerify methods used by multiple setup checks. Related issue: #6590 Type: Feature Checklist: - [x] Add verifyResourceIntegrity method - [x] Add getErrorAndTipFromVerify method with translated messages - [x] Declare required properties (signSetupService, urlGenerator, appManager, logger) - [x] Use IL10N for all user-facing strings Signed-off-by: Guilherme Carvalho --- lib/SetupCheck/SetupCheckUtils.php | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 lib/SetupCheck/SetupCheckUtils.php diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php new file mode 100644 index 0000000000..cf63bcccfa --- /dev/null +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -0,0 +1,74 @@ +signSetupService->willUseLocalCert($debugEnabled); + $result = $this->signSetupService->verify(php_uname('m'), $resource); + if (count($result) === 1 && $debugEnabled) { + if (isset($result['SIGNATURE_DATA_NOT_FOUND']) || isset($result['EMPTY_SIGNATURE_DATA'])) { + return []; + } + } + return $result; + } + + private function getErrorAndTipFromVerify(array $result, string $resource, bool $debugEnabled, IL10N $l10n): array + { + if (count($result) === 1 && !$debugEnabled) { + if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { + return [ + $l10n->t('Signature data not found.'), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + ]; + } + if (isset($result['EMPTY_SIGNATURE_DATA'])) { + return [ + $l10n->t('Your signature data is empty.'), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + ]; + } + } + if (isset($result['HASH_FILE_ERROR'])) { + if ($debugEnabled) { + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --%s --use-local-cert', [$resource]), + ]; + } + } + $this->logger->error('Invalid hash of binaries files', ['result' => $result]); + if ($this->appManager->isEnabledForUser('logreader')) { + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Check your nextcloud.log file on %s and run occ libresign:install --all', [ + $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) + ]), + ]; + } + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Check your nextcloud.log file and run occ libresign:install --all'), + ]; + } +} From 2222bb4ebc4daf3c75b4eb4ea3e33088f5a40b70 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:06:49 +0000 Subject: [PATCH 02/68] test: add test infrastructure for setup checks Adds ExecMock, FileSystemMock, SetupCheckFunctions to mock system calls, and updates bootstrap.php to load them. Related issue: #6590 Type: Test Checklist: - [x] Create ExecMock class - [x] Create FileSystemMock class - [x] Create SetupCheckFunctions with file_exists and exec overrides - [x] Update bootstrap.php to include new files Signed-off-by: Guilherme Carvalho --- tests/php/SetupCheckFunctions.php | 33 ++++++++++++++++++++ tests/php/Unit/SetupCheck/ExecMock.php | 14 +++++++++ tests/php/Unit/SetupCheck/FileSystemMock.php | 9 ++++++ tests/php/bootstrap.php | 5 +++ 4 files changed, 61 insertions(+) create mode 100644 tests/php/SetupCheckFunctions.php create mode 100644 tests/php/Unit/SetupCheck/ExecMock.php create mode 100644 tests/php/Unit/SetupCheck/FileSystemMock.php diff --git a/tests/php/SetupCheckFunctions.php b/tests/php/SetupCheckFunctions.php new file mode 100644 index 0000000000..b99fdcb671 --- /dev/null +++ b/tests/php/SetupCheckFunctions.php @@ -0,0 +1,33 @@ +addPsr4('Test\\', OC::$SERVERROOT . '/tests/php/lib/', true); \OC::$composerAutoloader->addPsr4('Tests\\', OC::$SERVERROOT . '/tests/php/', true); \OC::$composerAutoloader->addPsr4('OCA\\Libresign\\Tests\\', __DIR__, true); From 521e21976cb0683d1f9cee6844686fa759f76602 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:07:18 +0000 Subject: [PATCH 03/68] feat(setup-check): add JavaSetupCheck Adds JavaSetupCheck to verify Java installation, path, version and encoding. Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Use SetupCheckUtils trait - [x] Verify Java path exists - [x] Check Java version matches required version - [x] Check native.encoding for UTF-8 - [x] Return SetupResult with appropriate severity and translated messages - [x] Add unit tests covering all scenarios Signed-off-by: Guilherme Carvalho --- lib/SetupCheck/JavaSetupCheck.php | 154 ++++++ .../Unit/SetupCheck/JavaSetupCheckTest.php | 478 ++++++++++++++++++ 2 files changed, 632 insertions(+) create mode 100644 lib/SetupCheck/JavaSetupCheck.php create mode 100644 tests/php/Unit/SetupCheck/JavaSetupCheckTest.php diff --git a/lib/SetupCheck/JavaSetupCheck.php b/lib/SetupCheck/JavaSetupCheck.php new file mode 100644 index 0000000000..8577f78957 --- /dev/null +++ b/lib/SetupCheck/JavaSetupCheck.php @@ -0,0 +1,154 @@ +l10n = $l10n; + $this->javaHelper = $javaHelper; + $this->signSetupService = $signSetupService; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; + $this->logger = $logger; + $this->systemConfig = $systemConfig; + } + + public function getName(): string + { + return $this->l10n->t('Java'); + } + + public function getCategory(): string + { + return 'system'; + } + + public function run(): SetupResult + { + $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); + $javaPath = $this->javaHelper->getJavaPath(); + + if (!$javaPath) { + return SetupResult::error( + $this->l10n->t('Java not installed'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $verifyResult = $this->verifyResourceIntegrity('java', $debugEnabled); + if (!empty($verifyResult)) { + [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'java', $debugEnabled, $this->l10n); + return SetupResult::error($errorMsg, $tip); + } + + if (!file_exists($javaPath)) { + return SetupResult::error( + $this->l10n->t('Java binary not found: %s', [$javaPath]), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + exec($javaPath . ' -version 2>&1', $output, $returnCode); + if (empty($output)) { + return SetupResult::error( + $this->l10n->t('Failed to execute Java. Sounds that your operational system is blocking the JVM.'), + 'https://github.com/LibreSign/libresign/issues/2327#issuecomment-1961988790' + ); + } + if ($returnCode !== 0) { + return SetupResult::error( + $this->l10n->t('Failure to check Java version.'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $javaVersion = trim($output[0] ?? ''); + if ($javaVersion !== InstallService::JAVA_VERSION) { + return SetupResult::error( + $this->l10n->t('Invalid java version. Found: %s expected: %s', [$javaVersion, InstallService::JAVA_VERSION]), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + exec($javaPath . ' -XshowSettings:properties -version 2>&1', $encodingOutput); + $fullOutput = implode("\n", $encodingOutput); + preg_match('/native.encoding = (?.*?)(\n|$)/', $fullOutput, $matches); + $encoding = $matches['encoding'] ?? null; + + if (!$encoding) { + return SetupResult::error( + $this->l10n->t('Java encoding not found.'), + sprintf('The command %s need to have native.encoding', $javaPath . ' -XshowSettings:properties -version') + ); + } + + if (!str_contains($encoding, 'UTF-8')) { + $detectedEncoding = trim($encoding); + $phpLocale = setlocale(LC_CTYPE, 0) ?: 'not set'; + $phpLcAll = getenv('LC_ALL') ?: 'not set'; + $phpLang = getenv('LANG') ?: 'not set'; + + $tip = sprintf( + "Java detected encoding \"%s\" but UTF-8 is required.\n\n" + . "**Current PHP environment:**\n" + . "- LC_CTYPE: %s\n" + . "- LC_ALL: %s\n" + . "- LANG: %s\n\n" + . "**To fix this issue:**\n" + . "1. Set LC_ALL and LANG environment variables (e.g., LC_ALL=en_US.UTF-8) for your web server user\n" + . "2. Restart your web server after making changes\n" + . "3. Verify with command: `locale charmap` (should return UTF-8)\n\n" + . 'For more details, see: [Issue #4872](https://github.com/LibreSign/libresign/issues/4872)', + $detectedEncoding, + $phpLocale, + $phpLcAll, + $phpLang + ); + return SetupResult::info( + $this->l10n->t('Non-UTF-8 encoding detected: %s. This may cause issues with accented or special characters', [$detectedEncoding]), + $tip + ); + } + + $message = $this->l10n->t('Java version: %s', [$javaVersion]) . "\n" . + $this->l10n->t('Java binary: %s', [$javaPath]); + return SetupResult::success($message); + } +} diff --git a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php new file mode 100644 index 0000000000..29debc9bfb --- /dev/null +++ b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php @@ -0,0 +1,478 @@ +l10n = $this->createMock(IL10N::class); + $this->javaHelper = $this->createMock(JavaHelper::class); + $this->signSetupService = $this->createMock(SignSetupService::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->systemConfig = $this->createMock(IConfig::class); + } + + public function tearDown(): void + { + FileSystemMock::$files = []; + ExecMock::$commands = []; + parent::tearDown(); + } + + private function getInstance(): JavaSetupCheck + { + return new JavaSetupCheck( + $this->l10n, + $this->javaHelper, + $this->signSetupService, + $this->urlGenerator, + $this->appManager, + $this->logger, + $this->systemConfig + ); + } + + public function testJavaNotInstalled(): void + { + $this->javaHelper->method('getJavaPath')->willReturn(''); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string) => $string); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('Java not installed', $result->getDescription()); + } + + public function testJavaBinaryNotFound(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = false; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Java binary not found', $result->getDescription()); + } + + public function testJavaVersionInvalid(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => ['openjdk version "11.0.18"'], + 'result_code' => 0 + ]; + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = UTF-8'], + 'result_code' => 0 + ]; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid java version', $result->getDescription()); + } + + public function testJavaSuccess(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = UTF-8'], + 'result_code' => 0 + ]; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('success', $result->getSeverity()); + $this->assertStringContainsString('Java version: ' . InstallService::JAVA_VERSION, $result->getDescription()); + $this->assertStringContainsString('Java binary: ' . $javaPath, $result->getDescription()); + } + + public function testJavaVerifyResourceIntegrityFails(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with($this->anything(), 'java') + ->willReturn($verifyResult); + $this->signSetupService->method('willUseLocalCert'); + + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', ['result' => $verifyResult]); + + $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(false); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + } + + public function testJavaExecutionFails(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => ['some error output'], + 'result_code' => 1 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('Failure to check Java version.', $result->getDescription()); + } + + public function testJavaEmptyOutput(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [], + 'result_code' => 0 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Failed to execute Java', $result->getDescription()); + } + + public function testJavaEncodingNotFound(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['alguma outra coisa'], + 'result_code' => 0 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Java encoding not found', $result->getDescription()); + } + + public function testJavaEncodingNonUtf8(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = ISO-8859-1'], + 'result_code' => 0 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('Non-UTF-8 encoding detected', $result->getDescription()); + $this->assertStringContainsString('ISO-8859-1', $result->getDescription()); + } + + public function testGetName(): void + { + $this->l10n->method('t')->willReturnArgument(0); + $instance = $this->getInstance(); + $this->assertEquals('Java', $instance->getName()); + } + + public function testGetCategory(): void + { + $instance = $this->getInstance(); + $this->assertEquals('system', $instance->getCategory()); + } + + public function testVerifyResourceIntegritySignatureDataNotFoundWithoutDebug(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Signature data not found', $result->getDescription()); + $this->assertStringContainsString('running from source code', $result->getLinkToDoc()); + } + + public function testVerifyResourceIntegrityEmptySignatureDataWithoutDebug(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['EMPTY_SIGNATURE_DATA' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Your signature data is empty', $result->getDescription()); + } + + public function testVerifyResourceIntegritySignatureDataNotFoundWithDebug(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(true); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = UTF-8'], + 'result_code' => 0 + ]; + + $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('success', $result->getSeverity()); + } + + public function testVerifyResourceIntegrityHashFileErrorWithDebug(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(true); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + $this->assertStringContainsString('Debug mode is enabled', $result->getLinkToDoc()); + } + + public function testVerifyResourceIntegrityHashFileErrorWithoutDebugWithLogReader(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(true); + + $this->urlGenerator->method('linkToRouteAbsolute') + ->with('settings.adminsettings.form', ['section' => 'logging']) + ->willReturn('https://example.com/settings/logging'); + + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', $this->anything()); + + $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + $this->assertStringContainsString('Check your nextcloud.log file on', $result->getLinkToDoc()); + } + + public function testVerifyResourceIntegrityHashFileErrorWithoutDebugAndNoLogReader(): void + { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(false); + + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', $this->anything()); + + $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + $this->assertStringContainsString('Check your nextcloud.log file', $result->getLinkToDoc()); + } +} From 98d4df0e2a3c1318fdd8015bfcd4b7990a084d0d Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:07:48 +0000 Subject: [PATCH 04/68] feat(setup-check): add JSignPdfSetupCheck Adds JSignPdfSetupCheck to verify JSignPdf binary, integrity, Java dependency and version. Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Use SetupCheckUtils trait - [x] Verify JSignPdf path is configured and exists - [x] Verify Java is available - [x] Check JSignPdf version against required version - [x] Return SetupResult with appropriate severity and translated messages - [x] Add unit tests covering all scenarios Signed-off-by: Guilherme Carvalho --- lib/SetupCheck/JSignPdfSetupCheck.php | 133 +++++++ .../SetupCheck/JSignPdfSetupCheckTest.php | 325 ++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 lib/SetupCheck/JSignPdfSetupCheck.php create mode 100644 tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php new file mode 100644 index 0000000000..2c94d3c38d --- /dev/null +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -0,0 +1,133 @@ +l10n = $l10n; + $this->appConfig = $appConfig; + $this->jSignPdfHandler = $jSignPdfHandler; + $this->signSetupService = $signSetupService; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; + $this->logger = $logger; + $this->systemConfig = $systemConfig; + $this->javaHelper = $javaHelper; + } + + public function getName(): string + { + return $this->l10n->t('JSignPdf'); + } + + public function getCategory(): string + { + return 'system'; + } + + public function run(): SetupResult + { + $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); + $jsignpdfJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path'); + + if (!$jsignpdfJarPath) { + return SetupResult::error( + $this->l10n->t('JSignPdf not found'), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $verifyResult = $this->verifyResourceIntegrity('jsignpdf', $debugEnabled); + if (!empty($verifyResult)) { + [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'jsignpdf', $debugEnabled, $this->l10n); + return SetupResult::error($errorMsg, $tip); + } + + if (!file_exists($jsignpdfJarPath)) { + return SetupResult::error( + $this->l10n->t('JSignPdf binary not found: %s', [$jsignpdfJarPath]), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $javaPath = $this->javaHelper->getJavaPath(); + if (!$javaPath || !file_exists($javaPath)) { + return SetupResult::error( + $this->l10n->t('Necessary Java to run JSignPdf'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $jsignPdf = $this->jSignPdfHandler->getJSignPdf(); + $jsignPdf->setParam($this->jSignPdfHandler->getJSignParam()); + $currentVersion = $jsignPdf->getVersion(); + + if (!$currentVersion) { + $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); + return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + } + + if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { + $msg = $this->l10n->t('Necessary bump JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); + return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + } + + if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '>')) { + return SetupResult::error( + $this->l10n->t('Necessary downgrade JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $messages = [ + $this->l10n->t('JSignPdf version: %s', [$currentVersion]), + $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]) + ]; + + return SetupResult::success(implode("\n", $messages)); + } +} diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php new file mode 100644 index 0000000000..c3afbc93ae --- /dev/null +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -0,0 +1,325 @@ +l10n = $this->createMock(IL10N::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->jSignPdfHandler = $this->createMock(JSignPdfHandler::class); + $this->signSetupService = $this->createMock(SignSetupService::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->systemConfig = $this->createMock(IConfig::class); + $this->javaHelper = $this->createMock(JavaHelper::class); + + $this->check = new JSignPdfSetupCheck( + $this->l10n, + $this->appConfig, + $this->jSignPdfHandler, + $this->signSetupService, + $this->urlGenerator, + $this->appManager, + $this->logger, + $this->systemConfig, + $this->javaHelper + ); + } + + protected function tearDown(): void + { + FileSystemMock::$files = []; + parent::tearDown(); + } + + private function mockTranslation(): void + { + $this->l10n->method('t') + ->willReturnCallback(function ($text, $params = []) { + return vsprintf($text, $params); + }); + } + + public function testGetName(): void + { + $this->l10n->expects($this->once()) + ->method('t') + ->with('JSignPdf') + ->willReturn('JSignPdf'); + $this->assertSame('JSignPdf', $this->check->getName()); + } + + public function testGetCategory(): void + { + $this->assertSame('system', $this->check->getCategory()); + } + + public function testRunNoPathConfigured(): void + { + $this->mockTranslation(); + $this->appConfig->method('getValueString') + ->with('libresign', 'jsignpdf_jar_path') + ->willReturn(''); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('JSignPdf not found', $result->getDescription()); + } + + public function testRunVerifyFails(): void + { + $this->mockTranslation(); + $this->appConfig->method('getValueString') + ->willReturn('/fake/path/jsignpdf.jar'); + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->expects($this->once()) + ->method('willUseLocalCert') + ->with(false); + $this->signSetupService->expects($this->once()) + ->method('verify') + ->with($this->anything(), 'jsignpdf') + ->willReturn(['SOME_ERROR' => 'details']); + + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', $this->anything()); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash', $result->getDescription()); + } + + public function testRunBinaryNotFound(): void + { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify') + ->willReturn([]); + + FileSystemMock::$files[$jarPath] = false; + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('JSignPdf binary not found', $result->getDescription()); + } + + public function testRunJavaNotFound(): void + { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify') + ->willReturn([]); + + FileSystemMock::$files[$jarPath] = true; + + $this->javaHelper->method('getJavaPath') + ->willReturn(''); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('Necessary Java to run JSignPdf', $result->getDescription()); + } + + private function createJSignParamMock() + { + return $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\Sign\JSignParam::class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testRunVersionEmpty(): void + { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn(''); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('Necessary install the version', $result->getDescription()); + } + + public function testRunVersionTooLow(): void + { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn('1.0.0'); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('bump JSignPdf version', $result->getDescription()); + } + + public function testRunVersionTooHigh(): void + { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn('10.0.0'); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('downgrade JSignPdf version', $result->getDescription()); + } + + public function testRunSuccess(): void + { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn(InstallService::JSIGNPDF_VERSION); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('success', $result->getSeverity()); + $this->assertStringContainsString('JSignPdf version: ' . InstallService::JSIGNPDF_VERSION, $result->getDescription()); + $this->assertStringContainsString('JSignPdf path: ' . $jarPath, $result->getDescription()); + } +} From 29597934d21f4c09a9c69c25fe3a2f6c56be56c9 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:12:12 +0000 Subject: [PATCH 05/68] feat(setup-check): add PDFtkSetupCheck Adds PDFtkSetupCheck to verify PDFtk binary, integrity, Java dependency and version. Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Use SetupCheckUtils trait - [x] Verify PDFtk path is configured and exists - [x] Verify Java is available - [x] Check PDFtk version matches required version - [x] Return SetupResult with appropriate severity and translated messages - [x] Add unit tests covering all scenarios Signed-off-by: Guilherme Carvalho --- lib/SetupCheck/PDFtkSetupCheck.php | 129 ++++++ .../Unit/SetupCheck/PDFtkSetupCheckTest.php | 438 ++++++++++++++++++ 2 files changed, 567 insertions(+) create mode 100644 lib/SetupCheck/PDFtkSetupCheck.php create mode 100644 tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php diff --git a/lib/SetupCheck/PDFtkSetupCheck.php b/lib/SetupCheck/PDFtkSetupCheck.php new file mode 100644 index 0000000000..efcbdb4c53 --- /dev/null +++ b/lib/SetupCheck/PDFtkSetupCheck.php @@ -0,0 +1,129 @@ +l10n = $l10n; + $this->appConfig = $appConfig; + $this->signSetupService = $signSetupService; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; + $this->logger = $logger; + $this->systemConfig = $systemConfig; + $this->javaHelper = $javaHelper; + } + + public function getName(): string { + return $this->l10n->t('PDFtk'); + } + + public function getCategory(): string { + return 'system'; + } + + public function run(): SetupResult { + $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); + $pdftkPath = $this->appConfig->getValueString(Application::APP_ID, 'pdftk_path'); + + if (!$pdftkPath) { + return SetupResult::error( + $this->l10n->t('PDFtk not found'), + $this->l10n->t('Run occ libresign:install --pdftk') + ); + } + + $verifyResult = $this->verifyResourceIntegrity('pdftk', $debugEnabled); + if (!empty($verifyResult)) { + [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'pdftk', $debugEnabled, $this->l10n); + return SetupResult::error($errorMsg, $tip); + } + + if (!file_exists($pdftkPath)) { + return SetupResult::error( + $this->l10n->t('PDFtk binary not found: %s', [$pdftkPath]), + $this->l10n->t('Run occ libresign:install --pdftk') + ); + } + + $javaPath = $this->javaHelper->getJavaPath(); + if (!$javaPath || !file_exists($javaPath)) { + return SetupResult::error( + $this->l10n->t('Necessary Java to run PDFtk'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + exec($javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1', $versionOutput, $resultCode); + if ($resultCode !== 0) { + return SetupResult::error( + $this->l10n->t('Failure to check PDFtk version.'), + $this->l10n->t('Run occ libresign:install --pdftk') + ); + } + + $versionLine = $versionOutput[0] ?? ''; + preg_match('/pdftk port to java (?.*) a Handy Tool/', $versionLine, $matches); + $version = $matches['version'] ?? null; + + if (!$version) { + return SetupResult::error( + $this->l10n->t('PDFtk binary is invalid: %s', [$pdftkPath]), + $this->l10n->t('Run occ libresign:install --pdftk') + ); + } + + if ($version !== InstallService::PDFTK_VERSION) { + return SetupResult::error( + $this->l10n->t('Necessary install the version %s', [InstallService::PDFTK_VERSION]), + $this->l10n->t('Run occ libresign:install --pdftk') + ); + } + + $messages = [ + $this->l10n->t('PDFtk version: %s', [$version]), + $this->l10n->t('PDFtk path: %s', [$pdftkPath]), + ]; + return SetupResult::success(implode("\n", $messages)); + } +} diff --git a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php new file mode 100644 index 0000000000..66c23a8dec --- /dev/null +++ b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php @@ -0,0 +1,438 @@ +l10n = $this->createMock(IL10N::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->signSetupService = $this->createMock(SignSetupService::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->systemConfig = $this->createMock(IConfig::class); + $this->javaHelper = $this->createMock(JavaHelper::class); + + $this->l10n->method('t') + ->willReturnCallback(function ($text, $parameters = []) { + return vsprintf($text, $parameters); + }); + + FileSystemMock::$files = []; + ExecMock::$commands = []; + } + + protected function tearDown(): void + { + parent::tearDown(); + FileSystemMock::$files = []; + ExecMock::$commands = []; + } + + private function getInstance(): PDFtkSetupCheck + { + return new PDFtkSetupCheck( + $this->l10n, + $this->appConfig, + $this->signSetupService, + $this->urlGenerator, + $this->appManager, + $this->logger, + $this->systemConfig, + $this->javaHelper + ); + } + + public function testGetName(): void + { + $this->l10n->expects($this->once()) + ->method('t') + ->with('PDFtk') + ->willReturn('PDFtk'); + $check = $this->getInstance(); + $this->assertEquals('PDFtk', $check->getName()); + } + + public function testGetCategory(): void + { + $check = $this->getInstance(); + $this->assertEquals('system', $check->getCategory()); + } + + public function testRunNoPdftkPath(): void + { + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn(''); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk not found', $result->getDescription()); + } + + public function testRunWithVerifyError(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $debugEnabled = false; + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn($debugEnabled); + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + + $this->signSetupService->expects($this->once()) + ->method('willUseLocalCert') + ->with($debugEnabled); + $this->signSetupService->expects($this->once()) + ->method('verify') + ->with(php_uname('m'), 'pdftk') + ->willReturn($verifyResult); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Signature data not found', $result->getDescription()); + } + + public function testRunFileNotExists(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify') + ->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = false; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk binary not found', $result->getDescription()); + } + + public function testRunJavaMissing(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn(''); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Necessary Java to run PDFtk', $result->getDescription()); + } + + public function testRunJavaPathExistsButFileNotExists(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + FileSystemMock::$files[$javaPath] = false; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Necessary Java to run PDFtk', $result->getDescription()); + } + + public function testRunExecFailure(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => [], + 'result_code' => 1, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Failure to check PDFtk version', $result->getDescription()); + } + + public function testRunInvalidBinary(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ['unexpected output'], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk binary is invalid', $result->getDescription()); + } + + public function testRunVersionMismatch(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $wrongVersion = '2.0.0'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ["pdftk port to java $wrongVersion a Handy Tool"], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Necessary install the version', $result->getDescription()); + } + + public function testRunSuccess(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $expectedVersion = InstallService::PDFTK_VERSION; + + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ["pdftk port to java $expectedVersion a Handy Tool"], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); + $this->assertStringContainsString("PDFtk version: $expectedVersion", $result->getDescription()); + $this->assertStringContainsString("PDFtk path: $pdftkPath", $result->getDescription()); + } + + public function testRunWithDebugIgnoresSignatureError(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $expectedVersion = InstallService::PDFTK_VERSION; + + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(true); + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + $this->signSetupService->method('willUseLocalCert')->with(true); + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'pdftk') + ->willReturn($verifyResult); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ["pdftk port to java $expectedVersion a Handy Tool"], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); + } + + public function testRunEmptyCommandOutput(): void + { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => [], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk binary is invalid', $result->getDescription()); + } +} From d68f9739ab2d692b51e3dd14d2d0a64f0336a7fb Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:12:39 +0000 Subject: [PATCH 06/68] feat(setup-check): add PopplerSetupCheck Adds PopplerSetupCheck to verify pdfsig and pdfinfo utilities (optional). Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Check pdfsig installation and version - [x] Check pdfinfo installation and version - [x] Return info severity when missing (optional check) - [x] Return success when both tools are working - [x] Add unit tests covering all scenarios Signed-off-by: Guilherme Carvalho --- lib/SetupCheck/PopplerSetupCheck.php | 77 +++++++++ .../Unit/SetupCheck/PopplerSetupCheckTest.php | 163 ++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 lib/SetupCheck/PopplerSetupCheck.php create mode 100644 tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php diff --git a/lib/SetupCheck/PopplerSetupCheck.php b/lib/SetupCheck/PopplerSetupCheck.php new file mode 100644 index 0000000000..b0f92d66a3 --- /dev/null +++ b/lib/SetupCheck/PopplerSetupCheck.php @@ -0,0 +1,77 @@ +l10n = $l10n; + } + + public function getName(): string { + return $this->l10n->t('Poppler utils'); + } + + public function getCategory(): string { + return 'system'; + } + + public function run(): SetupResult { + $pdfsigOk = $this->checkPdfSig(); + $pdfinfoOk = $this->checkPdfinfo(); + + if ($pdfsigOk && $pdfinfoOk) { + return SetupResult::success( + $this->l10n->t('pdfsig version: %s, pdfinfo version: %s', [$pdfsigOk, $pdfinfoOk]) + ); + } + + $messages = []; + $tip = $this->l10n->t('Install the package poppler-utils on your operating system to enable PDF signature validation and page dimension detection.'); + + if (!$pdfsigOk) { + $messages[] = $this->l10n->t('pdfsig not installed or not working'); + } + if (!$pdfinfoOk) { + $messages[] = $this->l10n->t('pdfinfo not installed or not working'); + } + + // Since Poppler is optional, return info instead of error + return SetupResult::info(implode('; ', $messages), $tip); + } + + private function checkPdfSig(): ?string { + exec('pdfsig -v 2>&1', $output, $retval); + if ($retval !== 0 || empty($output)) { + return null; + } + $full = implode(PHP_EOL, $output); + if (preg_match('/pdfsig version (?.*)/', $full, $matches)) { + return $matches['version']; + } + return null; + } + + private function checkPdfinfo(): ?string { + exec('pdfinfo -v 2>&1', $output, $retval); + if ($retval !== 0 || empty($output)) { + return null; + } + $full = implode(PHP_EOL, $output); + if (preg_match('/pdfinfo version (?.*)/', $full, $matches)) { + return $matches['version']; + } + return null; + } +} diff --git a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php new file mode 100644 index 0000000000..db665faa8a --- /dev/null +++ b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php @@ -0,0 +1,163 @@ +l10n = $this->createMock(IL10N::class); + $this->l10n->method('t') + ->willReturnCallback(fn($text, $params = []) => vsprintf($text, $params)); + + $this->check = new PopplerSetupCheck($this->l10n); + } + + public function testBothToolsInstalled(): void + { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig version 25.03.0', 'extra line'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo version 25.03.0'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('success', $result->getSeverity()); + $this->assertEquals( + 'pdfsig version: 25.03.0, pdfinfo version: 25.03.0', + $result->getDescription() + ); + } + + public function testOnlyPdfsigInstalled(): void + { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig version 25.03.0'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + $this->assertStringContainsString('Install the package poppler-utils', $result->getLinkToDoc()); + } + + public function testNoToolsInstalled(): void + { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString( + 'pdfsig not installed or not working; pdfinfo not installed or not working', + $result->getDescription() + ); + } + + public function testPdfsigVersionParseFails(): void + { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig version 25.03.0'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo: unknown option'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + $this->assertStringNotContainsString('pdfsig', $result->getDescription()); + } + + public function testOnlyPdfinfoInstalledButParseFails(): void + { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo: unknown option'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + } + + public function testBothToolsParseFails(): void + { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig: error'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo: error'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + } + + public function testGetName(): void + { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Poppler utils') + ->willReturn('Poppler utils'); + $this->assertEquals('Poppler utils', $this->check->getName()); + } + + public function testGetCategory(): void + { + $this->assertEquals('system', $this->check->getCategory()); + } +} From 4f61d50bffd79611e0a32eb36cedcdad1d7dba4a Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:13:25 +0000 Subject: [PATCH 07/68] feat(setup-check): add ImagickSetupCheck Adds ImagickSetupCheck to verify imagick PHP extension (optional). Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Check if imagick extension is loaded - [x] Return info severity when not loaded (optional check) - [x] Return success when loaded - [x] Add unit tests covering both states Signed-off-by: Guilherme Carvalho --- lib/SetupCheck/ImagickSetupCheck.php | 39 ++++++++ .../Unit/SetupCheck/ImagickSetupCheckTest.php | 93 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 lib/SetupCheck/ImagickSetupCheck.php create mode 100644 tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php diff --git a/lib/SetupCheck/ImagickSetupCheck.php b/lib/SetupCheck/ImagickSetupCheck.php new file mode 100644 index 0000000000..48ac35a527 --- /dev/null +++ b/lib/SetupCheck/ImagickSetupCheck.php @@ -0,0 +1,39 @@ +l10n = $l10n; + } + + public function getName(): string { + return $this->l10n->t('Imagick PHP extension'); + } + + public function getCategory(): string { + return 'system'; + } + + public function run(): SetupResult { + if (!extension_loaded('imagick')) { + return SetupResult::info( + $this->l10n->t('Imagick extension is not loaded'), + $this->l10n->t('Install php-imagick to enable visible signatures, background images, and signature element rendering.') + ); + } + return SetupResult::success($this->l10n->t('Imagick extension is loaded')); + } +} diff --git a/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php b/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php new file mode 100644 index 0000000000..7087264e3e --- /dev/null +++ b/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php @@ -0,0 +1,93 @@ + */ + public static array $mockExtensionLoaded = []; + + /** @var IL10N&MockObject */ + private $l10n; + + /** @var ImagickSetupCheck */ + private $check; + + protected function setUp(): void + { + parent::setUp(); + self::$mockExtensionLoaded = []; + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t') + ->willReturnCallback(fn(string $text, array $parameters = []) => vsprintf($text, $parameters)); + + $this->check = new ImagickSetupCheck( + $this->l10n + ); + } + + public function testExtensionNotLoaded(): void + { + self::$mockExtensionLoaded['imagick'] = false; + $check = new ImagickSetupCheck($this->l10n); + + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('Imagick extension is not loaded', $result->getDescription()); + $this->assertNotNull($result->getDescription()); + } + + public function testExtensionLoaded(): void + { + self::$mockExtensionLoaded['imagick'] = true; + $check = new ImagickSetupCheck($this->l10n); + + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('success', $result->getSeverity()); + $this->assertStringContainsString('Imagick extension is loaded', $result->getDescription()); + } + + public function testGetName(): void + { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Imagick PHP extension') + ->willReturn('Imagick PHP extension'); + $this->assertEquals('Imagick PHP extension', $this->check->getName()); + } + + public function testGetCategory(): void + { + $this->assertEquals('system', $this->check->getCategory()); + } + } +} From aac6c549da3c442be151389909b40cf2df41a711 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:13:59 +0000 Subject: [PATCH 08/68] feat(setup-check): add CertificateEngineSetupCheck Adds CertificateEngineSetupCheck to process certificate engine configuration results. Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Use CertificateEngineFactory to get current engine - [x] Process ConfigureCheckHelper results from engine - [x] Convert to SetupResult with aggregated messages and tips - [x] Handle engine not defined (error) - [x] Add unit tests covering success, warning, error, and mixed results Signed-off-by: Guilherme Carvalho --- .../CertificateEngineSetupCheck.php | 94 +++++++ .../CertificateEngineSetupCheckTest.php | 235 ++++++++++++++++++ 2 files changed, 329 insertions(+) create mode 100644 lib/SetupCheck/CertificateEngineSetupCheck.php create mode 100644 tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php diff --git a/lib/SetupCheck/CertificateEngineSetupCheck.php b/lib/SetupCheck/CertificateEngineSetupCheck.php new file mode 100644 index 0000000000..78dd75e183 --- /dev/null +++ b/lib/SetupCheck/CertificateEngineSetupCheck.php @@ -0,0 +1,94 @@ +l10n = $l10n; + $this->certificateEngineFactory = $certificateEngineFactory; + } + + public function getName(): string + { + return $this->l10n->t('Certificate engine'); + } + + public function getCategory(): string + { + return 'security'; + } + + public function run(): SetupResult + { + try { + $engine = $this->certificateEngineFactory->getEngine(); + $checkResults = $engine->configureCheck(); + } catch (\Throwable $e) { + $engineName = $this->certificateEngineFactory->getEngine()->getName() ?? 'unknown'; + return SetupResult::error( + $this->l10n->t('Define the certificate engine to use'), + $this->l10n->t('Run occ libresign:configure:%s --help', [$engineName]) + ); + } + + // Process results: if any error, return error; else if any warning, return warning; else success + $hasError = false; + $hasWarning = false; + $messages = []; + $tips = []; + + foreach ($checkResults as $result) { + if ($result instanceof ConfigureCheckHelper) { + $status = $result->getStatus(); + $msg = $result->getMessage(); + $tip = $result->getTip(); + + if ($status === 'error') { + $hasError = true; + $messages[] = "[ERROR] $msg"; + } elseif ($status === 'warning') { + $hasWarning = true; + $messages[] = "[WARNING] $msg"; + } else { + $messages[] = $msg; + } + + if (!empty($tip)) { + $tips[] = $tip; + } + } + } + + $tip = ''; + if (!empty($tips)) { + $tip = implode("\n", array_unique($tips)); + } + + if ($hasError) { + return SetupResult::error(implode("\n", $messages), $tip); + } elseif ($hasWarning) { + return SetupResult::warning(implode("\n", $messages), $tip); + } else { + return SetupResult::success(implode("\n", $messages) ?: $this->l10n->t('Certificate engine is configured correctly')); + } + } +} diff --git a/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php b/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php new file mode 100644 index 0000000000..e398fb6d52 --- /dev/null +++ b/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php @@ -0,0 +1,235 @@ +l10n = $this->createMock(IL10N::class); + $this->certificateEngineFactory = $this->createMock(CertificateEngineFactory::class); + } + + private function getInstance(): CertificateEngineSetupCheck + { + return new CertificateEngineSetupCheck( + $this->l10n, + $this->certificateEngineFactory + ); + } + + public function testGetName(): void + { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Certificate engine') + ->willReturn('Certificate engine'); + + $check = $this->getInstance(); + $this->assertSame('Certificate engine', $check->getName()); + } + + public function testGetCategory(): void + { + $check = $this->getInstance(); + $this->assertSame('security', $check->getCategory()); + } + + public function testRunWithException(): void + { + $engineMock = $this->createMock(MockableIEngineHandler::class); + $engineMock->method('getName')->willReturn('cfssl'); + + $this->certificateEngineFactory->expects($this->exactly(2)) + ->method('getEngine') + ->willReturnOnConsecutiveCalls( + $this->throwException(new \RuntimeException('Engine not found')), + $engineMock + ); + + $calls = 0; + $this->l10n->expects($this->exactly(2)) + ->method('t') + ->willReturnCallback(function ($arg1, $arg2 = []) use (&$calls) { + $calls++; + if ($calls === 1) { + $this->assertEquals('Define the certificate engine to use', $arg1); + $this->assertEquals([], $arg2); + return 'Define the certificate engine to use'; + } elseif ($calls === 2) { + $this->assertEquals('Run occ libresign:configure:%s --help', $arg1); + $this->assertEquals(['cfssl'], $arg2); + return 'Run occ libresign:configure:cfssl --help'; + } + }); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('Define the certificate engine to use', $result->getDescription()); + $this->assertEquals('Run occ libresign:configure:cfssl --help', $result->getLinkToDoc()); + } + + public function testRunWithSuccess(): void + { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck') + ->willReturn([ + (new ConfigureCheckHelper())->setSuccessMessage('Engine OK'), + ]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('success', $result->getSeverity()); + $this->assertEquals('Engine OK', $result->getDescription()); + } + + public function testRunWithError(): void + { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck') + ->willReturn([ + (new ConfigureCheckHelper())->setErrorMessage('Engine error'), + ]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('[ERROR] Engine error', $result->getDescription()); + } + + public function testRunWithMixedResults(): void + { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck') + ->willReturn([ + (new ConfigureCheckHelper())->setSuccessMessage('Success'), + (new ConfigureCheckHelper())->setInfoMessage('Info message'), + (new ConfigureCheckHelper())->setErrorMessage('Error'), + ]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + $expected = "Success\nInfo message\n[ERROR] Error"; + $this->assertEquals($expected, $result->getDescription()); + } + + public function testRunWithEmptyResults(): void + { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck')->willReturn([]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $this->l10n->method('t') + ->with('Certificate engine is configured correctly') + ->willReturn('Certificate engine is configured correctly'); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('success', $result->getSeverity()); + $this->assertEquals('Certificate engine is configured correctly', $result->getDescription()); + } + + public function testRunWithOnlyWarnings(): void + { + $engine = $this->createMock(IEngineHandler::class); + + $warning1 = new ConfigureCheckHelper(); + $warning1->setStatus('warning'); + $warning1->setMessage('Disk space low'); + + $warning2 = new ConfigureCheckHelper(); + $warning2->setStatus('warning'); + $warning2->setMessage('Certificate expires soon'); + + $engine->method('configureCheck') + ->willReturn([$warning1, $warning2]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('warning', $result->getSeverity()); + $expected = "[WARNING] Disk space low\n[WARNING] Certificate expires soon"; + $this->assertEquals($expected, $result->getDescription()); + } + + public function testRunCollectsTipsAndRemovesDuplicates(): void + { + $engine = $this->createMock(IEngineHandler::class); + + $helper1 = (new ConfigureCheckHelper()) + ->setErrorMessage('First error') + ->setTip('Tip A'); + $helper2 = (new ConfigureCheckHelper()) + ->setInfoMessage('Irrelevant info') + ->setTip(''); + $helper3 = (new ConfigureCheckHelper()) + ->setErrorMessage('Second error') + ->setTip('Tip B'); + $helper4 = (new ConfigureCheckHelper()) + ->setErrorMessage('First error again') + ->setTip('Tip A'); + + $engine->method('configureCheck') + ->willReturn([$helper1, $helper2, $helper3, $helper4]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + + $expectedDesc = "[ERROR] First error\nIrrelevant info\n[ERROR] Second error\n[ERROR] First error again"; + $this->assertEquals($expectedDesc, $result->getDescription()); + $this->assertEquals("Tip A\nTip B", $result->getLinkToDoc()); + } +} From abba64cda4f92d50b74337409841084667d77306 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:14:25 +0000 Subject: [PATCH 09/68] feat(application): register new setup checks Registers all six setup check classes in Application.php. Related issue: #6590 Type: Feature Checklist: - [x] Add registerSetupCheck for JavaSetupCheck - [x] Add registerSetupCheck for JSignPdfSetupCheck - [x] Add registerSetupCheck for PDFtkSetupCheck - [x] Add registerSetupCheck for PopplerSetupCheck - [x] Add registerSetupCheck for ImagickSetupCheck - [x] Add registerSetupCheck for CertificateEngineSetupCheck Signed-off-by: Guilherme Carvalho --- lib/AppInfo/Application.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 43238b34fd..a454447802 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -94,5 +94,14 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class); $context->registerDashboardWidget(PendingSignaturesWidget::class); + + $context->registerSetupCheck(\OCA\Libresign\SetupCheck\JavaSetupCheck::class); + $context->registerSetupCheck(\OCA\Libresign\SetupCheck\JSignPdfSetupCheck::class); + $context->registerSetupCheck(\OCA\Libresign\SetupCheck\PDFtkSetupCheck::class); + + $context->registerSetupCheck(\OCA\Libresign\SetupCheck\PopplerSetupCheck::class); + $context->registerSetupCheck(\OCA\Libresign\SetupCheck\ImagickSetupCheck::class); + + $context->registerSetupCheck(\OCA\Libresign\SetupCheck\CertificateEngineSetupCheck::class); } } From 2a8e50da88493b5a5fae11ed155f12576fd53c92 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:51:25 +0000 Subject: [PATCH 10/68] refactor: deprecate ConfigureCheckService Adds @deprecated annotation to ConfigureCheckService pointing to the new individual setup checks. Related issue: #6590 Type: Maintenance Checklist: - [x] Add @deprecated tag with version 13.0.4 - [x] Mention replacement classes (JavaSetupCheck, JSignPdfSetupCheck, etc.) Signed-off-by: Guilherme Carvalho --- lib/Service/Install/ConfigureCheckService.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Service/Install/ConfigureCheckService.php b/lib/Service/Install/ConfigureCheckService.php index 8002343591..9e482b1f1d 100644 --- a/lib/Service/Install/ConfigureCheckService.php +++ b/lib/Service/Install/ConfigureCheckService.php @@ -20,6 +20,10 @@ use OCP\IURLGenerator; use Psr\Log\LoggerInterface; +/** +* @deprecated 13.0.4 Use the individual SetupCheck classes instead +* (JavaSetupCheck, JSignPdfSetupCheck, etc.). +*/ class ConfigureCheckService { private string $architecture; private bool $isCacheDisabled = false; From 088340645bd12f204e8a0cd019be8a23ad23609f Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Thu, 5 Mar 2026 18:08:23 +0000 Subject: [PATCH 11/68] refactor(setup-check): reorganize mocks and apply code standards This refactoring consolidates and organizes test classes related to SetupChecks, while also applying standardization improvements and best practices in the main codebase. - Move `ExecMock` and `FileSystemMock` classes to the new `Mock` namespace under `tests/php/Unit/SetupCheck/` and create a `functions.php` file to hold the overridden global functions (`file_exists`, `exec`). - Update `bootstrap.php` to load mocks from the new location and remove the obsolete `SetupCheckFunctions.php` file. - Apply code style fixes (indentation, spacing, trailing commas) to all files touched by the refactoring. Related issue: #6590 Type: Maintenance Checklist: - [x] Move mocks to subdirectory and update bootstrap - [x] Apply code style fixes Signed-off-by: Guilherme Carvalho --- tests/php/SetupCheckFunctions.php | 33 - .../CertificateEngineSetupCheckTest.php | 413 ++++---- tests/php/Unit/SetupCheck/FileSystemMock.php | 9 - .../Unit/SetupCheck/ImagickSetupCheckTest.php | 142 ++- .../SetupCheck/JSignPdfSetupCheckTest.php | 595 ++++++------ .../Unit/SetupCheck/JavaSetupCheckTest.php | 895 +++++++++--------- .../Unit/SetupCheck/{ => Mock}/ExecMock.php | 4 +- .../Unit/SetupCheck/Mock/FileSystemMock.php | 9 + tests/php/Unit/SetupCheck/Mock/functions.php | 34 + .../Unit/SetupCheck/PDFtkSetupCheckTest.php | 819 ++++++++-------- .../Unit/SetupCheck/PopplerSetupCheckTest.php | 284 +++--- tests/php/bootstrap.php | 7 +- 12 files changed, 1580 insertions(+), 1664 deletions(-) delete mode 100644 tests/php/SetupCheckFunctions.php delete mode 100644 tests/php/Unit/SetupCheck/FileSystemMock.php rename tests/php/Unit/SetupCheck/{ => Mock}/ExecMock.php (65%) create mode 100644 tests/php/Unit/SetupCheck/Mock/FileSystemMock.php create mode 100644 tests/php/Unit/SetupCheck/Mock/functions.php diff --git a/tests/php/SetupCheckFunctions.php b/tests/php/SetupCheckFunctions.php deleted file mode 100644 index b99fdcb671..0000000000 --- a/tests/php/SetupCheckFunctions.php +++ /dev/null @@ -1,33 +0,0 @@ -l10n = $this->createMock(IL10N::class); - $this->certificateEngineFactory = $this->createMock(CertificateEngineFactory::class); - } - - private function getInstance(): CertificateEngineSetupCheck - { - return new CertificateEngineSetupCheck( - $this->l10n, - $this->certificateEngineFactory - ); - } - - public function testGetName(): void - { - $this->l10n->expects($this->once()) - ->method('t') - ->with('Certificate engine') - ->willReturn('Certificate engine'); - - $check = $this->getInstance(); - $this->assertSame('Certificate engine', $check->getName()); - } - - public function testGetCategory(): void - { - $check = $this->getInstance(); - $this->assertSame('security', $check->getCategory()); - } - - public function testRunWithException(): void - { - $engineMock = $this->createMock(MockableIEngineHandler::class); - $engineMock->method('getName')->willReturn('cfssl'); - - $this->certificateEngineFactory->expects($this->exactly(2)) - ->method('getEngine') - ->willReturnOnConsecutiveCalls( - $this->throwException(new \RuntimeException('Engine not found')), - $engineMock - ); - - $calls = 0; - $this->l10n->expects($this->exactly(2)) - ->method('t') - ->willReturnCallback(function ($arg1, $arg2 = []) use (&$calls) { - $calls++; - if ($calls === 1) { - $this->assertEquals('Define the certificate engine to use', $arg1); - $this->assertEquals([], $arg2); - return 'Define the certificate engine to use'; - } elseif ($calls === 2) { - $this->assertEquals('Run occ libresign:configure:%s --help', $arg1); - $this->assertEquals(['cfssl'], $arg2); - return 'Run occ libresign:configure:cfssl --help'; - } - }); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('error', $result->getSeverity()); - $this->assertEquals('Define the certificate engine to use', $result->getDescription()); - $this->assertEquals('Run occ libresign:configure:cfssl --help', $result->getLinkToDoc()); - } - - public function testRunWithSuccess(): void - { - $engine = $this->createMock(IEngineHandler::class); - $engine->method('configureCheck') - ->willReturn([ - (new ConfigureCheckHelper())->setSuccessMessage('Engine OK'), - ]); - - $this->certificateEngineFactory->method('getEngine')->willReturn($engine); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('success', $result->getSeverity()); - $this->assertEquals('Engine OK', $result->getDescription()); - } - - public function testRunWithError(): void - { - $engine = $this->createMock(IEngineHandler::class); - $engine->method('configureCheck') - ->willReturn([ - (new ConfigureCheckHelper())->setErrorMessage('Engine error'), - ]); - - $this->certificateEngineFactory->method('getEngine')->willReturn($engine); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('error', $result->getSeverity()); - $this->assertEquals('[ERROR] Engine error', $result->getDescription()); - } - - public function testRunWithMixedResults(): void - { - $engine = $this->createMock(IEngineHandler::class); - $engine->method('configureCheck') - ->willReturn([ - (new ConfigureCheckHelper())->setSuccessMessage('Success'), - (new ConfigureCheckHelper())->setInfoMessage('Info message'), - (new ConfigureCheckHelper())->setErrorMessage('Error'), - ]); - - $this->certificateEngineFactory->method('getEngine')->willReturn($engine); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('error', $result->getSeverity()); - $expected = "Success\nInfo message\n[ERROR] Error"; - $this->assertEquals($expected, $result->getDescription()); - } - - public function testRunWithEmptyResults(): void - { - $engine = $this->createMock(IEngineHandler::class); - $engine->method('configureCheck')->willReturn([]); - - $this->certificateEngineFactory->method('getEngine')->willReturn($engine); - - $this->l10n->method('t') - ->with('Certificate engine is configured correctly') - ->willReturn('Certificate engine is configured correctly'); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('success', $result->getSeverity()); - $this->assertEquals('Certificate engine is configured correctly', $result->getDescription()); - } - - public function testRunWithOnlyWarnings(): void - { - $engine = $this->createMock(IEngineHandler::class); - - $warning1 = new ConfigureCheckHelper(); - $warning1->setStatus('warning'); - $warning1->setMessage('Disk space low'); - - $warning2 = new ConfigureCheckHelper(); - $warning2->setStatus('warning'); - $warning2->setMessage('Certificate expires soon'); - - $engine->method('configureCheck') - ->willReturn([$warning1, $warning2]); - - $this->certificateEngineFactory->method('getEngine')->willReturn($engine); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('warning', $result->getSeverity()); - $expected = "[WARNING] Disk space low\n[WARNING] Certificate expires soon"; - $this->assertEquals($expected, $result->getDescription()); - } - - public function testRunCollectsTipsAndRemovesDuplicates(): void - { - $engine = $this->createMock(IEngineHandler::class); - - $helper1 = (new ConfigureCheckHelper()) - ->setErrorMessage('First error') - ->setTip('Tip A'); - $helper2 = (new ConfigureCheckHelper()) - ->setInfoMessage('Irrelevant info') - ->setTip(''); - $helper3 = (new ConfigureCheckHelper()) - ->setErrorMessage('Second error') - ->setTip('Tip B'); - $helper4 = (new ConfigureCheckHelper()) - ->setErrorMessage('First error again') - ->setTip('Tip A'); - - $engine->method('configureCheck') - ->willReturn([$helper1, $helper2, $helper3, $helper4]); - - $this->certificateEngineFactory->method('getEngine')->willReturn($engine); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('error', $result->getSeverity()); - - $expectedDesc = "[ERROR] First error\nIrrelevant info\n[ERROR] Second error\n[ERROR] First error again"; - $this->assertEquals($expectedDesc, $result->getDescription()); - $this->assertEquals("Tip A\nTip B", $result->getLinkToDoc()); - } +final class CertificateEngineSetupCheckTest extends TestCase { + /** @var IL10N&MockObject */ + private $l10n; + /** @var CertificateEngineFactory&MockObject */ + private $certificateEngineFactory; + + protected function setUp(): void { + parent::setUp(); + $this->l10n = $this->createMock(IL10N::class); + $this->certificateEngineFactory = $this->createMock(CertificateEngineFactory::class); + } + + private function getInstance(): CertificateEngineSetupCheck { + return new CertificateEngineSetupCheck( + $this->l10n, + $this->certificateEngineFactory + ); + } + + public function testGetName(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Certificate engine') + ->willReturn('Certificate engine'); + + $check = $this->getInstance(); + $this->assertSame('Certificate engine', $check->getName()); + } + + public function testGetCategory(): void { + $check = $this->getInstance(); + $this->assertSame('security', $check->getCategory()); + } + + public function testRunWithException(): void { + $engineMock = $this->createMock(MockableIEngineHandler::class); + $engineMock->method('getName')->willReturn('cfssl'); + + $this->certificateEngineFactory->expects($this->exactly(2)) + ->method('getEngine') + ->willReturnOnConsecutiveCalls( + $this->throwException(new \RuntimeException('Engine not found')), + $engineMock + ); + + $calls = 0; + $this->l10n->expects($this->exactly(2)) + ->method('t') + ->willReturnCallback(function ($arg1, $arg2 = []) use (&$calls) { + $calls++; + if ($calls === 1) { + $this->assertEquals('Define the certificate engine to use', $arg1); + $this->assertEquals([], $arg2); + return 'Define the certificate engine to use'; + } elseif ($calls === 2) { + $this->assertEquals('Run occ libresign:configure:%s --help', $arg1); + $this->assertEquals(['cfssl'], $arg2); + return 'Run occ libresign:configure:cfssl --help'; + } + }); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('Define the certificate engine to use', $result->getDescription()); + $this->assertEquals('Run occ libresign:configure:cfssl --help', $result->getLinkToDoc()); + } + + public function testRunWithSuccess(): void { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck') + ->willReturn([ + (new ConfigureCheckHelper())->setSuccessMessage('Engine OK'), + ]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('success', $result->getSeverity()); + $this->assertEquals('Engine OK', $result->getDescription()); + } + + public function testRunWithError(): void { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck') + ->willReturn([ + (new ConfigureCheckHelper())->setErrorMessage('Engine error'), + ]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('[ERROR] Engine error', $result->getDescription()); + } + + public function testRunWithMixedResults(): void { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck') + ->willReturn([ + (new ConfigureCheckHelper())->setSuccessMessage('Success'), + (new ConfigureCheckHelper())->setInfoMessage('Info message'), + (new ConfigureCheckHelper())->setErrorMessage('Error'), + ]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + $expected = "Success\nInfo message\n[ERROR] Error"; + $this->assertEquals($expected, $result->getDescription()); + } + + public function testRunWithEmptyResults(): void { + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck')->willReturn([]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $this->l10n->method('t') + ->with('Certificate engine is configured correctly') + ->willReturn('Certificate engine is configured correctly'); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('success', $result->getSeverity()); + $this->assertEquals('Certificate engine is configured correctly', $result->getDescription()); + } + + public function testRunWithOnlyWarnings(): void { + $engine = $this->createMock(IEngineHandler::class); + + $warning1 = new ConfigureCheckHelper(); + $warning1->setStatus('warning'); + $warning1->setMessage('Disk space low'); + + $warning2 = new ConfigureCheckHelper(); + $warning2->setStatus('warning'); + $warning2->setMessage('Certificate expires soon'); + + $engine->method('configureCheck') + ->willReturn([$warning1, $warning2]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('warning', $result->getSeverity()); + $expected = "[WARNING] Disk space low\n[WARNING] Certificate expires soon"; + $this->assertEquals($expected, $result->getDescription()); + } + + public function testRunCollectsTipsAndRemovesDuplicates(): void { + $engine = $this->createMock(IEngineHandler::class); + + $helper1 = (new ConfigureCheckHelper()) + ->setErrorMessage('First error') + ->setTip('Tip A'); + $helper2 = (new ConfigureCheckHelper()) + ->setInfoMessage('Irrelevant info') + ->setTip(''); + $helper3 = (new ConfigureCheckHelper()) + ->setErrorMessage('Second error') + ->setTip('Tip B'); + $helper4 = (new ConfigureCheckHelper()) + ->setErrorMessage('First error again') + ->setTip('Tip A'); + + $engine->method('configureCheck') + ->willReturn([$helper1, $helper2, $helper3, $helper4]); + + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('error', $result->getSeverity()); + + $expectedDesc = "[ERROR] First error\nIrrelevant info\n[ERROR] Second error\n[ERROR] First error again"; + $this->assertEquals($expectedDesc, $result->getDescription()); + $this->assertEquals("Tip A\nTip B", $result->getLinkToDoc()); + } } diff --git a/tests/php/Unit/SetupCheck/FileSystemMock.php b/tests/php/Unit/SetupCheck/FileSystemMock.php deleted file mode 100644 index c8e8cfc4c1..0000000000 --- a/tests/php/Unit/SetupCheck/FileSystemMock.php +++ /dev/null @@ -1,9 +0,0 @@ - */ - public static array $mockExtensionLoaded = []; - - /** @var IL10N&MockObject */ - private $l10n; - - /** @var ImagickSetupCheck */ - private $check; - - protected function setUp(): void - { - parent::setUp(); - self::$mockExtensionLoaded = []; - - $this->l10n = $this->createMock(IL10N::class); - $this->l10n->method('t') - ->willReturnCallback(fn(string $text, array $parameters = []) => vsprintf($text, $parameters)); - - $this->check = new ImagickSetupCheck( - $this->l10n - ); - } - - public function testExtensionNotLoaded(): void - { - self::$mockExtensionLoaded['imagick'] = false; - $check = new ImagickSetupCheck($this->l10n); - - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('info', $result->getSeverity()); - $this->assertStringContainsString('Imagick extension is not loaded', $result->getDescription()); - $this->assertNotNull($result->getDescription()); - } - - public function testExtensionLoaded(): void - { - self::$mockExtensionLoaded['imagick'] = true; - $check = new ImagickSetupCheck($this->l10n); - - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals('success', $result->getSeverity()); - $this->assertStringContainsString('Imagick extension is loaded', $result->getDescription()); - } - - public function testGetName(): void - { - $this->l10n->expects($this->once()) - ->method('t') - ->with('Imagick PHP extension') - ->willReturn('Imagick PHP extension'); - $this->assertEquals('Imagick PHP extension', $this->check->getName()); - } - - public function testGetCategory(): void - { - $this->assertEquals('system', $this->check->getCategory()); - } - } + use OCA\Libresign\SetupCheck\ImagickSetupCheck; + use OCP\IL10N; + use OCP\SetupCheck\SetupResult; + use PHPUnit\Framework\MockObject\MockObject; + use PHPUnit\Framework\TestCase; + + class ImagickSetupCheckTest extends TestCase { + /** @var array */ + public static array $mockExtensionLoaded = []; + + /** @var IL10N&MockObject */ + private $l10n; + + /** @var ImagickSetupCheck */ + private $check; + + protected function setUp(): void { + parent::setUp(); + self::$mockExtensionLoaded = []; + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t') + ->willReturnCallback(fn (string $text, array $parameters = []) => vsprintf($text, $parameters)); + + $this->check = new ImagickSetupCheck( + $this->l10n + ); + } + + public function testExtensionNotLoaded(): void { + self::$mockExtensionLoaded['imagick'] = false; + $check = new ImagickSetupCheck($this->l10n); + + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('Imagick extension is not loaded', $result->getDescription()); + $this->assertNotNull($result->getDescription()); + } + + public function testExtensionLoaded(): void { + self::$mockExtensionLoaded['imagick'] = true; + $check = new ImagickSetupCheck($this->l10n); + + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals('success', $result->getSeverity()); + $this->assertStringContainsString('Imagick extension is loaded', $result->getDescription()); + } + + public function testGetName(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Imagick PHP extension') + ->willReturn('Imagick PHP extension'); + $this->assertEquals('Imagick PHP extension', $this->check->getName()); + } + + public function testGetCategory(): void { + $this->assertEquals('system', $this->check->getCategory()); + } + } } diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index c3afbc93ae..c9b1541577 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -8,318 +8,303 @@ namespace OCA\Libresign\Tests\Unit\SetupCheck; -use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Handler\SignEngine\JSignPdfHandler; -use OCA\Libresign\Service\Install\SignSetupService; +use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; +use OCA\Libresign\Service\Install\SignSetupService; use OCA\Libresign\SetupCheck\JSignPdfSetupCheck; -use OCA\Libresign\SetupCheck\FileSystemMock; -use OCP\IL10N; -use OCP\IAppConfig; -use OCP\IURLGenerator; +use OCA\Libresign\Tests\Unit\SetupCheck\Mock\FileSystemMock; use OCP\App\IAppManager; +use OCP\IAppConfig; use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; use OCP\SetupCheck\SetupResult; -use Psr\Log\LoggerInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; -class JSignPdfSetupCheckTest extends TestCase -{ - /** @var IL10N&MockObject */ - private $l10n; - /** @var IAppConfig&MockObject */ - private $appConfig; - /** @var JSignPdfHandler&MockObject */ - private $jSignPdfHandler; - /** @var SignSetupService&MockObject */ - private $signSetupService; - /** @var IURLGenerator&MockObject */ - private $urlGenerator; - /** @var IAppManager&MockObject */ - private $appManager; - /** @var LoggerInterface&MockObject */ - private $logger; - /** @var IConfig&MockObject */ - private $systemConfig; - /** @var JavaHelper&MockObject */ - private $javaHelper; - - /** @var JSignPdfSetupCheck */ - private $check; - - protected function setUp(): void - { - parent::setUp(); - FileSystemMock::$files = []; - - $this->l10n = $this->createMock(IL10N::class); - $this->appConfig = $this->createMock(IAppConfig::class); - $this->jSignPdfHandler = $this->createMock(JSignPdfHandler::class); - $this->signSetupService = $this->createMock(SignSetupService::class); - $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->appManager = $this->createMock(IAppManager::class); - $this->logger = $this->createMock(LoggerInterface::class); - $this->systemConfig = $this->createMock(IConfig::class); - $this->javaHelper = $this->createMock(JavaHelper::class); - - $this->check = new JSignPdfSetupCheck( - $this->l10n, - $this->appConfig, - $this->jSignPdfHandler, - $this->signSetupService, - $this->urlGenerator, - $this->appManager, - $this->logger, - $this->systemConfig, - $this->javaHelper - ); - } - - protected function tearDown(): void - { - FileSystemMock::$files = []; - parent::tearDown(); - } - - private function mockTranslation(): void - { - $this->l10n->method('t') - ->willReturnCallback(function ($text, $params = []) { - return vsprintf($text, $params); - }); - } - - public function testGetName(): void - { - $this->l10n->expects($this->once()) - ->method('t') - ->with('JSignPdf') - ->willReturn('JSignPdf'); - $this->assertSame('JSignPdf', $this->check->getName()); - } - - public function testGetCategory(): void - { - $this->assertSame('system', $this->check->getCategory()); - } - - public function testRunNoPathConfigured(): void - { - $this->mockTranslation(); - $this->appConfig->method('getValueString') - ->with('libresign', 'jsignpdf_jar_path') - ->willReturn(''); - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('JSignPdf not found', $result->getDescription()); - } - - public function testRunVerifyFails(): void - { - $this->mockTranslation(); - $this->appConfig->method('getValueString') - ->willReturn('/fake/path/jsignpdf.jar'); - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->expects($this->once()) - ->method('willUseLocalCert') - ->with(false); - $this->signSetupService->expects($this->once()) - ->method('verify') - ->with($this->anything(), 'jsignpdf') - ->willReturn(['SOME_ERROR' => 'details']); - - $this->logger->expects($this->once()) - ->method('error') - ->with('Invalid hash of binaries files', $this->anything()); - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('Invalid hash', $result->getDescription()); - } - - public function testRunBinaryNotFound(): void - { - $this->mockTranslation(); - $jarPath = '/fake/path/jsignpdf.jar'; - $this->appConfig->method('getValueString') - ->willReturn($jarPath); - $this->systemConfig->method('getSystemValueBool') - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify') - ->willReturn([]); - - FileSystemMock::$files[$jarPath] = false; - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('JSignPdf binary not found', $result->getDescription()); - } - - public function testRunJavaNotFound(): void - { - $this->mockTranslation(); - $jarPath = '/fake/path/jsignpdf.jar'; - $this->appConfig->method('getValueString') - ->willReturn($jarPath); - $this->systemConfig->method('getSystemValueBool') - ->willReturn(false); - $this->signSetupService->method('verify') - ->willReturn([]); - - FileSystemMock::$files[$jarPath] = true; - - $this->javaHelper->method('getJavaPath') - ->willReturn(''); - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('Necessary Java to run JSignPdf', $result->getDescription()); - } - - private function createJSignParamMock() - { - return $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\Sign\JSignParam::class) - ->disableOriginalConstructor() - ->getMock(); - } - - public function testRunVersionEmpty(): void - { - $this->mockTranslation(); - $jarPath = '/fake/path/jsignpdf.jar'; - $this->appConfig->method('getValueString') - ->willReturn($jarPath); - $this->systemConfig->method('getSystemValueBool') - ->willReturn(false); - $this->signSetupService->method('verify')->willReturn([]); - $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); - FileSystemMock::$files[$jarPath] = true; - FileSystemMock::$files['/usr/bin/java'] = true; - - $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) - ->disableOriginalConstructor() - ->onlyMethods(['setParam', 'getVersion']) - ->getMock(); - $jsignPdfMock->method('getVersion')->willReturn(''); - - $jsignParamMock = $this->createJSignParamMock(); - - $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); - $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('Necessary install the version', $result->getDescription()); - } - - public function testRunVersionTooLow(): void - { - $this->mockTranslation(); - $jarPath = '/fake/path/jsignpdf.jar'; - $this->appConfig->method('getValueString') - ->willReturn($jarPath); - $this->systemConfig->method('getSystemValueBool') - ->willReturn(false); - $this->signSetupService->method('verify')->willReturn([]); - $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); - FileSystemMock::$files[$jarPath] = true; - FileSystemMock::$files['/usr/bin/java'] = true; - - $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) - ->disableOriginalConstructor() - ->onlyMethods(['setParam', 'getVersion']) - ->getMock(); - $jsignPdfMock->method('getVersion')->willReturn('1.0.0'); - - $jsignParamMock = $this->createJSignParamMock(); - - $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); - $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('bump JSignPdf version', $result->getDescription()); - } - - public function testRunVersionTooHigh(): void - { - $this->mockTranslation(); - $jarPath = '/fake/path/jsignpdf.jar'; - $this->appConfig->method('getValueString') - ->willReturn($jarPath); - $this->systemConfig->method('getSystemValueBool') - ->willReturn(false); - $this->signSetupService->method('verify')->willReturn([]); - $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); - FileSystemMock::$files[$jarPath] = true; - FileSystemMock::$files['/usr/bin/java'] = true; - - $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) - ->disableOriginalConstructor() - ->onlyMethods(['setParam', 'getVersion']) - ->getMock(); - $jsignPdfMock->method('getVersion')->willReturn('10.0.0'); - - $jsignParamMock = $this->createJSignParamMock(); - - $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); - $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('downgrade JSignPdf version', $result->getDescription()); - } - - public function testRunSuccess(): void - { - $this->mockTranslation(); - $jarPath = '/fake/path/jsignpdf.jar'; - $this->appConfig->method('getValueString') - ->willReturn($jarPath); - $this->systemConfig->method('getSystemValueBool') - ->willReturn(false); - $this->signSetupService->method('verify')->willReturn([]); - $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); - FileSystemMock::$files[$jarPath] = true; - FileSystemMock::$files['/usr/bin/java'] = true; - - $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) - ->disableOriginalConstructor() - ->onlyMethods(['setParam', 'getVersion']) - ->getMock(); - $jsignPdfMock->method('getVersion')->willReturn(InstallService::JSIGNPDF_VERSION); - - $jsignParamMock = $this->createJSignParamMock(); - - $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); - $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); - - $result = $this->check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertSame('success', $result->getSeverity()); - $this->assertStringContainsString('JSignPdf version: ' . InstallService::JSIGNPDF_VERSION, $result->getDescription()); - $this->assertStringContainsString('JSignPdf path: ' . $jarPath, $result->getDescription()); - } +class JSignPdfSetupCheckTest extends TestCase { + /** @var IL10N&MockObject */ + private $l10n; + /** @var IAppConfig&MockObject */ + private $appConfig; + /** @var JSignPdfHandler&MockObject */ + private $jSignPdfHandler; + /** @var SignSetupService&MockObject */ + private $signSetupService; + /** @var IURLGenerator&MockObject */ + private $urlGenerator; + /** @var IAppManager&MockObject */ + private $appManager; + /** @var LoggerInterface&MockObject */ + private $logger; + /** @var IConfig&MockObject */ + private $systemConfig; + /** @var JavaHelper&MockObject */ + private $javaHelper; + + /** @var JSignPdfSetupCheck */ + private $check; + + protected function setUp(): void { + parent::setUp(); + FileSystemMock::$files = []; + + $this->l10n = $this->createMock(IL10N::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->jSignPdfHandler = $this->createMock(JSignPdfHandler::class); + $this->signSetupService = $this->createMock(SignSetupService::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->systemConfig = $this->createMock(IConfig::class); + $this->javaHelper = $this->createMock(JavaHelper::class); + + $this->check = new JSignPdfSetupCheck( + $this->l10n, + $this->appConfig, + $this->jSignPdfHandler, + $this->signSetupService, + $this->urlGenerator, + $this->appManager, + $this->logger, + $this->systemConfig, + $this->javaHelper + ); + } + + protected function tearDown(): void { + FileSystemMock::$files = []; + parent::tearDown(); + } + + private function mockTranslation(): void { + $this->l10n->method('t') + ->willReturnCallback(function ($text, $params = []) { + return vsprintf($text, $params); + }); + } + + public function testGetName(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('JSignPdf') + ->willReturn('JSignPdf'); + $this->assertSame('JSignPdf', $this->check->getName()); + } + + public function testGetCategory(): void { + $this->assertSame('system', $this->check->getCategory()); + } + + public function testRunNoPathConfigured(): void { + $this->mockTranslation(); + $this->appConfig->method('getValueString') + ->with('libresign', 'jsignpdf_jar_path') + ->willReturn(''); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('JSignPdf not found', $result->getDescription()); + } + + public function testRunVerifyFails(): void { + $this->mockTranslation(); + $this->appConfig->method('getValueString') + ->willReturn('/fake/path/jsignpdf.jar'); + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->expects($this->once()) + ->method('willUseLocalCert') + ->with(false); + $this->signSetupService->expects($this->once()) + ->method('verify') + ->with($this->anything(), 'jsignpdf') + ->willReturn(['SOME_ERROR' => 'details']); + + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', $this->anything()); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash', $result->getDescription()); + } + + public function testRunBinaryNotFound(): void { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify') + ->willReturn([]); + + FileSystemMock::$files[$jarPath] = false; + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('JSignPdf binary not found', $result->getDescription()); + } + + public function testRunJavaNotFound(): void { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify') + ->willReturn([]); + + FileSystemMock::$files[$jarPath] = true; + + $this->javaHelper->method('getJavaPath') + ->willReturn(''); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('Necessary Java to run JSignPdf', $result->getDescription()); + } + + private function createJSignParamMock() { + return $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\Sign\JSignParam::class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testRunVersionEmpty(): void { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn(''); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('Necessary install the version', $result->getDescription()); + } + + public function testRunVersionTooLow(): void { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn('1.0.0'); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('bump JSignPdf version', $result->getDescription()); + } + + public function testRunVersionTooHigh(): void { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn('10.0.0'); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('error', $result->getSeverity()); + $this->assertStringContainsString('downgrade JSignPdf version', $result->getDescription()); + } + + public function testRunSuccess(): void { + $this->mockTranslation(); + $jarPath = '/fake/path/jsignpdf.jar'; + $this->appConfig->method('getValueString') + ->willReturn($jarPath); + $this->systemConfig->method('getSystemValueBool') + ->willReturn(false); + $this->signSetupService->method('verify')->willReturn([]); + $this->javaHelper->method('getJavaPath')->willReturn('/usr/bin/java'); + FileSystemMock::$files[$jarPath] = true; + FileSystemMock::$files['/usr/bin/java'] = true; + + $jsignPdfMock = $this->getMockBuilder(\OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParam', 'getVersion']) + ->getMock(); + $jsignPdfMock->method('getVersion')->willReturn(InstallService::JSIGNPDF_VERSION); + + $jsignParamMock = $this->createJSignParamMock(); + + $this->jSignPdfHandler->method('getJSignPdf')->willReturn($jsignPdfMock); + $this->jSignPdfHandler->method('getJSignParam')->willReturn($jsignParamMock); + + $result = $this->check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertSame('success', $result->getSeverity()); + $this->assertStringContainsString('JSignPdf version: ' . InstallService::JSIGNPDF_VERSION, $result->getDescription()); + $this->assertStringContainsString('JSignPdf path: ' . $jarPath, $result->getDescription()); + } } diff --git a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php index 29debc9bfb..4c19e66ec1 100644 --- a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php @@ -9,470 +9,449 @@ namespace OCA\Libresign\Tests\Unit\SetupCheck; use OCA\Libresign\Helper\JavaHelper; -use OCA\Libresign\Service\Install\SignSetupService; use OCA\Libresign\Service\Install\InstallService; +use OCA\Libresign\Service\Install\SignSetupService; use OCA\Libresign\SetupCheck\JavaSetupCheck; -use OCA\Libresign\SetupCheck\FileSystemMock; -use OCA\Libresign\SetupCheck\ExecMock; -use OCP\IL10N; -use OCP\IURLGenerator; +use OCA\Libresign\Tests\Unit\SetupCheck\Mock\ExecMock; +use OCA\Libresign\Tests\Unit\SetupCheck\Mock\FileSystemMock; use OCP\App\IAppManager; use OCP\IConfig; -use Psr\Log\LoggerInterface; +use OCP\IL10N; +use OCP\IURLGenerator; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class JavaSetupCheckTest extends TestCase { + private IL10N&MockObject $l10n; + private JavaHelper&MockObject $javaHelper; + private SignSetupService&MockObject $signSetupService; + private IURLGenerator&MockObject $urlGenerator; + private IAppManager&MockObject $appManager; + private LoggerInterface&MockObject $logger; + private IConfig&MockObject $systemConfig; + + public function setUp(): void { + parent::setUp(); + FileSystemMock::$files = []; + ExecMock::$commands = []; + + $this->l10n = $this->createMock(IL10N::class); + $this->javaHelper = $this->createMock(JavaHelper::class); + $this->signSetupService = $this->createMock(SignSetupService::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->systemConfig = $this->createMock(IConfig::class); + } + + public function tearDown(): void { + FileSystemMock::$files = []; + ExecMock::$commands = []; + parent::tearDown(); + } + + private function getInstance(): JavaSetupCheck { + return new JavaSetupCheck( + $this->l10n, + $this->javaHelper, + $this->signSetupService, + $this->urlGenerator, + $this->appManager, + $this->logger, + $this->systemConfig + ); + } + + public function testJavaNotInstalled(): void { + $this->javaHelper->method('getJavaPath')->willReturn(''); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string) => $string); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('Java not installed', $result->getDescription()); + } + + public function testJavaBinaryNotFound(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = false; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Java binary not found', $result->getDescription()); + } + + public function testJavaVersionInvalid(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => ['openjdk version "11.0.18"'], + 'result_code' => 0 + ]; + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = UTF-8'], + 'result_code' => 0 + ]; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid java version', $result->getDescription()); + } + + public function testJavaSuccess(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = UTF-8'], + 'result_code' => 0 + ]; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('success', $result->getSeverity()); + $this->assertStringContainsString('Java version: ' . InstallService::JAVA_VERSION, $result->getDescription()); + $this->assertStringContainsString('Java binary: ' . $javaPath, $result->getDescription()); + } + + public function testJavaVerifyResourceIntegrityFails(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with($this->anything(), 'java') + ->willReturn($verifyResult); + $this->signSetupService->method('willUseLocalCert'); + + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', ['result' => $verifyResult]); + + $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(false); + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + } + + public function testJavaExecutionFails(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => ['some error output'], + 'result_code' => 1 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertEquals('Failure to check Java version.', $result->getDescription()); + } + + public function testJavaEmptyOutput(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [], + 'result_code' => 0 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Failed to execute Java', $result->getDescription()); + } + + public function testJavaEncodingNotFound(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['alguma outra coisa'], + 'result_code' => 0 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Java encoding not found', $result->getDescription()); + } + + public function testJavaEncodingNonUtf8(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $this->signSetupService->method('verify')->willReturn([]); + $this->signSetupService->method('willUseLocalCert'); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = ISO-8859-1'], + 'result_code' => 0 + ]; + + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('Non-UTF-8 encoding detected', $result->getDescription()); + $this->assertStringContainsString('ISO-8859-1', $result->getDescription()); + } + + public function testGetName(): void { + $this->l10n->method('t')->willReturnArgument(0); + $instance = $this->getInstance(); + $this->assertEquals('Java', $instance->getName()); + } + + public function testGetCategory(): void { + $instance = $this->getInstance(); + $this->assertEquals('system', $instance->getCategory()); + } + + public function testVerifyResourceIntegritySignatureDataNotFoundWithoutDebug(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->method('t')->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Signature data not found', $result->getDescription()); + $this->assertStringContainsString('running from source code', $result->getLinkToDoc()); + } + + public function testVerifyResourceIntegrityEmptySignatureDataWithoutDebug(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['EMPTY_SIGNATURE_DATA' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->signSetupService->method('willUseLocalCert'); + + $this->l10n->method('t')->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Your signature data is empty', $result->getDescription()); + } + + public function testVerifyResourceIntegritySignatureDataNotFoundWithDebug(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(true); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ + 'output' => [InstallService::JAVA_VERSION], + 'result_code' => 0 + ]; + ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ + 'output' => ['native.encoding = UTF-8'], + 'result_code' => 0 + ]; + + $this->l10n->method('t')->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('success', $result->getSeverity()); + } + + public function testVerifyResourceIntegrityHashFileErrorWithDebug(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(true); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->l10n->method('t')->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + $this->assertStringContainsString('Debug mode is enabled', $result->getLinkToDoc()); + } + + public function testVerifyResourceIntegrityHashFileErrorWithoutDebugWithLogReader(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); + + $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(true); + + $this->urlGenerator->method('linkToRouteAbsolute') + ->with('settings.adminsettings.form', ['section' => 'logging']) + ->willReturn('https://example.com/settings/logging'); + + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', $this->anything()); + + $this->l10n->method('t')->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + $this->assertStringContainsString('Check your nextcloud.log file on', $result->getLinkToDoc()); + } + + public function testVerifyResourceIntegrityHashFileErrorWithoutDebugAndNoLogReader(): void { + $javaPath = '/fake/java'; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); + FileSystemMock::$files[$javaPath] = true; + + $verifyResult = ['HASH_FILE_ERROR' => true]; + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'java') + ->willReturn($verifyResult); -class JavaSetupCheckTest extends TestCase -{ - private IL10N&MockObject $l10n; - private JavaHelper&MockObject $javaHelper; - private SignSetupService&MockObject $signSetupService; - private IURLGenerator&MockObject $urlGenerator; - private IAppManager&MockObject $appManager; - private LoggerInterface&MockObject $logger; - private IConfig&MockObject $systemConfig; - - public function setUp(): void - { - parent::setUp(); - FileSystemMock::$files = []; - ExecMock::$commands = []; - - $this->l10n = $this->createMock(IL10N::class); - $this->javaHelper = $this->createMock(JavaHelper::class); - $this->signSetupService = $this->createMock(SignSetupService::class); - $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->appManager = $this->createMock(IAppManager::class); - $this->logger = $this->createMock(LoggerInterface::class); - $this->systemConfig = $this->createMock(IConfig::class); - } - - public function tearDown(): void - { - FileSystemMock::$files = []; - ExecMock::$commands = []; - parent::tearDown(); - } - - private function getInstance(): JavaSetupCheck - { - return new JavaSetupCheck( - $this->l10n, - $this->javaHelper, - $this->signSetupService, - $this->urlGenerator, - $this->appManager, - $this->logger, - $this->systemConfig - ); - } - - public function testJavaNotInstalled(): void - { - $this->javaHelper->method('getJavaPath')->willReturn(''); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string) => $string); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertEquals('Java not installed', $result->getDescription()); - } - - public function testJavaBinaryNotFound(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = false; - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Java binary not found', $result->getDescription()); - } - - public function testJavaVersionInvalid(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ - 'output' => ['openjdk version "11.0.18"'], - 'result_code' => 0 - ]; - ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ - 'output' => ['native.encoding = UTF-8'], - 'result_code' => 0 - ]; - - $this->signSetupService->method('verify')->willReturn([]); - $this->signSetupService->method('willUseLocalCert'); - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Invalid java version', $result->getDescription()); - } - - public function testJavaSuccess(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ - 'output' => [InstallService::JAVA_VERSION], - 'result_code' => 0 - ]; - ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ - 'output' => ['native.encoding = UTF-8'], - 'result_code' => 0 - ]; - - $this->signSetupService->method('verify')->willReturn([]); - $this->signSetupService->method('willUseLocalCert'); - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('success', $result->getSeverity()); - $this->assertStringContainsString('Java version: ' . InstallService::JAVA_VERSION, $result->getDescription()); - $this->assertStringContainsString('Java binary: ' . $javaPath, $result->getDescription()); - } - - public function testJavaVerifyResourceIntegrityFails(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $verifyResult = ['HASH_FILE_ERROR' => true]; - $this->signSetupService->method('verify') - ->with($this->anything(), 'java') - ->willReturn($verifyResult); - $this->signSetupService->method('willUseLocalCert'); - - $this->logger->expects($this->once()) - ->method('error') - ->with('Invalid hash of binaries files', ['result' => $verifyResult]); - - $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(false); - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); - } - - public function testJavaExecutionFails(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $this->signSetupService->method('verify')->willReturn([]); - $this->signSetupService->method('willUseLocalCert'); - - ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ - 'output' => ['some error output'], - 'result_code' => 1 - ]; - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertEquals('Failure to check Java version.', $result->getDescription()); - } - - public function testJavaEmptyOutput(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $this->signSetupService->method('verify')->willReturn([]); - $this->signSetupService->method('willUseLocalCert'); - - ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ - 'output' => [], - 'result_code' => 0 - ]; - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Failed to execute Java', $result->getDescription()); - } - - public function testJavaEncodingNotFound(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $this->signSetupService->method('verify')->willReturn([]); - $this->signSetupService->method('willUseLocalCert'); - - ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ - 'output' => [InstallService::JAVA_VERSION], - 'result_code' => 0 - ]; - - ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ - 'output' => ['alguma outra coisa'], - 'result_code' => 0 - ]; - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Java encoding not found', $result->getDescription()); - } - - public function testJavaEncodingNonUtf8(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $this->signSetupService->method('verify')->willReturn([]); - $this->signSetupService->method('willUseLocalCert'); - - ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ - 'output' => [InstallService::JAVA_VERSION], - 'result_code' => 0 - ]; - - ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ - 'output' => ['native.encoding = ISO-8859-1'], - 'result_code' => 0 - ]; - - $this->l10n->expects($this->any()) - ->method('t') - ->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('info', $result->getSeverity()); - $this->assertStringContainsString('Non-UTF-8 encoding detected', $result->getDescription()); - $this->assertStringContainsString('ISO-8859-1', $result->getDescription()); - } - - public function testGetName(): void - { - $this->l10n->method('t')->willReturnArgument(0); - $instance = $this->getInstance(); - $this->assertEquals('Java', $instance->getName()); - } - - public function testGetCategory(): void - { - $instance = $this->getInstance(); - $this->assertEquals('system', $instance->getCategory()); - } - - public function testVerifyResourceIntegritySignatureDataNotFoundWithoutDebug(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; - $this->signSetupService->method('verify') - ->with(php_uname('m'), 'java') - ->willReturn($verifyResult); - - $this->signSetupService->method('willUseLocalCert'); - - $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Signature data not found', $result->getDescription()); - $this->assertStringContainsString('running from source code', $result->getLinkToDoc()); - } - - public function testVerifyResourceIntegrityEmptySignatureDataWithoutDebug(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $verifyResult = ['EMPTY_SIGNATURE_DATA' => true]; - $this->signSetupService->method('verify') - ->with(php_uname('m'), 'java') - ->willReturn($verifyResult); - - $this->signSetupService->method('willUseLocalCert'); - - $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Your signature data is empty', $result->getDescription()); - } - - public function testVerifyResourceIntegritySignatureDataNotFoundWithDebug(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(true); - FileSystemMock::$files[$javaPath] = true; - - $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; - $this->signSetupService->method('verify') - ->with(php_uname('m'), 'java') - ->willReturn($verifyResult); - - ExecMock::$commands[$javaPath . ' -version 2>&1'] = [ - 'output' => [InstallService::JAVA_VERSION], - 'result_code' => 0 - ]; - ExecMock::$commands[$javaPath . ' -XshowSettings:properties -version 2>&1'] = [ - 'output' => ['native.encoding = UTF-8'], - 'result_code' => 0 - ]; - - $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('success', $result->getSeverity()); - } - - public function testVerifyResourceIntegrityHashFileErrorWithDebug(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(true); - FileSystemMock::$files[$javaPath] = true; - - $verifyResult = ['HASH_FILE_ERROR' => true]; - $this->signSetupService->method('verify') - ->with(php_uname('m'), 'java') - ->willReturn($verifyResult); - - $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); - $this->assertStringContainsString('Debug mode is enabled', $result->getLinkToDoc()); - } - - public function testVerifyResourceIntegrityHashFileErrorWithoutDebugWithLogReader(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $verifyResult = ['HASH_FILE_ERROR' => true]; - $this->signSetupService->method('verify') - ->with(php_uname('m'), 'java') - ->willReturn($verifyResult); - - $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(true); + $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(false); - $this->urlGenerator->method('linkToRouteAbsolute') - ->with('settings.adminsettings.form', ['section' => 'logging']) - ->willReturn('https://example.com/settings/logging'); - - $this->logger->expects($this->once()) - ->method('error') - ->with('Invalid hash of binaries files', $this->anything()); - - $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); - $this->assertStringContainsString('Check your nextcloud.log file on', $result->getLinkToDoc()); - } - - public function testVerifyResourceIntegrityHashFileErrorWithoutDebugAndNoLogReader(): void - { - $javaPath = '/fake/java'; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - $this->systemConfig->method('getSystemValueBool')->with('debug', false)->willReturn(false); - FileSystemMock::$files[$javaPath] = true; - - $verifyResult = ['HASH_FILE_ERROR' => true]; - $this->signSetupService->method('verify') - ->with(php_uname('m'), 'java') - ->willReturn($verifyResult); - - $this->appManager->method('isEnabledForUser')->with('logreader')->willReturn(false); - - $this->logger->expects($this->once()) - ->method('error') - ->with('Invalid hash of binaries files', $this->anything()); - - $this->l10n->method('t')->willReturnCallback(fn($string, $params = []) => vsprintf($string, $params)); - - $instance = $this->getInstance(); - $result = $instance->run(); - - $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); - $this->assertStringContainsString('Check your nextcloud.log file', $result->getLinkToDoc()); - } + $this->logger->expects($this->once()) + ->method('error') + ->with('Invalid hash of binaries files', $this->anything()); + + $this->l10n->method('t')->willReturnCallback(fn ($string, $params = []) => vsprintf($string, $params)); + + $instance = $this->getInstance(); + $result = $instance->run(); + + $this->assertEquals('error', $result->getSeverity()); + $this->assertStringContainsString('Invalid hash of binaries files', $result->getDescription()); + $this->assertStringContainsString('Check your nextcloud.log file', $result->getLinkToDoc()); + } } diff --git a/tests/php/Unit/SetupCheck/ExecMock.php b/tests/php/Unit/SetupCheck/Mock/ExecMock.php similarity index 65% rename from tests/php/Unit/SetupCheck/ExecMock.php rename to tests/php/Unit/SetupCheck/Mock/ExecMock.php index 0ae9c343cc..1126f99a2f 100644 --- a/tests/php/Unit/SetupCheck/ExecMock.php +++ b/tests/php/Unit/SetupCheck/Mock/ExecMock.php @@ -7,8 +7,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCA\Libresign\SetupCheck; +namespace OCA\Libresign\Tests\Unit\SetupCheck\Mock; class ExecMock { - public static array $commands = []; + public static array $commands = []; } diff --git a/tests/php/Unit/SetupCheck/Mock/FileSystemMock.php b/tests/php/Unit/SetupCheck/Mock/FileSystemMock.php new file mode 100644 index 0000000000..e5a3e077f9 --- /dev/null +++ b/tests/php/Unit/SetupCheck/Mock/FileSystemMock.php @@ -0,0 +1,9 @@ +l10n = $this->createMock(IL10N::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->signSetupService = $this->createMock(SignSetupService::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->systemConfig = $this->createMock(IConfig::class); + $this->javaHelper = $this->createMock(JavaHelper::class); + + $this->l10n->method('t') + ->willReturnCallback(function ($text, $parameters = []) { + return vsprintf($text, $parameters); + }); + + FileSystemMock::$files = []; + ExecMock::$commands = []; + } + + protected function tearDown(): void { + parent::tearDown(); + FileSystemMock::$files = []; + ExecMock::$commands = []; + } + + private function getInstance(): PDFtkSetupCheck { + return new PDFtkSetupCheck( + $this->l10n, + $this->appConfig, + $this->signSetupService, + $this->urlGenerator, + $this->appManager, + $this->logger, + $this->systemConfig, + $this->javaHelper + ); + } + + public function testGetName(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('PDFtk') + ->willReturn('PDFtk'); + $check = $this->getInstance(); + $this->assertEquals('PDFtk', $check->getName()); + } + + public function testGetCategory(): void { + $check = $this->getInstance(); + $this->assertEquals('system', $check->getCategory()); + } + + public function testRunNoPdftkPath(): void { + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn(''); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk not found', $result->getDescription()); + } + + public function testRunWithVerifyError(): void { + $pdftkPath = '/fake/pdftk.jar'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $debugEnabled = false; + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn($debugEnabled); + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + + $this->signSetupService->expects($this->once()) + ->method('willUseLocalCert') + ->with($debugEnabled); + $this->signSetupService->expects($this->once()) + ->method('verify') + ->with(php_uname('m'), 'pdftk') + ->willReturn($verifyResult); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Signature data not found', $result->getDescription()); + } + + public function testRunFileNotExists(): void { + $pdftkPath = '/fake/pdftk.jar'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify') + ->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = false; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk binary not found', $result->getDescription()); + } + + public function testRunJavaMissing(): void { + $pdftkPath = '/fake/pdftk.jar'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn(''); + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Necessary Java to run PDFtk', $result->getDescription()); + } + + public function testRunJavaPathExistsButFileNotExists(): void { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + FileSystemMock::$files[$javaPath] = false; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Necessary Java to run PDFtk', $result->getDescription()); + } + + public function testRunExecFailure(): void { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => [], + 'result_code' => 1, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Failure to check PDFtk version', $result->getDescription()); + } + + public function testRunInvalidBinary(): void { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ['unexpected output'], + 'result_code' => 0, + ]; -class PDFtkSetupCheckTest extends TestCase -{ - /** @var IL10N|MockObject */ - private $l10n; - /** @var IAppConfig|MockObject */ - private $appConfig; - /** @var SignSetupService|MockObject */ - private $signSetupService; - /** @var IURLGenerator|MockObject */ - private $urlGenerator; - /** @var IAppManager|MockObject */ - private $appManager; - /** @var LoggerInterface|MockObject */ - private $logger; - /** @var IConfig|MockObject */ - private $systemConfig; - /** @var JavaHelper|MockObject */ - private $javaHelper; - - protected function setUp(): void - { - parent::setUp(); - - $this->l10n = $this->createMock(IL10N::class); - $this->appConfig = $this->createMock(IAppConfig::class); - $this->signSetupService = $this->createMock(SignSetupService::class); - $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->appManager = $this->createMock(IAppManager::class); - $this->logger = $this->createMock(LoggerInterface::class); - $this->systemConfig = $this->createMock(IConfig::class); - $this->javaHelper = $this->createMock(JavaHelper::class); - - $this->l10n->method('t') - ->willReturnCallback(function ($text, $parameters = []) { - return vsprintf($text, $parameters); - }); - - FileSystemMock::$files = []; - ExecMock::$commands = []; - } - - protected function tearDown(): void - { - parent::tearDown(); - FileSystemMock::$files = []; - ExecMock::$commands = []; - } - - private function getInstance(): PDFtkSetupCheck - { - return new PDFtkSetupCheck( - $this->l10n, - $this->appConfig, - $this->signSetupService, - $this->urlGenerator, - $this->appManager, - $this->logger, - $this->systemConfig, - $this->javaHelper - ); - } - - public function testGetName(): void - { - $this->l10n->expects($this->once()) - ->method('t') - ->with('PDFtk') - ->willReturn('PDFtk'); - $check = $this->getInstance(); - $this->assertEquals('PDFtk', $check->getName()); - } - - public function testGetCategory(): void - { - $check = $this->getInstance(); - $this->assertEquals('system', $check->getCategory()); - } - - public function testRunNoPdftkPath(): void - { - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn(''); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('PDFtk not found', $result->getDescription()); - } - - public function testRunWithVerifyError(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $debugEnabled = false; - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn($debugEnabled); - - $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; - - $this->signSetupService->expects($this->once()) - ->method('willUseLocalCert') - ->with($debugEnabled); - $this->signSetupService->expects($this->once()) - ->method('verify') - ->with(php_uname('m'), 'pdftk') - ->willReturn($verifyResult); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('Signature data not found', $result->getDescription()); - } - - public function testRunFileNotExists(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify') - ->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = false; - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('PDFtk binary not found', $result->getDescription()); - } - - public function testRunJavaMissing(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify')->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = true; - - $this->javaHelper->method('getJavaPath')->willReturn(''); - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('Necessary Java to run PDFtk', $result->getDescription()); - } - - public function testRunJavaPathExistsButFileNotExists(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $javaPath = '/fake/java'; - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify')->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = true; - - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - FileSystemMock::$files[$javaPath] = false; - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('Necessary Java to run PDFtk', $result->getDescription()); - } - - public function testRunExecFailure(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $javaPath = '/fake/java'; - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify')->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = true; - FileSystemMock::$files[$javaPath] = true; - - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - - $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; - ExecMock::$commands[$expectedCommand] = [ - 'output' => [], - 'result_code' => 1, - ]; + $check = $this->getInstance(); + $result = $check->run(); - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('Failure to check PDFtk version', $result->getDescription()); - } + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk binary is invalid', $result->getDescription()); + } + + public function testRunVersionMismatch(): void { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $wrongVersion = '2.0.0'; + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; - public function testRunInvalidBinary(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $javaPath = '/fake/java'; - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify')->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = true; - FileSystemMock::$files[$javaPath] = true; - - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - - $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; - ExecMock::$commands[$expectedCommand] = [ - 'output' => ['unexpected output'], - 'result_code' => 0, - ]; - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('PDFtk binary is invalid', $result->getDescription()); - } - - public function testRunVersionMismatch(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $javaPath = '/fake/java'; - $wrongVersion = '2.0.0'; - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify')->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = true; - FileSystemMock::$files[$javaPath] = true; - - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - - $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; - ExecMock::$commands[$expectedCommand] = [ - 'output' => ["pdftk port to java $wrongVersion a Handy Tool"], - 'result_code' => 0, - ]; - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('Necessary install the version', $result->getDescription()); - } - - public function testRunSuccess(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $javaPath = '/fake/java'; - $expectedVersion = InstallService::PDFTK_VERSION; - - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify')->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = true; - FileSystemMock::$files[$javaPath] = true; - - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - - $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; - ExecMock::$commands[$expectedCommand] = [ - 'output' => ["pdftk port to java $expectedVersion a Handy Tool"], - 'result_code' => 0, - ]; - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); - $this->assertStringContainsString("PDFtk version: $expectedVersion", $result->getDescription()); - $this->assertStringContainsString("PDFtk path: $pdftkPath", $result->getDescription()); - } - - public function testRunWithDebugIgnoresSignatureError(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $javaPath = '/fake/java'; - $expectedVersion = InstallService::PDFTK_VERSION; - - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(true); - - $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; - $this->signSetupService->method('willUseLocalCert')->with(true); - $this->signSetupService->method('verify') - ->with(php_uname('m'), 'pdftk') - ->willReturn($verifyResult); - - FileSystemMock::$files[$pdftkPath] = true; - FileSystemMock::$files[$javaPath] = true; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - - $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; - ExecMock::$commands[$expectedCommand] = [ - 'output' => ["pdftk port to java $expectedVersion a Handy Tool"], - 'result_code' => 0, - ]; - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); - } - - public function testRunEmptyCommandOutput(): void - { - $pdftkPath = '/fake/pdftk.jar'; - $javaPath = '/fake/java'; - - $this->appConfig->method('getValueString') - ->with('libresign', 'pdftk_path') - ->willReturn($pdftkPath); - - $this->systemConfig->method('getSystemValueBool') - ->with('debug', false) - ->willReturn(false); - - $this->signSetupService->method('willUseLocalCert'); - $this->signSetupService->method('verify')->willReturn([]); - - FileSystemMock::$files[$pdftkPath] = true; - FileSystemMock::$files[$javaPath] = true; - $this->javaHelper->method('getJavaPath')->willReturn($javaPath); - - $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; - ExecMock::$commands[$expectedCommand] = [ - 'output' => [], - 'result_code' => 0, - ]; - - $check = $this->getInstance(); - $result = $check->run(); - - $this->assertInstanceOf(SetupResult::class, $result); - $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('PDFtk binary is invalid', $result->getDescription()); - } + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ["pdftk port to java $wrongVersion a Handy Tool"], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('Necessary install the version', $result->getDescription()); + } + + public function testRunSuccess(): void { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $expectedVersion = InstallService::PDFTK_VERSION; + + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ["pdftk port to java $expectedVersion a Handy Tool"], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); + $this->assertStringContainsString("PDFtk version: $expectedVersion", $result->getDescription()); + $this->assertStringContainsString("PDFtk path: $pdftkPath", $result->getDescription()); + } + + public function testRunWithDebugIgnoresSignatureError(): void { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + $expectedVersion = InstallService::PDFTK_VERSION; + + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(true); + + $verifyResult = ['SIGNATURE_DATA_NOT_FOUND' => true]; + $this->signSetupService->method('willUseLocalCert')->with(true); + $this->signSetupService->method('verify') + ->with(php_uname('m'), 'pdftk') + ->willReturn($verifyResult); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => ["pdftk port to java $expectedVersion a Handy Tool"], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); + } + + public function testRunEmptyCommandOutput(): void { + $pdftkPath = '/fake/pdftk.jar'; + $javaPath = '/fake/java'; + + $this->appConfig->method('getValueString') + ->with('libresign', 'pdftk_path') + ->willReturn($pdftkPath); + + $this->systemConfig->method('getSystemValueBool') + ->with('debug', false) + ->willReturn(false); + + $this->signSetupService->method('willUseLocalCert'); + $this->signSetupService->method('verify')->willReturn([]); + + FileSystemMock::$files[$pdftkPath] = true; + FileSystemMock::$files[$javaPath] = true; + $this->javaHelper->method('getJavaPath')->willReturn($javaPath); + + $expectedCommand = $javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1'; + ExecMock::$commands[$expectedCommand] = [ + 'output' => [], + 'result_code' => 0, + ]; + + $check = $this->getInstance(); + $result = $check->run(); + + $this->assertInstanceOf(SetupResult::class, $result); + $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); + $this->assertStringContainsString('PDFtk binary is invalid', $result->getDescription()); + } } diff --git a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php index db665faa8a..58e69c5ff7 100644 --- a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php @@ -9,155 +9,145 @@ namespace OCA\Libresign\Tests\Unit\SetupCheck; use OCA\Libresign\SetupCheck\PopplerSetupCheck; -use OCA\Libresign\SetupCheck\ExecMock; +use OCA\Libresign\Tests\Unit\SetupCheck\Mock\ExecMock; use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -class PopplerSetupCheckTest extends TestCase -{ - /** @var IL10N|MockObject */ - private $l10n; - - /** @var PopplerSetupCheck */ - private $check; - - protected function setUp(): void - { - parent::setUp(); - - ExecMock::$commands = []; - - $this->l10n = $this->createMock(IL10N::class); - $this->l10n->method('t') - ->willReturnCallback(fn($text, $params = []) => vsprintf($text, $params)); - - $this->check = new PopplerSetupCheck($this->l10n); - } - - public function testBothToolsInstalled(): void - { - ExecMock::$commands['pdfsig -v 2>&1'] = [ - 'output' => ['pdfsig version 25.03.0', 'extra line'], - 'result_code' => 0, - ]; - ExecMock::$commands['pdfinfo -v 2>&1'] = [ - 'output' => ['pdfinfo version 25.03.0'], - 'result_code' => 0, - ]; - - $result = $this->check->run(); - - $this->assertEquals('success', $result->getSeverity()); - $this->assertEquals( - 'pdfsig version: 25.03.0, pdfinfo version: 25.03.0', - $result->getDescription() - ); - } - - public function testOnlyPdfsigInstalled(): void - { - ExecMock::$commands['pdfsig -v 2>&1'] = [ - 'output' => ['pdfsig version 25.03.0'], - 'result_code' => 0, - ]; - ExecMock::$commands['pdfinfo -v 2>&1'] = [ - 'output' => [], - 'result_code' => 127, - ]; - - $result = $this->check->run(); - - $this->assertEquals('info', $result->getSeverity()); - $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); - $this->assertStringContainsString('Install the package poppler-utils', $result->getLinkToDoc()); - } - - public function testNoToolsInstalled(): void - { - ExecMock::$commands['pdfsig -v 2>&1'] = [ - 'output' => [], - 'result_code' => 127, - ]; - ExecMock::$commands['pdfinfo -v 2>&1'] = [ - 'output' => [], - 'result_code' => 127, - ]; - - $result = $this->check->run(); - - $this->assertEquals('info', $result->getSeverity()); - $this->assertStringContainsString( - 'pdfsig not installed or not working; pdfinfo not installed or not working', - $result->getDescription() - ); - } - - public function testPdfsigVersionParseFails(): void - { - ExecMock::$commands['pdfsig -v 2>&1'] = [ - 'output' => ['pdfsig version 25.03.0'], - 'result_code' => 0, - ]; - ExecMock::$commands['pdfinfo -v 2>&1'] = [ - 'output' => ['pdfinfo: unknown option'], - 'result_code' => 0, - ]; - - $result = $this->check->run(); - - $this->assertEquals('info', $result->getSeverity()); - $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); - $this->assertStringNotContainsString('pdfsig', $result->getDescription()); - } - - public function testOnlyPdfinfoInstalledButParseFails(): void - { - ExecMock::$commands['pdfsig -v 2>&1'] = [ - 'output' => [], - 'result_code' => 127, - ]; - ExecMock::$commands['pdfinfo -v 2>&1'] = [ - 'output' => ['pdfinfo: unknown option'], - 'result_code' => 0, - ]; - - $result = $this->check->run(); - - $this->assertEquals('info', $result->getSeverity()); - $this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription()); - $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); - } - - public function testBothToolsParseFails(): void - { - ExecMock::$commands['pdfsig -v 2>&1'] = [ - 'output' => ['pdfsig: error'], - 'result_code' => 0, - ]; - ExecMock::$commands['pdfinfo -v 2>&1'] = [ - 'output' => ['pdfinfo: error'], - 'result_code' => 0, - ]; - - $result = $this->check->run(); - - $this->assertEquals('info', $result->getSeverity()); - $this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription()); - $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); - } - - public function testGetName(): void - { - $this->l10n->expects($this->once()) - ->method('t') - ->with('Poppler utils') - ->willReturn('Poppler utils'); - $this->assertEquals('Poppler utils', $this->check->getName()); - } - - public function testGetCategory(): void - { - $this->assertEquals('system', $this->check->getCategory()); - } +class PopplerSetupCheckTest extends TestCase { + /** @var IL10N|MockObject */ + private $l10n; + + /** @var PopplerSetupCheck */ + private $check; + + protected function setUp(): void { + parent::setUp(); + + ExecMock::$commands = []; + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t') + ->willReturnCallback(fn ($text, $params = []) => vsprintf($text, $params)); + + $this->check = new PopplerSetupCheck($this->l10n); + } + + public function testBothToolsInstalled(): void { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig version 25.03.0', 'extra line'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo version 25.03.0'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('success', $result->getSeverity()); + $this->assertEquals( + 'pdfsig version: 25.03.0, pdfinfo version: 25.03.0', + $result->getDescription() + ); + } + + public function testOnlyPdfsigInstalled(): void { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig version 25.03.0'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + $this->assertStringContainsString('Install the package poppler-utils', $result->getLinkToDoc()); + } + + public function testNoToolsInstalled(): void { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString( + 'pdfsig not installed or not working; pdfinfo not installed or not working', + $result->getDescription() + ); + } + + public function testPdfsigVersionParseFails(): void { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig version 25.03.0'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo: unknown option'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + $this->assertStringNotContainsString('pdfsig', $result->getDescription()); + } + + public function testOnlyPdfinfoInstalledButParseFails(): void { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => [], + 'result_code' => 127, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo: unknown option'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + } + + public function testBothToolsParseFails(): void { + ExecMock::$commands['pdfsig -v 2>&1'] = [ + 'output' => ['pdfsig: error'], + 'result_code' => 0, + ]; + ExecMock::$commands['pdfinfo -v 2>&1'] = [ + 'output' => ['pdfinfo: error'], + 'result_code' => 0, + ]; + + $result = $this->check->run(); + + $this->assertEquals('info', $result->getSeverity()); + $this->assertStringContainsString('pdfsig not installed or not working', $result->getDescription()); + $this->assertStringContainsString('pdfinfo not installed or not working', $result->getDescription()); + } + + public function testGetName(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Poppler utils') + ->willReturn('Poppler utils'); + $this->assertEquals('Poppler utils', $this->check->getName()); + } + + public function testGetCategory(): void { + $this->assertEquals('system', $this->check->getCategory()); + } } diff --git a/tests/php/bootstrap.php b/tests/php/bootstrap.php index 1e2aa208ef..d721533ba7 100644 --- a/tests/php/bootstrap.php +++ b/tests/php/bootstrap.php @@ -17,10 +17,9 @@ require_once __DIR__ . '/../../../../lib/base.php'; require_once __DIR__ . '/../../../../tests/autoload.php'; -require_once __DIR__ . '/Unit/SetupCheck/FileSystemMock.php'; -require_once __DIR__ . '/Unit/SetupCheck/ExecMock.php'; - -require_once __DIR__ . '/SetupCheckFunctions.php'; +require_once __DIR__ . '/Unit/SetupCheck/Mock/FileSystemMock.php'; +require_once __DIR__ . '/Unit/SetupCheck/Mock/ExecMock.php'; +require_once __DIR__ . '/Unit/SetupCheck/Mock/functions.php'; \OC::$composerAutoloader->addPsr4('Test\\', OC::$SERVERROOT . '/tests/php/lib/', true); \OC::$composerAutoloader->addPsr4('Tests\\', OC::$SERVERROOT . '/tests/php/', true); From 4e6f90bfd0d2b45a94f7c611b03d2b49e6e25c5c Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Thu, 5 Mar 2026 19:31:00 +0000 Subject: [PATCH 12/68] style(setup-check): apply code style fixes and improve Psalm annotations This commit applies automated code style fixes using `composer cs:fix` and addresses issues identified by Psalm static analysis across the SetupCheck classes and their related tests. The changes include: - Normalizing indentation (tabs vs spaces) and adding missing trailing commas in argument lists across all SetupCheck classes and `SetupCheckUtils.php`. - Fixing the formatting of the `@deprecated` comment in `ConfigureCheckService.php`. - Adding `#[\Override]` attributes to all methods implementing `ISetupCheck`. Related issue: #6590 Type: Maintenance Checklist: - [x] Run `composer cs:fix` to apply coding standards - [x] Address Psalm static analysis findings - [x] Add `#[\Override]` attributes to interface methods - [x] Fix deprecation comment formatting Signed-off-by: Guilherme Carvalho --- lib/Service/Install/ConfigureCheckService.php | 6 +- .../CertificateEngineSetupCheck.php | 133 +++++---- lib/SetupCheck/ImagickSetupCheck.php | 3 + lib/SetupCheck/JSignPdfSetupCheck.php | 225 ++++++++------- lib/SetupCheck/JavaSetupCheck.php | 269 +++++++++--------- lib/SetupCheck/PDFtkSetupCheck.php | 23 +- lib/SetupCheck/PopplerSetupCheck.php | 3 + lib/SetupCheck/SetupCheckUtils.php | 111 ++++---- 8 files changed, 388 insertions(+), 385 deletions(-) diff --git a/lib/Service/Install/ConfigureCheckService.php b/lib/Service/Install/ConfigureCheckService.php index 9e482b1f1d..30b2f069fb 100644 --- a/lib/Service/Install/ConfigureCheckService.php +++ b/lib/Service/Install/ConfigureCheckService.php @@ -21,9 +21,9 @@ use Psr\Log\LoggerInterface; /** -* @deprecated 13.0.4 Use the individual SetupCheck classes instead -* (JavaSetupCheck, JSignPdfSetupCheck, etc.). -*/ + * @deprecated 13.0.4 Use the individual SetupCheck classes instead + * (JavaSetupCheck, JSignPdfSetupCheck, etc.). + */ class ConfigureCheckService { private string $architecture; private bool $isCacheDisabled = false; diff --git a/lib/SetupCheck/CertificateEngineSetupCheck.php b/lib/SetupCheck/CertificateEngineSetupCheck.php index 78dd75e183..34ffdc4f5c 100644 --- a/lib/SetupCheck/CertificateEngineSetupCheck.php +++ b/lib/SetupCheck/CertificateEngineSetupCheck.php @@ -14,81 +14,80 @@ use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -class CertificateEngineSetupCheck implements ISetupCheck -{ - private IL10N $l10n; - private CertificateEngineFactory $certificateEngineFactory; +class CertificateEngineSetupCheck implements ISetupCheck { + private IL10N $l10n; + private CertificateEngineFactory $certificateEngineFactory; - public function __construct( - IL10N $l10n, - CertificateEngineFactory $certificateEngineFactory - ) { - $this->l10n = $l10n; - $this->certificateEngineFactory = $certificateEngineFactory; - } + public function __construct( + IL10N $l10n, + CertificateEngineFactory $certificateEngineFactory, + ) { + $this->l10n = $l10n; + $this->certificateEngineFactory = $certificateEngineFactory; + } - public function getName(): string - { - return $this->l10n->t('Certificate engine'); - } + #[\Override] + public function getName(): string { + return $this->l10n->t('Certificate engine'); + } - public function getCategory(): string - { - return 'security'; - } + #[\Override] + public function getCategory(): string { + return 'security'; + } - public function run(): SetupResult - { - try { - $engine = $this->certificateEngineFactory->getEngine(); - $checkResults = $engine->configureCheck(); - } catch (\Throwable $e) { - $engineName = $this->certificateEngineFactory->getEngine()->getName() ?? 'unknown'; - return SetupResult::error( - $this->l10n->t('Define the certificate engine to use'), - $this->l10n->t('Run occ libresign:configure:%s --help', [$engineName]) - ); - } + #[\Override] + public function run(): SetupResult { + try { + $engine = $this->certificateEngineFactory->getEngine(); + $checkResults = $engine->configureCheck(); + } catch (\Throwable $e) { + $engineName = $this->certificateEngineFactory->getEngine()->getName() ?? 'unknown'; + return SetupResult::error( + $this->l10n->t('Define the certificate engine to use'), + $this->l10n->t('Run occ libresign:configure:%s --help', [$engineName]) + ); + } - // Process results: if any error, return error; else if any warning, return warning; else success - $hasError = false; - $hasWarning = false; - $messages = []; - $tips = []; + // Process results: if any error, return error; else if any warning, return warning; else success + $hasError = false; + $hasWarning = false; + $messages = []; + $tips = []; - foreach ($checkResults as $result) { - if ($result instanceof ConfigureCheckHelper) { - $status = $result->getStatus(); - $msg = $result->getMessage(); - $tip = $result->getTip(); + foreach ($checkResults as $result) { + if ($result instanceof ConfigureCheckHelper) { + $status = $result->getStatus(); + $msg = $result->getMessage(); + $tip = $result->getTip(); - if ($status === 'error') { - $hasError = true; - $messages[] = "[ERROR] $msg"; - } elseif ($status === 'warning') { - $hasWarning = true; - $messages[] = "[WARNING] $msg"; - } else { - $messages[] = $msg; - } + if ($status === 'error') { + $hasError = true; + $messages[] = "[ERROR] $msg"; + } elseif ($status === 'warning') { + $hasWarning = true; + $messages[] = "[WARNING] $msg"; + } else { + $messages[] = $msg; + } - if (!empty($tip)) { - $tips[] = $tip; - } - } - } + if (!empty($tip)) { + $tips[] = $tip; + } + } + } - $tip = ''; - if (!empty($tips)) { - $tip = implode("\n", array_unique($tips)); - } + $tip = ''; + if (!empty($tips)) { + $tip = implode("\n", array_unique($tips)); + } - if ($hasError) { - return SetupResult::error(implode("\n", $messages), $tip); - } elseif ($hasWarning) { - return SetupResult::warning(implode("\n", $messages), $tip); - } else { - return SetupResult::success(implode("\n", $messages) ?: $this->l10n->t('Certificate engine is configured correctly')); - } - } + if ($hasError) { + return SetupResult::error(implode("\n", $messages), $tip); + } elseif ($hasWarning) { + return SetupResult::warning(implode("\n", $messages), $tip); + } else { + return SetupResult::success(implode("\n", $messages) ?: $this->l10n->t('Certificate engine is configured correctly')); + } + } } diff --git a/lib/SetupCheck/ImagickSetupCheck.php b/lib/SetupCheck/ImagickSetupCheck.php index 48ac35a527..d24446bf15 100644 --- a/lib/SetupCheck/ImagickSetupCheck.php +++ b/lib/SetupCheck/ImagickSetupCheck.php @@ -19,14 +19,17 @@ public function __construct(IL10N $l10n) { $this->l10n = $l10n; } + #[\Override] public function getName(): string { return $this->l10n->t('Imagick PHP extension'); } + #[\Override] public function getCategory(): string { return 'system'; } + #[\Override] public function run(): SetupResult { if (!extension_loaded('imagick')) { return SetupResult::info( diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 2c94d3c38d..1a727a1e01 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -8,126 +8,125 @@ namespace OCA\Libresign\SetupCheck; -use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\AppInfo\Application; use OCA\Libresign\Handler\SignEngine\JSignPdfHandler; -use OCA\Libresign\Service\Install\SignSetupService; +use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; -use OCP\IL10N; +use OCA\Libresign\Service\Install\SignSetupService; +use OCP\App\IAppManager; use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IL10N; use OCP\IURLGenerator; -use OCP\App\IAppManager; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -use OCP\IConfig; use Psr\Log\LoggerInterface; -class JSignPdfSetupCheck implements ISetupCheck -{ - use SetupCheckUtils; - - private IL10N $l10n; - private IAppConfig $appConfig; - private JSignPdfHandler $jSignPdfHandler; - private IConfig $systemConfig; - private JavaHelper $javaHelper; - - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; - - public function __construct( - IL10N $l10n, - IAppConfig $appConfig, - JSignPdfHandler $jSignPdfHandler, - SignSetupService $signSetupService, - IURLGenerator $urlGenerator, - IAppManager $appManager, - LoggerInterface $logger, - IConfig $systemConfig, - JavaHelper $javaHelper - ) { - $this->l10n = $l10n; - $this->appConfig = $appConfig; - $this->jSignPdfHandler = $jSignPdfHandler; - $this->signSetupService = $signSetupService; - $this->urlGenerator = $urlGenerator; - $this->appManager = $appManager; - $this->logger = $logger; - $this->systemConfig = $systemConfig; - $this->javaHelper = $javaHelper; - } - - public function getName(): string - { - return $this->l10n->t('JSignPdf'); - } - - public function getCategory(): string - { - return 'system'; - } - - public function run(): SetupResult - { - $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); - $jsignpdfJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path'); - - if (!$jsignpdfJarPath) { - return SetupResult::error( - $this->l10n->t('JSignPdf not found'), - $this->l10n->t('Run occ libresign:install --jsignpdf') - ); - } - - $verifyResult = $this->verifyResourceIntegrity('jsignpdf', $debugEnabled); - if (!empty($verifyResult)) { - [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'jsignpdf', $debugEnabled, $this->l10n); - return SetupResult::error($errorMsg, $tip); - } - - if (!file_exists($jsignpdfJarPath)) { - return SetupResult::error( - $this->l10n->t('JSignPdf binary not found: %s', [$jsignpdfJarPath]), - $this->l10n->t('Run occ libresign:install --jsignpdf') - ); - } - - $javaPath = $this->javaHelper->getJavaPath(); - if (!$javaPath || !file_exists($javaPath)) { - return SetupResult::error( - $this->l10n->t('Necessary Java to run JSignPdf'), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - $jsignPdf = $this->jSignPdfHandler->getJSignPdf(); - $jsignPdf->setParam($this->jSignPdfHandler->getJSignParam()); - $currentVersion = $jsignPdf->getVersion(); - - if (!$currentVersion) { - $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); - return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); - } - - if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { - $msg = $this->l10n->t('Necessary bump JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); - return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); - } - - if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '>')) { - return SetupResult::error( - $this->l10n->t('Necessary downgrade JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]), - $this->l10n->t('Run occ libresign:install --jsignpdf') - ); - } - - $messages = [ - $this->l10n->t('JSignPdf version: %s', [$currentVersion]), - $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]) - ]; - - return SetupResult::success(implode("\n", $messages)); - } +class JSignPdfSetupCheck implements ISetupCheck { + use SetupCheckUtils; + + private IL10N $l10n; + private IAppConfig $appConfig; + private JSignPdfHandler $jSignPdfHandler; + private IConfig $systemConfig; + private JavaHelper $javaHelper; + + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; + + public function __construct( + IL10N $l10n, + IAppConfig $appConfig, + JSignPdfHandler $jSignPdfHandler, + SignSetupService $signSetupService, + IURLGenerator $urlGenerator, + IAppManager $appManager, + LoggerInterface $logger, + IConfig $systemConfig, + JavaHelper $javaHelper, + ) { + $this->l10n = $l10n; + $this->appConfig = $appConfig; + $this->jSignPdfHandler = $jSignPdfHandler; + $this->signSetupService = $signSetupService; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; + $this->logger = $logger; + $this->systemConfig = $systemConfig; + $this->javaHelper = $javaHelper; + } + + #[\Override] + public function getName(): string { + return $this->l10n->t('JSignPdf'); + } + + #[\Override] + public function getCategory(): string { + return 'system'; + } + + #[\Override] + public function run(): SetupResult { + $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); + $jsignpdfJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path'); + + if (!$jsignpdfJarPath) { + return SetupResult::error( + $this->l10n->t('JSignPdf not found'), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $verifyResult = $this->verifyResourceIntegrity('jsignpdf', $debugEnabled); + if (!empty($verifyResult)) { + [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'jsignpdf', $debugEnabled, $this->l10n); + return SetupResult::error($errorMsg, $tip); + } + + if (!file_exists($jsignpdfJarPath)) { + return SetupResult::error( + $this->l10n->t('JSignPdf binary not found: %s', [$jsignpdfJarPath]), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $javaPath = $this->javaHelper->getJavaPath(); + if (!$javaPath || !file_exists($javaPath)) { + return SetupResult::error( + $this->l10n->t('Necessary Java to run JSignPdf'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $jsignPdf = $this->jSignPdfHandler->getJSignPdf(); + $jsignPdf->setParam($this->jSignPdfHandler->getJSignParam()); + $currentVersion = $jsignPdf->getVersion(); + + if (!$currentVersion) { + $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); + return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + } + + if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { + $msg = $this->l10n->t('Necessary bump JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); + return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + } + + if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '>')) { + return SetupResult::error( + $this->l10n->t('Necessary downgrade JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $messages = [ + $this->l10n->t('JSignPdf version: %s', [$currentVersion]), + $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]) + ]; + + return SetupResult::success(implode("\n", $messages)); + } } diff --git a/lib/SetupCheck/JavaSetupCheck.php b/lib/SetupCheck/JavaSetupCheck.php index 8577f78957..8a03674c04 100644 --- a/lib/SetupCheck/JavaSetupCheck.php +++ b/lib/SetupCheck/JavaSetupCheck.php @@ -9,146 +9,145 @@ namespace OCA\Libresign\SetupCheck; use OCA\Libresign\Helper\JavaHelper; -use OCA\Libresign\Service\Install\SignSetupService; use OCA\Libresign\Service\Install\InstallService; +use OCA\Libresign\Service\Install\SignSetupService; +use OCP\App\IAppManager; +use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; -use OCP\App\IAppManager; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -use OCP\IConfig; use Psr\Log\LoggerInterface; -class JavaSetupCheck implements ISetupCheck -{ - use SetupCheckUtils; - - private IL10N $l10n; - private JavaHelper $javaHelper; - private IConfig $systemConfig; - - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; - - public function __construct( - IL10N $l10n, - JavaHelper $javaHelper, - SignSetupService $signSetupService, - IURLGenerator $urlGenerator, - IAppManager $appManager, - LoggerInterface $logger, - IConfig $systemConfig - ) { - $this->l10n = $l10n; - $this->javaHelper = $javaHelper; - $this->signSetupService = $signSetupService; - $this->urlGenerator = $urlGenerator; - $this->appManager = $appManager; - $this->logger = $logger; - $this->systemConfig = $systemConfig; - } - - public function getName(): string - { - return $this->l10n->t('Java'); - } - - public function getCategory(): string - { - return 'system'; - } - - public function run(): SetupResult - { - $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); - $javaPath = $this->javaHelper->getJavaPath(); - - if (!$javaPath) { - return SetupResult::error( - $this->l10n->t('Java not installed'), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - $verifyResult = $this->verifyResourceIntegrity('java', $debugEnabled); - if (!empty($verifyResult)) { - [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'java', $debugEnabled, $this->l10n); - return SetupResult::error($errorMsg, $tip); - } - - if (!file_exists($javaPath)) { - return SetupResult::error( - $this->l10n->t('Java binary not found: %s', [$javaPath]), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - exec($javaPath . ' -version 2>&1', $output, $returnCode); - if (empty($output)) { - return SetupResult::error( - $this->l10n->t('Failed to execute Java. Sounds that your operational system is blocking the JVM.'), - 'https://github.com/LibreSign/libresign/issues/2327#issuecomment-1961988790' - ); - } - if ($returnCode !== 0) { - return SetupResult::error( - $this->l10n->t('Failure to check Java version.'), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - $javaVersion = trim($output[0] ?? ''); - if ($javaVersion !== InstallService::JAVA_VERSION) { - return SetupResult::error( - $this->l10n->t('Invalid java version. Found: %s expected: %s', [$javaVersion, InstallService::JAVA_VERSION]), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - exec($javaPath . ' -XshowSettings:properties -version 2>&1', $encodingOutput); - $fullOutput = implode("\n", $encodingOutput); - preg_match('/native.encoding = (?.*?)(\n|$)/', $fullOutput, $matches); - $encoding = $matches['encoding'] ?? null; - - if (!$encoding) { - return SetupResult::error( - $this->l10n->t('Java encoding not found.'), - sprintf('The command %s need to have native.encoding', $javaPath . ' -XshowSettings:properties -version') - ); - } - - if (!str_contains($encoding, 'UTF-8')) { - $detectedEncoding = trim($encoding); - $phpLocale = setlocale(LC_CTYPE, 0) ?: 'not set'; - $phpLcAll = getenv('LC_ALL') ?: 'not set'; - $phpLang = getenv('LANG') ?: 'not set'; - - $tip = sprintf( - "Java detected encoding \"%s\" but UTF-8 is required.\n\n" - . "**Current PHP environment:**\n" - . "- LC_CTYPE: %s\n" - . "- LC_ALL: %s\n" - . "- LANG: %s\n\n" - . "**To fix this issue:**\n" - . "1. Set LC_ALL and LANG environment variables (e.g., LC_ALL=en_US.UTF-8) for your web server user\n" - . "2. Restart your web server after making changes\n" - . "3. Verify with command: `locale charmap` (should return UTF-8)\n\n" - . 'For more details, see: [Issue #4872](https://github.com/LibreSign/libresign/issues/4872)', - $detectedEncoding, - $phpLocale, - $phpLcAll, - $phpLang - ); - return SetupResult::info( - $this->l10n->t('Non-UTF-8 encoding detected: %s. This may cause issues with accented or special characters', [$detectedEncoding]), - $tip - ); - } - - $message = $this->l10n->t('Java version: %s', [$javaVersion]) . "\n" . - $this->l10n->t('Java binary: %s', [$javaPath]); - return SetupResult::success($message); - } +class JavaSetupCheck implements ISetupCheck { + use SetupCheckUtils; + + private IL10N $l10n; + private JavaHelper $javaHelper; + private IConfig $systemConfig; + + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; + + public function __construct( + IL10N $l10n, + JavaHelper $javaHelper, + SignSetupService $signSetupService, + IURLGenerator $urlGenerator, + IAppManager $appManager, + LoggerInterface $logger, + IConfig $systemConfig, + ) { + $this->l10n = $l10n; + $this->javaHelper = $javaHelper; + $this->signSetupService = $signSetupService; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; + $this->logger = $logger; + $this->systemConfig = $systemConfig; + } + + #[\Override] + public function getName(): string { + return $this->l10n->t('Java'); + } + + #[\Override] + public function getCategory(): string { + return 'system'; + } + + #[\Override] + public function run(): SetupResult { + $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); + $javaPath = $this->javaHelper->getJavaPath(); + + if (!$javaPath) { + return SetupResult::error( + $this->l10n->t('Java not installed'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $verifyResult = $this->verifyResourceIntegrity('java', $debugEnabled); + if (!empty($verifyResult)) { + [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'java', $debugEnabled, $this->l10n); + return SetupResult::error($errorMsg, $tip); + } + + if (!file_exists($javaPath)) { + return SetupResult::error( + $this->l10n->t('Java binary not found: %s', [$javaPath]), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + exec($javaPath . ' -version 2>&1', $output, $returnCode); + if (empty($output)) { + return SetupResult::error( + $this->l10n->t('Failed to execute Java. Sounds that your operational system is blocking the JVM.'), + 'https://github.com/LibreSign/libresign/issues/2327#issuecomment-1961988790' + ); + } + if ($returnCode !== 0) { + return SetupResult::error( + $this->l10n->t('Failure to check Java version.'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $javaVersion = trim($output[0] ?? ''); + if ($javaVersion !== InstallService::JAVA_VERSION) { + return SetupResult::error( + $this->l10n->t('Invalid java version. Found: %s expected: %s', [$javaVersion, InstallService::JAVA_VERSION]), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + exec($javaPath . ' -XshowSettings:properties -version 2>&1', $encodingOutput); + $fullOutput = implode("\n", $encodingOutput); + preg_match('/native.encoding = (?.*?)(\n|$)/', $fullOutput, $matches); + $encoding = $matches['encoding'] ?? null; + + if (!$encoding) { + return SetupResult::error( + $this->l10n->t('Java encoding not found.'), + sprintf('The command %s need to have native.encoding', $javaPath . ' -XshowSettings:properties -version') + ); + } + + if (!str_contains($encoding, 'UTF-8')) { + $detectedEncoding = trim($encoding); + $phpLocale = setlocale(LC_CTYPE, 0) ?: 'not set'; + $phpLcAll = getenv('LC_ALL') ?: 'not set'; + $phpLang = getenv('LANG') ?: 'not set'; + + $tip = sprintf( + "Java detected encoding \"%s\" but UTF-8 is required.\n\n" + . "**Current PHP environment:**\n" + . "- LC_CTYPE: %s\n" + . "- LC_ALL: %s\n" + . "- LANG: %s\n\n" + . "**To fix this issue:**\n" + . "1. Set LC_ALL and LANG environment variables (e.g., LC_ALL=en_US.UTF-8) for your web server user\n" + . "2. Restart your web server after making changes\n" + . "3. Verify with command: `locale charmap` (should return UTF-8)\n\n" + . 'For more details, see: [Issue #4872](https://github.com/LibreSign/libresign/issues/4872)', + $detectedEncoding, + $phpLocale, + $phpLcAll, + $phpLang + ); + return SetupResult::info( + $this->l10n->t('Non-UTF-8 encoding detected: %s. This may cause issues with accented or special characters', [$detectedEncoding]), + $tip + ); + } + + $message = $this->l10n->t('Java version: %s', [$javaVersion]) . "\n" + . $this->l10n->t('Java binary: %s', [$javaPath]); + return SetupResult::success($message); + } } diff --git a/lib/SetupCheck/PDFtkSetupCheck.php b/lib/SetupCheck/PDFtkSetupCheck.php index efcbdb4c53..d03db7d4a9 100644 --- a/lib/SetupCheck/PDFtkSetupCheck.php +++ b/lib/SetupCheck/PDFtkSetupCheck.php @@ -8,17 +8,17 @@ namespace OCA\Libresign\SetupCheck; -use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\AppInfo\Application; -use OCA\Libresign\Service\Install\SignSetupService; +use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; -use OCP\IL10N; +use OCA\Libresign\Service\Install\SignSetupService; +use OCP\App\IAppManager; use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IL10N; use OCP\IURLGenerator; -use OCP\App\IAppManager; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -use OCP\IConfig; use Psr\Log\LoggerInterface; class PDFtkSetupCheck implements ISetupCheck { @@ -29,10 +29,10 @@ class PDFtkSetupCheck implements ISetupCheck { private IConfig $systemConfig; private JavaHelper $javaHelper; - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; public function __construct( IL10N $l10n, @@ -42,7 +42,7 @@ public function __construct( IAppManager $appManager, LoggerInterface $logger, IConfig $systemConfig, - JavaHelper $javaHelper + JavaHelper $javaHelper, ) { $this->l10n = $l10n; $this->appConfig = $appConfig; @@ -54,14 +54,17 @@ public function __construct( $this->javaHelper = $javaHelper; } + #[\Override] public function getName(): string { return $this->l10n->t('PDFtk'); } + #[\Override] public function getCategory(): string { return 'system'; } + #[\Override] public function run(): SetupResult { $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); $pdftkPath = $this->appConfig->getValueString(Application::APP_ID, 'pdftk_path'); diff --git a/lib/SetupCheck/PopplerSetupCheck.php b/lib/SetupCheck/PopplerSetupCheck.php index b0f92d66a3..10cd6d2962 100644 --- a/lib/SetupCheck/PopplerSetupCheck.php +++ b/lib/SetupCheck/PopplerSetupCheck.php @@ -19,14 +19,17 @@ public function __construct(IL10N $l10n) { $this->l10n = $l10n; } + #[\Override] public function getName(): string { return $this->l10n->t('Poppler utils'); } + #[\Override] public function getCategory(): string { return 'system'; } + #[\Override] public function run(): SetupResult { $pdfsigOk = $this->checkPdfSig(); $pdfinfoOk = $this->checkPdfinfo(); diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index cf63bcccfa..b2f8641e5f 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -9,66 +9,63 @@ namespace OCA\Libresign\SetupCheck; use OCA\Libresign\Service\Install\SignSetupService; -use OCP\IURLGenerator; use OCP\App\IAppManager; -use Psr\Log\LoggerInterface; use OCP\IL10N; +use OCP\IURLGenerator; +use Psr\Log\LoggerInterface; -trait SetupCheckUtils -{ - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; +trait SetupCheckUtils { + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; - private function verifyResourceIntegrity(string $resource, bool $debugEnabled): array - { - $this->signSetupService->willUseLocalCert($debugEnabled); - $result = $this->signSetupService->verify(php_uname('m'), $resource); - if (count($result) === 1 && $debugEnabled) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND']) || isset($result['EMPTY_SIGNATURE_DATA'])) { - return []; - } - } - return $result; - } + private function verifyResourceIntegrity(string $resource, bool $debugEnabled): array { + $this->signSetupService->willUseLocalCert($debugEnabled); + $result = $this->signSetupService->verify(php_uname('m'), $resource); + if (count($result) === 1 && $debugEnabled) { + if (isset($result['SIGNATURE_DATA_NOT_FOUND']) || isset($result['EMPTY_SIGNATURE_DATA'])) { + return []; + } + } + return $result; + } - private function getErrorAndTipFromVerify(array $result, string $resource, bool $debugEnabled, IL10N $l10n): array - { - if (count($result) === 1 && !$debugEnabled) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { - return [ - $l10n->t('Signature data not found.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), - ]; - } - if (isset($result['EMPTY_SIGNATURE_DATA'])) { - return [ - $l10n->t('Your signature data is empty.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), - ]; - } - } - if (isset($result['HASH_FILE_ERROR'])) { - if ($debugEnabled) { - return [ - $l10n->t('Invalid hash of binaries files.'), - $l10n->t('Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --%s --use-local-cert', [$resource]), - ]; - } - } - $this->logger->error('Invalid hash of binaries files', ['result' => $result]); - if ($this->appManager->isEnabledForUser('logreader')) { - return [ - $l10n->t('Invalid hash of binaries files.'), - $l10n->t('Check your nextcloud.log file on %s and run occ libresign:install --all', [ - $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) - ]), - ]; - } - return [ - $l10n->t('Invalid hash of binaries files.'), - $l10n->t('Check your nextcloud.log file and run occ libresign:install --all'), - ]; - } + private function getErrorAndTipFromVerify(array $result, string $resource, bool $debugEnabled, IL10N $l10n): array { + if (count($result) === 1 && !$debugEnabled) { + if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { + return [ + $l10n->t('Signature data not found.'), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + ]; + } + if (isset($result['EMPTY_SIGNATURE_DATA'])) { + return [ + $l10n->t('Your signature data is empty.'), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + ]; + } + } + if (isset($result['HASH_FILE_ERROR'])) { + if ($debugEnabled) { + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --%s --use-local-cert', [$resource]), + ]; + } + } + $this->logger->error('Invalid hash of binaries files', ['result' => $result]); + if ($this->appManager->isEnabledForUser('logreader')) { + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Check your nextcloud.log file on %s and run occ libresign:install --all', [ + $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) + ]), + ]; + } + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Check your nextcloud.log file and run occ libresign:install --all'), + ]; + } } From 00b91f67fb8c6c4a1e296f5b33bfdab631c9c4b8 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Thu, 5 Mar 2026 19:38:02 +0000 Subject: [PATCH 13/68] fix(setup-check): address Psalm issues and improve robustness - Add `@var list` type annotation to `$output` variable in `JavaSetupCheck.php` to satisfy Psalm's type checking. - Improve empty output check in `PDFtkSetupCheck.php` by verifying `empty($versionOutput)` in addition to the return code, preventing false positives when the command returns an empty string but a zero exit code. These changes enhance code quality and the reliability of the setup checks. Related issue: #6590 Type: Bug fix Checklist: - [x] Add type annotation for Psalm in JavaSetupCheck - [x] Fix empty output check in PDFtkSetupCheck Signed-off-by: Guilherme Carvalho --- lib/SetupCheck/JavaSetupCheck.php | 1 + lib/SetupCheck/PDFtkSetupCheck.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/JavaSetupCheck.php b/lib/SetupCheck/JavaSetupCheck.php index 8a03674c04..56b9fbfe4d 100644 --- a/lib/SetupCheck/JavaSetupCheck.php +++ b/lib/SetupCheck/JavaSetupCheck.php @@ -84,6 +84,7 @@ public function run(): SetupResult { ); } + /** @var list $output */ exec($javaPath . ' -version 2>&1', $output, $returnCode); if (empty($output)) { return SetupResult::error( diff --git a/lib/SetupCheck/PDFtkSetupCheck.php b/lib/SetupCheck/PDFtkSetupCheck.php index d03db7d4a9..8086b61dbc 100644 --- a/lib/SetupCheck/PDFtkSetupCheck.php +++ b/lib/SetupCheck/PDFtkSetupCheck.php @@ -97,8 +97,9 @@ public function run(): SetupResult { ); } + /** @var list $versionOutput */ exec($javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1', $versionOutput, $resultCode); - if ($resultCode !== 0) { + if (empty($versionOutput) || $resultCode !== 0) { return SetupResult::error( $this->l10n->t('Failure to check PDFtk version.'), $this->l10n->t('Run occ libresign:install --pdftk') From 93cb5047c65a6f1405d715adf26e12bab1aa2d07 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Thu, 5 Mar 2026 19:43:27 +0000 Subject: [PATCH 14/68] test(setup-check): update PDFtkSetupCheckTest to match new error message The previous fix for empty output in PDFtkSetupCheck changed the error message from "PDFtk binary is invalid" to "Failure to check PDFtk version". This commit updates the corresponding test assertion to align with the new behavior. Related issue: #6590 Type: Test Checklist: - [x] Update assertion in PDFtkSetupCheckTest Signed-off-by: Guilherme Carvalho --- tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php index aeec1ac721..cf2ec7e9f9 100644 --- a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php @@ -416,6 +416,6 @@ public function testRunEmptyCommandOutput(): void { $this->assertInstanceOf(SetupResult::class, $result); $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('PDFtk binary is invalid', $result->getDescription()); + $this->assertStringContainsString('Failure to check PDFtk version', $result->getDescription()); } } From d6b31733a2dcd45011b11494e13312162565d784 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Fri, 6 Mar 2026 02:31:27 +0000 Subject: [PATCH 15/68] test(setupcheck): reorganize test mocks into dedicated namespace Move ExecMock and FileSystemMock from Unit/SetupCheck to the Mock/ directory to centralize test doubles and follow standard testing practices. Consolidate function overrides (file_exists, exec) from multiple test files into a single functions.php file to improve maintainability and reduce duplication. This refactoring addresses the reviewer's request by: - Placing mocks in the proper namespace (OCA\Libresign\Tests\Mock) before the function overrides, aligning with PHP namespace fallback mechanics. - Centralizing mock logic in dedicated, reusable classes. - Making test setup cleaner and more maintainable. Related issue: #6590 Type: Test Signed-off-by: Guilherme Carvalho --- tests/php/Mock/ExecMock.php | 24 +++++++++++++ tests/php/Mock/FileSystemMock.php | 18 ++++++++++ .../SetupCheck/JSignPdfSetupCheckTest.php | 11 +++++- .../Unit/SetupCheck/JavaSetupCheckTest.php | 19 +++++++++-- tests/php/Unit/SetupCheck/Mock/ExecMock.php | 14 -------- .../Unit/SetupCheck/Mock/FileSystemMock.php | 9 ----- tests/php/Unit/SetupCheck/Mock/functions.php | 34 ------------------- .../Unit/SetupCheck/PDFtkSetupCheckTest.php | 20 +++++++++-- .../Unit/SetupCheck/PopplerSetupCheckTest.php | 12 ++++++- tests/php/bootstrap.php | 4 --- 10 files changed, 98 insertions(+), 67 deletions(-) create mode 100644 tests/php/Mock/ExecMock.php create mode 100644 tests/php/Mock/FileSystemMock.php delete mode 100644 tests/php/Unit/SetupCheck/Mock/ExecMock.php delete mode 100644 tests/php/Unit/SetupCheck/Mock/FileSystemMock.php delete mode 100644 tests/php/Unit/SetupCheck/Mock/functions.php diff --git a/tests/php/Mock/ExecMock.php b/tests/php/Mock/ExecMock.php new file mode 100644 index 0000000000..d8fa1e464b --- /dev/null +++ b/tests/php/Mock/ExecMock.php @@ -0,0 +1,24 @@ +addPsr4('Test\\', OC::$SERVERROOT . '/tests/php/lib/', true); \OC::$composerAutoloader->addPsr4('Tests\\', OC::$SERVERROOT . '/tests/php/', true); \OC::$composerAutoloader->addPsr4('OCA\\Libresign\\Tests\\', __DIR__, true); From 01b53a635c459bca93d26358e9899c31d199071e Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Fri, 6 Mar 2026 17:18:45 +0000 Subject: [PATCH 16/68] feat(command): update configure:check to use ISetupCheckManager Refactor the libresign:configure:check command to consume the new setup check classes via ISetupCheckManager, instead of the deprecated ConfigureCheckService. The command now: - Filters only checks from the LibreSign namespace - Displays friendly resource names (e.g. "Java" instead of FQCN) - Respects the --sign (system category) and --certificate (security) filtering options Related issue: #6590 Type: Feature Checklist: - [x] Replace ConfigureCheckService with ISetupCheckManager - [x] Add namespace filtering for LibreSign checks - [x] Improve resource name display - [x] Preserve option-based filtering by category Signed-off-by: Guilherme Carvalho --- lib/Command/Configure/Check.php | 70 +++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/lib/Command/Configure/Check.php b/lib/Command/Configure/Check.php index e31cfcba4a..204ecac268 100644 --- a/lib/Command/Configure/Check.php +++ b/lib/Command/Configure/Check.php @@ -9,8 +9,8 @@ namespace OCA\Libresign\Command\Configure; use OC\Core\Command\Base; -use OCA\Libresign\Service\Install\ConfigureCheckService; -use OCP\IConfig; +use OCP\SetupCheck\ISetupCheckManager; +use Psr\Container\ContainerInterface; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableCell; use Symfony\Component\Console\Helper\TableCellStyle; @@ -20,8 +20,7 @@ class Check extends Base { public function __construct( - private ConfigureCheckService $configureCheckService, - private IConfig $config, + private ContainerInterface $container, ) { parent::__construct(); } @@ -49,31 +48,52 @@ protected function execute(InputInterface $input, OutputInterface $output): int $certificate = $input->getOption('certificate'); $all = (!$sign && !$certificate); - $result = []; - if ($all) { - $result = $this->configureCheckService->checkAll(); - } else { - if ($sign) { - $result = array_merge($result, $this->configureCheckService->checkSign()); - } - if ($certificate) { - $result = array_merge($result, $this->configureCheckService->checkCertificate()); + $checkManager = $this->container->get(ISetupCheckManager::class); + $allResults = $checkManager->runAll(); + + $filteredRows = []; + + foreach ($allResults as $category => $checks) { + foreach ($checks as $checkName => $result) { + + if (!str_starts_with($checkName, 'OCA\\Libresign\\SetupCheck\\')) { + continue; + } + + $includeCategory = $all + || ($sign && $category === 'system') + || ($certificate && $category === 'security'); + + if (!$includeCategory) { + continue; + } + + $status = $this->mapSeverityToStatus($result->getSeverity()); + $shortName = substr($checkName, strrpos($checkName, '\\') + 1); + $resource = str_replace('SetupCheck', '', $shortName); + + $filteredRows[] = [ + 'status' => $status, + 'resource' => $resource, + 'message' => $result->getDescription(), + 'tip' => $result->getLinkToDoc() ?? '', + ]; } } - if (count($result)) { + if (!empty($filteredRows)) { $table = new Table($output); $table->setColumnMaxWidth(3, 40); - foreach ($result as $row) { + foreach ($filteredRows as $row) { $table->addRow([ - new TableCell($row->getStatus(), ['style' => new TableCellStyle([ - 'bg' => $this->getStatusColor($row->getStatus()), + new TableCell($row['status'], ['style' => new TableCellStyle([ + 'bg' => $this->getStatusColor($row['status']), 'fg' => 'black', 'align' => 'center', ])]), - $row->getResource(), - $row->getMessage(), - $row->getTip(), + $row['resource'], + $row['message'], + $row['tip'], ]); } $table @@ -86,9 +106,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int ->setStyle('symfony-style-guide') ->render(); } + return 0; } + private function mapSeverityToStatus(string $severity): string { + return match ($severity) { + 'error' => 'error', + 'warning' => 'info', + 'success' => 'success', + default => 'error', + }; + } + private function getStatusColor($status): string { return match ($status) { 'success' => 'green', From 549095e4bc57147f156082f2b043aea3e7aa9266 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 15:50:11 +0000 Subject: [PATCH 17/68] refactor: replace deprecated ConfigureCheckService with SetupCheckResultService Centralize the logic for filtering and formatting LibreSign setup check results using the new SetupCheckResultService. This service internally uses the ISetupCheckManager and the individual SetupCheck classes, resolving the deprecation of the old ConfigureCheckService. Related issue: #6590 Type: Refactor Checklist: - [x] Update `Check` command to use `SetupCheckResultService` - [x] Update `AdminController` to use `SetupCheckResultService` - [x] Remove direct dependency on `ConfigureCheckService` from controllers - [x] Create new `SetupCheckResultService` with methods for formatted results - [x] Update `@psalm-type LibresignConfigureCheck` to include 'info' status - [x] Ensure legacy API output (`getLegacyFormattedChecks`) remains unchanged Signed-off-by: Guilherme Carvalho --- lib/Command/Configure/Check.php | 59 +++++----------- lib/Controller/AdminController.php | 19 ++--- lib/ResponseDefinitions.php | 2 +- lib/Service/SetupCheckResultService.php | 92 +++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 56 deletions(-) create mode 100644 lib/Service/SetupCheckResultService.php diff --git a/lib/Command/Configure/Check.php b/lib/Command/Configure/Check.php index 204ecac268..a303b2a3d1 100644 --- a/lib/Command/Configure/Check.php +++ b/lib/Command/Configure/Check.php @@ -9,8 +9,7 @@ namespace OCA\Libresign\Command\Configure; use OC\Core\Command\Base; -use OCP\SetupCheck\ISetupCheckManager; -use Psr\Container\ContainerInterface; +use OCA\Libresign\Service\SetupCheckResultService; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableCell; use Symfony\Component\Console\Helper\TableCellStyle; @@ -19,8 +18,9 @@ use Symfony\Component\Console\Output\OutputInterface; class Check extends Base { + public function __construct( - private ContainerInterface $container, + private SetupCheckResultService $setupCheckResultService, ) { parent::__construct(); } @@ -48,38 +48,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int $certificate = $input->getOption('certificate'); $all = (!$sign && !$certificate); - $checkManager = $this->container->get(ISetupCheckManager::class); - $allResults = $checkManager->runAll(); - - $filteredRows = []; - - foreach ($allResults as $category => $checks) { - foreach ($checks as $checkName => $result) { - - if (!str_starts_with($checkName, 'OCA\\Libresign\\SetupCheck\\')) { - continue; - } - - $includeCategory = $all - || ($sign && $category === 'system') - || ($certificate && $category === 'security'); - - if (!$includeCategory) { - continue; - } - - $status = $this->mapSeverityToStatus($result->getSeverity()); - $shortName = substr($checkName, strrpos($checkName, '\\') + 1); - $resource = str_replace('SetupCheck', '', $shortName); + $allChecks = $this->setupCheckResultService->getFormattedChecks(); - $filteredRows[] = [ - 'status' => $status, - 'resource' => $resource, - 'message' => $result->getDescription(), - 'tip' => $result->getLinkToDoc() ?? '', - ]; + $filteredRows = array_filter($allChecks, function ($check) use ($all, $sign, $certificate) { + if ($all) { + return true; } - } + if ($sign && $check['category'] === 'system') { + return true; + } + if ($certificate && $check['category'] === 'security') { + return true; + } + return false; + }); if (!empty($filteredRows)) { $table = new Table($output); @@ -110,16 +92,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } - private function mapSeverityToStatus(string $severity): string { - return match ($severity) { - 'error' => 'error', - 'warning' => 'info', - 'success' => 'success', - default => 'error', - }; - } - - private function getStatusColor($status): string { + private function getStatusColor(string $status): string { return match ($status) { 'success' => 'green', 'error' => 'red', diff --git a/lib/Controller/AdminController.php b/lib/Controller/AdminController.php index 09498f04dd..58426bdb0f 100644 --- a/lib/Controller/AdminController.php +++ b/lib/Controller/AdminController.php @@ -22,9 +22,9 @@ use OCA\Libresign\Service\DocMdp\ConfigService as DocMdpConfigService; use OCA\Libresign\Service\FooterService; use OCA\Libresign\Service\IdentifyMethodService; -use OCA\Libresign\Service\Install\ConfigureCheckService; use OCA\Libresign\Service\Install\InstallService; use OCA\Libresign\Service\ReminderService; +use OCA\Libresign\Service\SetupCheckResultService; use OCA\Libresign\Service\SignatureBackgroundService; use OCA\Libresign\Service\SignatureTextService; use OCA\Libresign\Settings\Admin; @@ -70,7 +70,6 @@ class AdminController extends AEnvironmentAwareController { public function __construct( IRequest $request, private IAppConfig $appConfig, - private ConfigureCheckService $configureCheckService, private InstallService $installService, private CertificateEngineFactory $certificateEngineFactory, private IEventSourceFactory $eventSourceFactory, @@ -85,6 +84,7 @@ public function __construct( private DocMdpConfigService $docMdpConfigService, private IdentifyMethodService $identifyMethodService, private FileMapper $fileMapper, + private SetupCheckResultService $setupCheckResultService, ) { parent::__construct(Application::APP_ID, $request); $this->eventSource = $this->eventSourceFactory->create(); @@ -257,11 +257,7 @@ public function loadCertificate(): DataResponse { #[NoCSRFRequired] #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/admin/configure-check', requirements: ['apiVersion' => '(v1)'])] public function configureCheck(): DataResponse { - /** @var LibresignConfigureChecksResponse $configureCheckList */ - $configureCheckList = array_values($this->configureCheckService->checkAll()); - return new DataResponse( - $configureCheckList - ); + return new DataResponse($this->setupCheckResultService->getLegacyFormattedChecks()); } /** @@ -300,8 +296,7 @@ public function installAndValidate(): void { $this->installService->installCfssl($async); } - $this->configureCheckService->disableCache(); - $this->eventSource->send('configure_check', $this->configureCheckService->checkAll()); + $this->eventSource->send('configure_check', $this->setupCheckResultService->getLegacyFormattedChecks()); $seconds = 0; while ($this->installService->isDownloadWip()) { $totalSize = $this->installService->getTotalSize(); @@ -312,7 +307,7 @@ public function installAndValidate(): void { usleep(200000); // 0.2 seconds $seconds += 0.2; if ($seconds === 5.0) { - $this->eventSource->send('configure_check', $this->configureCheckService->checkAll()); + $this->eventSource->send('configure_check', $this->setupCheckResultService->getLegacyFormattedChecks()); $seconds = 0; } } @@ -326,7 +321,7 @@ public function installAndValidate(): void { ])); } - $this->eventSource->send('configure_check', $this->configureCheckService->checkAll()); + $this->eventSource->send('configure_check', $this->setupCheckResultService->getLegacyFormattedChecks()); $this->eventSource->send('done', ''); $this->eventSource->close(); // Nextcloud inject a lot of headers that is incompatible with SSE @@ -563,7 +558,7 @@ public function signerName( float $fontSize, bool $isDarkTheme, string $align, - ): FileDisplayResponse|DataResponse { + ): FileDisplayResponse|DataResponse { try { $blob = $this->signatureTextService->signerNameImage( width: $width, diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index cce724d0bd..2d478a01eb 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -267,7 +267,7 @@ * @psalm-type LibresignConfigureCheck = array{ * message: string, * resource: string, - * status: "error"|"success", + * status: "error"|"info"|"success", * tip: string, * } * @psalm-type LibresignConfigureChecksResponse = list diff --git a/lib/Service/SetupCheckResultService.php b/lib/Service/SetupCheckResultService.php new file mode 100644 index 0000000000..ada836dfaf --- /dev/null +++ b/lib/Service/SetupCheckResultService.php @@ -0,0 +1,92 @@ + + */ + public function getFormattedChecks(): array { + $allResults = $this->checkManager->runAll(); + $formatted = []; + + foreach ($allResults as $category => $checks) { + foreach ($checks as $checkName => $result) { + if (!str_starts_with($checkName, 'OCA\\Libresign\\SetupCheck\\')) { + continue; + } + $formatted[] = [ + 'status' => $this->mapSeverityToStatus($result->getSeverity()), + 'resource' => $this->formatResourceName($checkName), + 'message' => (string)$result->getDescription(), // garantia de string + 'tip' => $result->getLinkToDoc() ?? '', + 'category' => $category, + ]; + } + } + + return $formatted; + } + + /** + * Get formatted checks without category, suitable for legacy API. + * + * @return list + */ + public function getLegacyFormattedChecks(): array { + $checks = $this->getFormattedChecks(); + return array_map(function ($check) { + unset($check['category']); + return $check; + }, $checks); + } + + /** + * Map SetupResult severity to legacy status string. + * + * @param string $severity (error, warning, success) + * @return 'error'|'info'|'success' + */ + private function mapSeverityToStatus(string $severity): string { + return match ($severity) { + 'error' => 'error', + 'warning' => 'info', + 'success' => 'success', + default => 'info', + }; + } + + /** + * Format a fully qualified class name to a short resource name. + * + * @param string $checkName e.g. "OCA\Libresign\SetupCheck\JavaSetupCheck" + * @return string e.g. "Java" + */ + private function formatResourceName(string $checkName): string { + $shortName = substr($checkName, strrpos($checkName, '\\') + 1); + return str_replace('SetupCheck', '', $shortName); + } +} From 17453daf4135db35e359e81687f8f1712dbb4fdd Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 15:54:22 +0000 Subject: [PATCH 18/68] fix(api): ensure OU field is always returned as string in certificate data The API endpoint GET /api/v1/admin/certificate was returning the Organizational Unit (OU) field as an array when the certificate was already generated. This violated the OpenAPI schema which expects a string, causing test failures in AdminControllerTest. This change ensures that for the OU field, any array value is always converted to a comma-separated string. The CA ID is preserved when the certificate is generated, and removed only when it is not (matching the original behavior of the filter). Although discovered during refactoring of ConfigureCheckService, this issue was pre-existing and is fixed here following the "leave it better than you found it" principle. Signed-off-by: Guilherme Carvalho --- lib/Handler/CertificateEngine/AEngineHandler.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Handler/CertificateEngine/AEngineHandler.php b/lib/Handler/CertificateEngine/AEngineHandler.php index 92cd407288..093f5ed113 100644 --- a/lib/Handler/CertificateEngine/AEngineHandler.php +++ b/lib/Handler/CertificateEngine/AEngineHandler.php @@ -777,9 +777,11 @@ private function getConfigPathForApi(bool $generated): string { } private function filterNameValue(string $name, mixed $value, bool $generated): mixed { - if ($name === 'OU' && is_array($value) && !$generated) { - $filtered = $this->removeCaIdFromOrganizationalUnit($value); - return empty($filtered) ? null : $filtered; + if ($name === 'OU' && is_array($value)) { + if (!$generated) { + $value = $this->removeCaIdFromOrganizationalUnit($value); + } + return empty($value) ? null : implode(', ', $value); } return $value; } From 254e8f6c11583aae292e3c9e4dfd231fb64b9c01 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 16:22:43 +0000 Subject: [PATCH 19/68] test(service): add unit tests for SetupCheckResultService MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive test coverage for the new SetupCheckResultService, which formats and filters ISetupCheck results for LibreSign. The tests verify: - Filtering of checks by LibreSign namespace - Correct formatting of results with category - Legacy format without category - Severity mapping (warning → info, etc.) Related issue: #6590 Type: Test Checklist: - [x] Add tests for getFormattedChecks filtering - [x] Add tests for getLegacyFormattedChecks format - [x] Add tests for severity mapping - [x] Use data providers for multiple scenarios - [x] Mock ISetupCheckManager and SetupResult Signed-off-by: Guilherme Carvalho --- .../Service/SetupCheckResultServiceTest.php | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 tests/php/Unit/Service/SetupCheckResultServiceTest.php diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php new file mode 100644 index 0000000000..686d644cb7 --- /dev/null +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -0,0 +1,161 @@ +checkManager = $this->createMock(ISetupCheckManager::class); + $this->service = new SetupCheckResultService($this->checkManager); + } + + /** + * @return array + */ + public static function providerGetFormattedChecks(): array { + return [ + 'only_libresign_checks' => [ + 'checkData' => [ + 'security' => [ + 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => [ + 'severity' => 'success', + 'description' => 'Java OK', + 'link' => 'https://example.com', + ], + ], + 'system' => [ + 'OCA\\OtherApp\\SetupCheck\\DatabaseCheck' => [], // Será ignorado + ], + ], + 'expectedCount' => 1, + 'expectedFirstResource' => 'Java', + ], + 'mixed_checks' => [ + 'checkData' => [ + 'system' => [ + 'OCA\\Libresign\\SetupCheck\\JSignPdfSetupCheck' => [ + 'severity' => 'success', + 'description' => 'JSignPdf OK', + 'link' => 'https://example.com/jsignpdf', + ], + 'OCA\\OtherApp\\SetupCheck\\DatabaseCheck' => [], + 'OCA\\Libresign\\SetupCheck\\ImagickSetupCheck' => [ + 'severity' => 'success', + 'description' => 'Imagick OK', + 'link' => 'https://example.com/imagick', + ], + ], + ], + 'expectedCount' => 2, + 'expectedFirstResource' => 'JSignPdf', + ], + ]; + } + + /** @dataProvider providerGetFormattedChecks */ + public function testGetFormattedChecks(array $checkData, int $expectedCount, string $expectedFirstResource): void { + $checks = $this->buildCheckResults($checkData); + $this->checkManager->method('runAll')->willReturn($checks); + + $result = $this->service->getFormattedChecks(); + + $this->assertCount($expectedCount, $result); + if ($expectedCount > 0) { + $this->assertEquals($expectedFirstResource, $result[0]['resource']); + $this->assertArrayHasKey('category', $result[0]); + } + } + + public function testGetLegacyFormattedChecksRemovesCategory(): void { + $checkData = [ + 'system' => [ + 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => [ + 'severity' => 'warning', + 'description' => 'Java Warning', + 'link' => null, + ], + ], + ]; + $checks = $this->buildCheckResults($checkData); + $this->checkManager->method('runAll')->willReturn($checks); + + $formatted = $this->service->getFormattedChecks(); + $legacyFormatted = $this->service->getLegacyFormattedChecks(); + + $this->assertCount(1, $legacyFormatted); + $this->assertArrayNotHasKey('category', $legacyFormatted[0]); + // Verifica se o status foi mapeado corretamente (warning -> info) + $this->assertEquals('info', $legacyFormatted[0]['status']); + // Compara com o array original sem a categoria + $expected = $formatted[0]; + unset($expected['category']); + $this->assertEquals($expected, $legacyFormatted[0]); + } + + /** + * @dataProvider providerSeverityMapping + */ + public function testSeverityMapping(string $severity, string $expectedStatus): void { + $checkData = [ + 'system' => [ + 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => [ + 'severity' => $severity, + 'description' => 'Message', + 'link' => null, + ], + ], + ]; + $checks = $this->buildCheckResults($checkData); + $this->checkManager->method('runAll')->willReturn($checks); + + $result = $this->service->getFormattedChecks(); + + $this->assertEquals($expectedStatus, $result[0]['status']); + } + + public static function providerSeverityMapping(): array { + return [ + 'error' => ['error', 'error'], + 'warning' => ['warning', 'info'], + 'success' => ['success', 'success'], + 'unknown' => ['unknown', 'info'], + ]; + } + + /** + * Constrói os mocks de SetupResult a partir dos dados fornecidos. + * + * @param array $checkData Estrutura com os dados das verificações + * @return array Array no formato esperado pelo ISetupCheckManager::runAll() + */ + private function buildCheckResults(array $checkData): array { + $checks = []; + foreach ($checkData as $category => $items) { + foreach ($items as $checkName => $data) { + if (!empty($data)) { + $mockResult = $this->createMock(SetupResult::class); + $mockResult->method('getSeverity')->willReturn($data['severity']); + $mockResult->method('getDescription')->willReturn($data['description']); + $mockResult->method('getLinkToDoc')->willReturn($data['link']); + $checks[$category][$checkName] = $mockResult; + } else { + // Para verificações de outros apps, criamos um mock genérico + $mockResult = $this->createMock(SetupResult::class); + $checks[$category][$checkName] = $mockResult; + } + } + } + return $checks; + } +} From 9055dbeb22cdf7d64a8d06ee40a40100fd5d8336 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 16:24:42 +0000 Subject: [PATCH 20/68] chore(service): remove redundant comment in SetupCheckResultService Signed-off-by: Guilherme Carvalho --- lib/Service/SetupCheckResultService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/SetupCheckResultService.php b/lib/Service/SetupCheckResultService.php index ada836dfaf..47927a63b3 100644 --- a/lib/Service/SetupCheckResultService.php +++ b/lib/Service/SetupCheckResultService.php @@ -36,7 +36,7 @@ public function getFormattedChecks(): array { $formatted[] = [ 'status' => $this->mapSeverityToStatus($result->getSeverity()), 'resource' => $this->formatResourceName($checkName), - 'message' => (string)$result->getDescription(), // garantia de string + 'message' => (string)$result->getDescription(), 'tip' => $result->getLinkToDoc() ?? '', 'category' => $category, ]; From 471894471b2a328d53aae58e3444bfe7cece1de0 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 17:47:50 +0000 Subject: [PATCH 21/68] test(handler): update OU filtering test to expect string The toArray() method in AEngineHandler now returns the Organizational Unit as a string (or null) instead of an array, to match the API contract. This commit updates the test data provider to expect the correct new return types. Related issue: #6590 Type: Tests Signed-off-by: Guilherme Carvalho --- .../CertificateEngine/AEngineHandlerTest.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/php/Unit/Handler/CertificateEngine/AEngineHandlerTest.php b/tests/php/Unit/Handler/CertificateEngine/AEngineHandlerTest.php index c0a0569866..412d4e5c89 100644 --- a/tests/php/Unit/Handler/CertificateEngine/AEngineHandlerTest.php +++ b/tests/php/Unit/Handler/CertificateEngine/AEngineHandlerTest.php @@ -309,7 +309,7 @@ public function testToArrayFiltersConfigPathWhenNotGenerated( public function testToArrayFiltersCaIdFromOrganizationalUnitWhenNotGenerated( bool $certificateGenerated, array $organizationalUnits, - array $expectedOuValues, + ?string $expectedOuValues, string $description, ): void { $instance = $this->getInstance(); @@ -390,37 +390,37 @@ public static function dataProviderToArrayCaIdFiltering(): array { 'OU without CA ID - not generated' => [ false, ['Engineering', 'Security'], - ['Engineering', 'Security'], + 'Engineering, Security', 'normal OU values should pass through when not generated', ], 'OU without CA ID - generated' => [ true, ['Engineering', 'Security'], - ['Engineering', 'Security'], + 'Engineering, Security', 'normal OU values should pass through when generated', ], 'OU with CA ID - not generated (filtered)' => [ false, ['libresign-ca-id:abc123_g:1_e:openssl', 'Engineering', 'Security'], - ['Engineering', 'Security'], + 'Engineering, Security', 'CA ID should be filtered when certificate not generated', ], 'OU with CA ID - generated (kept)' => [ true, ['libresign-ca-id:abc123_g:1_e:openssl', 'Engineering', 'Security'], - ['libresign-ca-id:abc123_g:1_e:openssl', 'Engineering', 'Security'], + 'libresign-ca-id:abc123_g:1_e:openssl, Engineering, Security', 'CA ID should be kept when certificate is generated', ], 'OU with only CA ID - not generated' => [ false, ['libresign-ca-id:abc123_g:1_e:openssl'], - [], + null, 'OU should be empty when only CA ID and not generated', ], 'OU with only CA ID - generated' => [ true, ['libresign-ca-id:abc123_g:1_e:openssl'], - ['libresign-ca-id:abc123_g:1_e:openssl'], + 'libresign-ca-id:abc123_g:1_e:openssl', 'OU with only CA ID should be kept when generated', ], ]; From edf4d581ff20505fed024c370d923cdbcd807008 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 19:27:16 +0000 Subject: [PATCH 22/68] chore(service): remove redundant comments Signed-off-by: Guilherme Carvalho --- tests/php/Unit/Service/SetupCheckResultServiceTest.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index 686d644cb7..6aca99bd49 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -35,7 +35,7 @@ public static function providerGetFormattedChecks(): array { ], ], 'system' => [ - 'OCA\\OtherApp\\SetupCheck\\DatabaseCheck' => [], // Será ignorado + 'OCA\\OtherApp\\SetupCheck\\DatabaseCheck' => [], ], ], 'expectedCount' => 1, @@ -95,9 +95,7 @@ public function testGetLegacyFormattedChecksRemovesCategory(): void { $this->assertCount(1, $legacyFormatted); $this->assertArrayNotHasKey('category', $legacyFormatted[0]); - // Verifica se o status foi mapeado corretamente (warning -> info) $this->assertEquals('info', $legacyFormatted[0]['status']); - // Compara com o array original sem a categoria $expected = $formatted[0]; unset($expected['category']); $this->assertEquals($expected, $legacyFormatted[0]); @@ -150,7 +148,6 @@ private function buildCheckResults(array $checkData): array { $mockResult->method('getLinkToDoc')->willReturn($data['link']); $checks[$category][$checkName] = $mockResult; } else { - // Para verificações de outros apps, criamos um mock genérico $mockResult = $this->createMock(SetupResult::class); $checks[$category][$checkName] = $mockResult; } From 511760e0fdaf7adf6ce95500fd40f3f9c6ee21b8 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 20 Jun 2026 10:44:07 -0400 Subject: [PATCH 23/68] chore(service): add SPDX header to SetupCheckResultService Signed-off-by: YvesCesar --- lib/Service/SetupCheckResultService.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Service/SetupCheckResultService.php b/lib/Service/SetupCheckResultService.php index 47927a63b3..32c4910053 100644 --- a/lib/Service/SetupCheckResultService.php +++ b/lib/Service/SetupCheckResultService.php @@ -1,6 +1,10 @@ Date: Sat, 20 Jun 2026 17:22:01 -0400 Subject: [PATCH 24/68] refactor(setup-check): use typed result object for formatted checks Signed-off-by: YvesCesar --- lib/Command/Configure/Check.php | 17 +++--- lib/Controller/AdminController.php | 10 ++-- .../SetupCheck/ConfigureCheckResult.php | 52 +++++++++++++++++++ lib/Service/SetupCheckResultService.php | 43 ++++----------- .../Service/SetupCheckResultServiceTest.php | 23 ++++---- 5 files changed, 87 insertions(+), 58 deletions(-) create mode 100644 lib/Service/SetupCheck/ConfigureCheckResult.php diff --git a/lib/Command/Configure/Check.php b/lib/Command/Configure/Check.php index a303b2a3d1..9847f21bd8 100644 --- a/lib/Command/Configure/Check.php +++ b/lib/Command/Configure/Check.php @@ -9,6 +9,7 @@ namespace OCA\Libresign\Command\Configure; use OC\Core\Command\Base; +use OCA\Libresign\Service\SetupCheck\ConfigureCheckResult; use OCA\Libresign\Service\SetupCheckResultService; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableCell; @@ -50,14 +51,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $allChecks = $this->setupCheckResultService->getFormattedChecks(); - $filteredRows = array_filter($allChecks, function ($check) use ($all, $sign, $certificate) { + $filteredRows = array_filter($allChecks, function (ConfigureCheckResult $check) use ($all, $sign, $certificate) { if ($all) { return true; } - if ($sign && $check['category'] === 'system') { + if ($sign && $check->getCategory() === 'system') { return true; } - if ($certificate && $check['category'] === 'security') { + if ($certificate && $check->getCategory() === 'security') { return true; } return false; @@ -68,14 +69,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $table->setColumnMaxWidth(3, 40); foreach ($filteredRows as $row) { $table->addRow([ - new TableCell($row['status'], ['style' => new TableCellStyle([ - 'bg' => $this->getStatusColor($row['status']), + new TableCell($row->getStatus(), ['style' => new TableCellStyle([ + 'bg' => $this->getStatusColor($row->getStatus()), 'fg' => 'black', 'align' => 'center', ])]), - $row['resource'], - $row['message'], - $row['tip'], + $row->getResource(), + $row->getMessage(), + $row->getTip(), ]); } $table diff --git a/lib/Controller/AdminController.php b/lib/Controller/AdminController.php index 58426bdb0f..86e9b2cbf6 100644 --- a/lib/Controller/AdminController.php +++ b/lib/Controller/AdminController.php @@ -257,7 +257,9 @@ public function loadCertificate(): DataResponse { #[NoCSRFRequired] #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/admin/configure-check', requirements: ['apiVersion' => '(v1)'])] public function configureCheck(): DataResponse { - return new DataResponse($this->setupCheckResultService->getLegacyFormattedChecks()); + /** @var LibresignConfigureChecksResponse $checks */ + $checks = $this->setupCheckResultService->getFormattedChecks(); + return new DataResponse($checks); } /** @@ -296,7 +298,7 @@ public function installAndValidate(): void { $this->installService->installCfssl($async); } - $this->eventSource->send('configure_check', $this->setupCheckResultService->getLegacyFormattedChecks()); + $this->eventSource->send('configure_check', $this->setupCheckResultService->getFormattedChecks()); $seconds = 0; while ($this->installService->isDownloadWip()) { $totalSize = $this->installService->getTotalSize(); @@ -307,7 +309,7 @@ public function installAndValidate(): void { usleep(200000); // 0.2 seconds $seconds += 0.2; if ($seconds === 5.0) { - $this->eventSource->send('configure_check', $this->setupCheckResultService->getLegacyFormattedChecks()); + $this->eventSource->send('configure_check', $this->setupCheckResultService->getFormattedChecks()); $seconds = 0; } } @@ -321,7 +323,7 @@ public function installAndValidate(): void { ])); } - $this->eventSource->send('configure_check', $this->setupCheckResultService->getLegacyFormattedChecks()); + $this->eventSource->send('configure_check', $this->setupCheckResultService->getFormattedChecks()); $this->eventSource->send('done', ''); $this->eventSource->close(); // Nextcloud inject a lot of headers that is incompatible with SSE diff --git a/lib/Service/SetupCheck/ConfigureCheckResult.php b/lib/Service/SetupCheck/ConfigureCheckResult.php new file mode 100644 index 0000000000..5d92409062 --- /dev/null +++ b/lib/Service/SetupCheck/ConfigureCheckResult.php @@ -0,0 +1,52 @@ +status; + } + + public function getResource(): string { + return $this->resource; + } + + public function getMessage(): string { + return $this->message; + } + + public function getTip(): string { + return $this->tip; + } + + public function getCategory(): string { + return $this->category; + } + + #[\Override] + public function jsonSerialize(): array { + return [ + 'status' => $this->status, + 'resource' => $this->resource, + 'message' => $this->message, + 'tip' => $this->tip, + ]; + } +} diff --git a/lib/Service/SetupCheckResultService.php b/lib/Service/SetupCheckResultService.php index 32c4910053..c4d3aa387d 100644 --- a/lib/Service/SetupCheckResultService.php +++ b/lib/Service/SetupCheckResultService.php @@ -8,6 +8,7 @@ namespace OCA\Libresign\Service; +use OCA\Libresign\Service\SetupCheck\ConfigureCheckResult; use OCP\SetupCheck\ISetupCheckManager; class SetupCheckResultService { @@ -18,15 +19,7 @@ public function __construct( } /** - * Get formatted results of all LibreSign setup checks. - * - * @return list + * @return list */ public function getFormattedChecks(): array { $allResults = $this->checkManager->runAll(); @@ -37,37 +30,19 @@ public function getFormattedChecks(): array { if (!str_starts_with($checkName, 'OCA\\Libresign\\SetupCheck\\')) { continue; } - $formatted[] = [ - 'status' => $this->mapSeverityToStatus($result->getSeverity()), - 'resource' => $this->formatResourceName($checkName), - 'message' => (string)$result->getDescription(), - 'tip' => $result->getLinkToDoc() ?? '', - 'category' => $category, - ]; + $formatted[] = new ConfigureCheckResult( + $this->mapSeverityToStatus($result->getSeverity()), + $this->formatResourceName($checkName), + (string)$result->getDescription(), + $result->getLinkToDoc() ?? '', + $category, + ); } } return $formatted; } - /** - * Get formatted checks without category, suitable for legacy API. - * - * @return list - */ - public function getLegacyFormattedChecks(): array { - $checks = $this->getFormattedChecks(); - return array_map(function ($check) { - unset($check['category']); - return $check; - }, $checks); - } - /** * Map SetupResult severity to legacy status string. * diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index 6aca99bd49..40e52b79c6 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -72,12 +72,12 @@ public function testGetFormattedChecks(array $checkData, int $expectedCount, str $this->assertCount($expectedCount, $result); if ($expectedCount > 0) { - $this->assertEquals($expectedFirstResource, $result[0]['resource']); - $this->assertArrayHasKey('category', $result[0]); + $this->assertEquals($expectedFirstResource, $result[0]->getResource()); + $this->assertNotSame('', $result[0]->getCategory()); } } - public function testGetLegacyFormattedChecksRemovesCategory(): void { + public function testJsonSerializeOmitsCategory(): void { $checkData = [ 'system' => [ 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => [ @@ -90,15 +90,14 @@ public function testGetLegacyFormattedChecksRemovesCategory(): void { $checks = $this->buildCheckResults($checkData); $this->checkManager->method('runAll')->willReturn($checks); - $formatted = $this->service->getFormattedChecks(); - $legacyFormatted = $this->service->getLegacyFormattedChecks(); + $result = $this->service->getFormattedChecks(); + + $this->assertCount(1, $result); + $this->assertSame('system', $result[0]->getCategory()); - $this->assertCount(1, $legacyFormatted); - $this->assertArrayNotHasKey('category', $legacyFormatted[0]); - $this->assertEquals('info', $legacyFormatted[0]['status']); - $expected = $formatted[0]; - unset($expected['category']); - $this->assertEquals($expected, $legacyFormatted[0]); + $serialized = $result[0]->jsonSerialize(); + $this->assertSame(['status', 'resource', 'message', 'tip'], array_keys($serialized)); + $this->assertSame('info', $serialized['status']); } /** @@ -119,7 +118,7 @@ public function testSeverityMapping(string $severity, string $expectedStatus): v $result = $this->service->getFormattedChecks(); - $this->assertEquals($expectedStatus, $result[0]['status']); + $this->assertEquals($expectedStatus, $result[0]->getStatus()); } public static function providerSeverityMapping(): array { From 6480f49562883a98e4859ed921d62e15675fb1d0 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 22 Jun 2026 07:54:11 -0400 Subject: [PATCH 25/68] style(setup-check): apply code style fixes to test files Signed-off-by: YvesCesar --- tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php | 1 - tests/php/Unit/SetupCheck/JavaSetupCheckTest.php | 1 - tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php | 2 -- tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php | 2 -- 4 files changed, 6 deletions(-) diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index 0df54ba555..6871981d46 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -17,7 +17,6 @@ function file_exists(string $filename): bool { namespace OCA\Libresign\Tests\Unit\SetupCheck; use OCA\Libresign\Handler\SignEngine\JSignPdfHandler; - use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; use OCA\Libresign\Service\Install\SignSetupService; diff --git a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php index e1d763fffb..86e4204f35 100644 --- a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php @@ -24,7 +24,6 @@ function exec(string $command, &$output = null, &$result_code = null): string|fa use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; - use OCA\Libresign\Service\Install\SignSetupService; use OCA\Libresign\SetupCheck\JavaSetupCheck; use OCA\Libresign\Tests\Mock\ExecMock; diff --git a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php index fa7c476a82..5a43703030 100644 --- a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php @@ -24,10 +24,8 @@ function exec(string $command, &$output = null, &$result_code = null): string|fa use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; - use OCA\Libresign\Service\Install\SignSetupService; use OCA\Libresign\SetupCheck\PDFtkSetupCheck; - use OCA\Libresign\Tests\Mock\ExecMock; use OCA\Libresign\Tests\Mock\FileSystemMock; use OCP\App\IAppManager; diff --git a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php index 2a11a28211..1329bfd2b6 100644 --- a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php @@ -17,9 +17,7 @@ function exec(string $command, &$output = null, &$result_code = null): string|fa namespace OCA\Libresign\Tests\Unit\SetupCheck; use OCA\Libresign\SetupCheck\PopplerSetupCheck; - use OCA\Libresign\Tests\Mock\ExecMock; - use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; From 459b2da53ae788219fe7141e4158ed697f593bdc Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 22 Jun 2026 08:31:07 -0400 Subject: [PATCH 26/68] chore(setup-check): add SPDX header to SetupCheckResultServiceTest Signed-off-by: YvesCesar --- tests/php/Unit/Service/SetupCheckResultServiceTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index 40e52b79c6..46ba1f699c 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -1,6 +1,10 @@ Date: Mon, 22 Jun 2026 08:45:51 -0400 Subject: [PATCH 27/68] chore(openapi): regenerate specs for 'info' setup check status Signed-off-by: YvesCesar --- openapi-administration.json | 1 + openapi-full.json | 1 + src/types/openapi/openapi-administration.ts | 2 +- src/types/openapi/openapi-full.ts | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openapi-administration.json b/openapi-administration.json index 6168edf453..186927ebfa 100644 --- a/openapi-administration.json +++ b/openapi-administration.json @@ -231,6 +231,7 @@ "type": "string", "enum": [ "error", + "info", "success" ] }, diff --git a/openapi-full.json b/openapi-full.json index 45eac3ef7c..78b520880d 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -449,6 +449,7 @@ "type": "string", "enum": [ "error", + "info", "success" ] }, diff --git a/src/types/openapi/openapi-administration.ts b/src/types/openapi/openapi-administration.ts index b11d2f0fb9..4f46b46aa7 100644 --- a/src/types/openapi/openapi-administration.ts +++ b/src/types/openapi/openapi-administration.ts @@ -564,7 +564,7 @@ export type components = { message: string; resource: string; /** @enum {string} */ - status: "error" | "success"; + status: "error" | "info" | "success"; tip: string; }; ConfigureChecksResponse: components["schemas"]["ConfigureCheck"][]; diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index dfa3d1f283..78a4eae00f 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -1362,7 +1362,7 @@ export type components = { message: string; resource: string; /** @enum {string} */ - status: "error" | "success"; + status: "error" | "info" | "success"; tip: string; }; ConfigureChecksResponse: components["schemas"]["ConfigureCheck"][]; From d994272fbc4abe896802c32ef352169850f7b3e2 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 22 Jun 2026 08:58:07 -0400 Subject: [PATCH 28/68] fix(setup-check): query locale with string in setlocale for PHP 8.5 Signed-off-by: YvesCesar --- lib/SetupCheck/JavaSetupCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SetupCheck/JavaSetupCheck.php b/lib/SetupCheck/JavaSetupCheck.php index 56b9fbfe4d..864676db55 100644 --- a/lib/SetupCheck/JavaSetupCheck.php +++ b/lib/SetupCheck/JavaSetupCheck.php @@ -121,7 +121,7 @@ public function run(): SetupResult { if (!str_contains($encoding, 'UTF-8')) { $detectedEncoding = trim($encoding); - $phpLocale = setlocale(LC_CTYPE, 0) ?: 'not set'; + $phpLocale = setlocale(LC_CTYPE, '0') ?: 'not set'; $phpLcAll = getenv('LC_ALL') ?: 'not set'; $phpLang = getenv('LANG') ?: 'not set'; From 5e5734efa18d5417b44c3176279b6aa0a7f0439a Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Wed, 24 Jun 2026 07:24:28 -0400 Subject: [PATCH 29/68] test(integration): expect OU as string in certificate names The certificate API now returns the Organizational Unit value as a single comma-separated string (matching the OpenAPI schema) instead of an array. Update the affected scenarios to assert the value as a string. Signed-off-by: YvesCesar --- tests/integration/features/account/signature.feature | 8 ++------ .../features/admin/certificate_openssl.feature | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/integration/features/account/signature.feature b/tests/integration/features/account/signature.feature index 94d5a54345..014c12c23b 100644 --- a/tests/integration/features/account/signature.feature +++ b/tests/integration/features/account/signature.feature @@ -15,9 +15,7 @@ Feature: account/signature | (jq).ocs.data.rootCert.names[2] | {"id":"L","value":"City name"} | | (jq).ocs.data.rootCert.names[3] | {"id":"O","value":"Organization"} | | (jq).ocs.data.rootCert.names[4].id | OU | - | (jq).ocs.data.rootCert.names[4].value\|length | 2 | - | (jq).ocs.data.rootCert.names[4].value[0] | Organizational Unit | - | (jq).ocs.data.rootCert.names[4].value | (jq)any(.[]; test("^libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$")) | + | (jq).ocs.data.rootCert.names[4] | (jq).value \|test("^Organizational Unit, libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$") | | (jq).ocs.data.generated | true | Scenario: Create root certificate with CFSSL engine using API @@ -38,9 +36,7 @@ Feature: account/signature | (jq).ocs.data.rootCert.names[2] | {"id":"L","value":"City name"} | | (jq).ocs.data.rootCert.names[3] | {"id":"O","value":"Organization"} | | (jq).ocs.data.rootCert.names[4].id | OU | - | (jq).ocs.data.rootCert.names[4].value\|length | 2 | - | (jq).ocs.data.rootCert.names[4].value[0] | Organizational Unit | - | (jq).ocs.data.rootCert.names[4].value | (jq)any(.[]; test("^libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$")) | + | (jq).ocs.data.rootCert.names[4] | (jq).value \|test("^Organizational Unit, libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$") | | (jq).ocs.data.generated | true | Scenario: Create pfx with success using CFSSL diff --git a/tests/integration/features/admin/certificate_openssl.feature b/tests/integration/features/admin/certificate_openssl.feature index 060ceb5953..400e56ab3e 100644 --- a/tests/integration/features/admin/certificate_openssl.feature +++ b/tests/integration/features/admin/certificate_openssl.feature @@ -11,7 +11,7 @@ Feature: admin/certificate_openssl | (jq).ocs.data.rootCert.commonName | Common Name | | (jq).ocs.data.rootCert.names\|length | 1 | | (jq).ocs.data.rootCert.names[0].id | OU | - | (jq).ocs.data.rootCert.names[0].value | (jq) .[] \|test("^libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$") | + | (jq).ocs.data.rootCert.names[0] | (jq).value \|test("^libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$") | | (jq).ocs.data.generated | true | Scenario: Generate root cert with fail without CN @@ -46,7 +46,7 @@ Feature: admin/certificate_openssl | (jq).ocs.data.rootCert.names[0].id | C | | (jq).ocs.data.rootCert.names[0].value | BR | | (jq).ocs.data.rootCert.names[1].id | OU | - | (jq).ocs.data.rootCert.names[1].value | (jq) .[] \|test("^libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$") | + | (jq).ocs.data.rootCert.names[1] | (jq).value \|test("^libresign-ca-id:[a-z0-9]+_g:[0-9]+_e:[oc]?$") | | (jq).ocs.data.generated | true | Scenario: Generate root cert with fail when country have less then 2 characters From 4ffbb82c97b53d5978033571c4118262f2d1bdf5 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Wed, 24 Jun 2026 08:33:57 -0400 Subject: [PATCH 30/68] fix(setup-check): restore stable lowercase resource identifiers Map each binary check to its stable lowercase resource, and expand the certificate engine into the granular resources produced by the engine handler instead of the single aggregated CertificateEngineSetupCheck result (kept for the Nextcloud setup panel). Signed-off-by: YvesCesar --- lib/Service/SetupCheckResultService.php | 60 +++++++++++++++---- .../Service/SetupCheckResultServiceTest.php | 14 ++++- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/lib/Service/SetupCheckResultService.php b/lib/Service/SetupCheckResultService.php index c4d3aa387d..f533b566b0 100644 --- a/lib/Service/SetupCheckResultService.php +++ b/lib/Service/SetupCheckResultService.php @@ -8,13 +8,25 @@ namespace OCA\Libresign\Service; +use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory; +use OCA\Libresign\Helper\ConfigureCheckHelper; use OCA\Libresign\Service\SetupCheck\ConfigureCheckResult; use OCP\SetupCheck\ISetupCheckManager; class SetupCheckResultService { + /** @var array */ + private const RESOURCE_MAP = [ + 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => 'java', + 'OCA\\Libresign\\SetupCheck\\JSignPdfSetupCheck' => 'jsignpdf', + 'OCA\\Libresign\\SetupCheck\\PDFtkSetupCheck' => 'pdftk', + 'OCA\\Libresign\\SetupCheck\\PopplerSetupCheck' => 'poppler', + 'OCA\\Libresign\\SetupCheck\\ImagickSetupCheck' => 'imagick', + ]; + public function __construct( private ISetupCheckManager $checkManager, + private CertificateEngineFactory $certificateEngineFactory, ) { } @@ -22,17 +34,27 @@ public function __construct( * @return list */ public function getFormattedChecks(): array { + return array_merge( + $this->getBinaryChecks(), + $this->getCertificateEngineChecks(), + ); + } + + /** + * @return list + */ + private function getBinaryChecks(): array { $allResults = $this->checkManager->runAll(); $formatted = []; foreach ($allResults as $category => $checks) { foreach ($checks as $checkName => $result) { - if (!str_starts_with($checkName, 'OCA\\Libresign\\SetupCheck\\')) { + if (!isset(self::RESOURCE_MAP[$checkName])) { continue; } $formatted[] = new ConfigureCheckResult( $this->mapSeverityToStatus($result->getSeverity()), - $this->formatResourceName($checkName), + self::RESOURCE_MAP[$checkName], (string)$result->getDescription(), $result->getLinkToDoc() ?? '', $category, @@ -43,6 +65,29 @@ public function getFormattedChecks(): array { return $formatted; } + /** + * @return list + */ + private function getCertificateEngineChecks(): array { + $engineChecks = $this->certificateEngineFactory->getEngine()->configureCheck(); + + $formatted = []; + foreach ($engineChecks as $check) { + if (!$check instanceof ConfigureCheckHelper) { + continue; + } + $formatted[] = new ConfigureCheckResult( + $check->getStatus(), + $check->getResource(), + $check->getMessage(), + $check->getTip(), + 'security', + ); + } + + return $formatted; + } + /** * Map SetupResult severity to legacy status string. * @@ -57,15 +102,4 @@ private function mapSeverityToStatus(string $severity): string { default => 'info', }; } - - /** - * Format a fully qualified class name to a short resource name. - * - * @param string $checkName e.g. "OCA\Libresign\SetupCheck\JavaSetupCheck" - * @return string e.g. "Java" - */ - private function formatResourceName(string $checkName): string { - $shortName = substr($checkName, strrpos($checkName, '\\') + 1); - return str_replace('SetupCheck', '', $shortName); - } } diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index 46ba1f699c..eaa9ba382e 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -8,6 +8,8 @@ namespace OCA\Libresign\Tests\Unit\Service; +use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory; +use OCA\Libresign\Handler\CertificateEngine\IEngineHandler; use OCA\Libresign\Service\SetupCheckResultService; use OCP\SetupCheck\ISetupCheckManager; use OCP\SetupCheck\SetupResult; @@ -17,11 +19,17 @@ class SetupCheckResultServiceTest extends TestCase { /** @var ISetupCheckManager&MockObject */ private $checkManager; + /** @var CertificateEngineFactory&MockObject */ + private $certificateEngineFactory; private SetupCheckResultService $service; public function setUp(): void { $this->checkManager = $this->createMock(ISetupCheckManager::class); - $this->service = new SetupCheckResultService($this->checkManager); + $this->certificateEngineFactory = $this->createMock(CertificateEngineFactory::class); + $engine = $this->createMock(IEngineHandler::class); + $engine->method('configureCheck')->willReturn([]); + $this->certificateEngineFactory->method('getEngine')->willReturn($engine); + $this->service = new SetupCheckResultService($this->checkManager, $this->certificateEngineFactory); } /** @@ -43,7 +51,7 @@ public static function providerGetFormattedChecks(): array { ], ], 'expectedCount' => 1, - 'expectedFirstResource' => 'Java', + 'expectedFirstResource' => 'java', ], 'mixed_checks' => [ 'checkData' => [ @@ -62,7 +70,7 @@ public static function providerGetFormattedChecks(): array { ], ], 'expectedCount' => 2, - 'expectedFirstResource' => 'JSignPdf', + 'expectedFirstResource' => 'jsignpdf', ], ]; } From d9594294f7f687beeedd956845065126ff3c61e1 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Thu, 25 Jun 2026 08:07:04 -0400 Subject: [PATCH 31/68] fix(setup-check): run only LibreSign checks in configure-check endpoint Signed-off-by: YvesCesar --- lib/Service/SetupCheckResultService.php | 55 +++--- .../Service/SetupCheckResultServiceTest.php | 162 +++++++----------- 2 files changed, 90 insertions(+), 127 deletions(-) diff --git a/lib/Service/SetupCheckResultService.php b/lib/Service/SetupCheckResultService.php index f533b566b0..574ecff6bb 100644 --- a/lib/Service/SetupCheckResultService.php +++ b/lib/Service/SetupCheckResultService.php @@ -11,22 +11,22 @@ use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory; use OCA\Libresign\Helper\ConfigureCheckHelper; use OCA\Libresign\Service\SetupCheck\ConfigureCheckResult; -use OCP\SetupCheck\ISetupCheckManager; +use OCA\Libresign\SetupCheck\ImagickSetupCheck; +use OCA\Libresign\SetupCheck\JavaSetupCheck; +use OCA\Libresign\SetupCheck\JSignPdfSetupCheck; +use OCA\Libresign\SetupCheck\PDFtkSetupCheck; +use OCA\Libresign\SetupCheck\PopplerSetupCheck; +use OCP\SetupCheck\ISetupCheck; class SetupCheckResultService { - /** @var array */ - private const RESOURCE_MAP = [ - 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => 'java', - 'OCA\\Libresign\\SetupCheck\\JSignPdfSetupCheck' => 'jsignpdf', - 'OCA\\Libresign\\SetupCheck\\PDFtkSetupCheck' => 'pdftk', - 'OCA\\Libresign\\SetupCheck\\PopplerSetupCheck' => 'poppler', - 'OCA\\Libresign\\SetupCheck\\ImagickSetupCheck' => 'imagick', - ]; - public function __construct( - private ISetupCheckManager $checkManager, private CertificateEngineFactory $certificateEngineFactory, + private JavaSetupCheck $javaSetupCheck, + private JSignPdfSetupCheck $jSignPdfSetupCheck, + private PDFtkSetupCheck $pdftkSetupCheck, + private PopplerSetupCheck $popplerSetupCheck, + private ImagickSetupCheck $imagickSetupCheck, ) { } @@ -44,22 +44,25 @@ public function getFormattedChecks(): array { * @return list */ private function getBinaryChecks(): array { - $allResults = $this->checkManager->runAll(); - $formatted = []; + /** @var array $checks */ + $checks = [ + 'java' => $this->javaSetupCheck, + 'jsignpdf' => $this->jSignPdfSetupCheck, + 'pdftk' => $this->pdftkSetupCheck, + 'poppler' => $this->popplerSetupCheck, + 'imagick' => $this->imagickSetupCheck, + ]; - foreach ($allResults as $category => $checks) { - foreach ($checks as $checkName => $result) { - if (!isset(self::RESOURCE_MAP[$checkName])) { - continue; - } - $formatted[] = new ConfigureCheckResult( - $this->mapSeverityToStatus($result->getSeverity()), - self::RESOURCE_MAP[$checkName], - (string)$result->getDescription(), - $result->getLinkToDoc() ?? '', - $category, - ); - } + $formatted = []; + foreach ($checks as $resource => $check) { + $result = $check->run(); + $formatted[] = new ConfigureCheckResult( + $this->mapSeverityToStatus($result->getSeverity()), + $resource, + (string)$result->getDescription(), + $result->getLinkToDoc() ?? '', + $check->getCategory(), + ); } return $formatted; diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index eaa9ba382e..ba03881bf8 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -11,100 +11,93 @@ use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory; use OCA\Libresign\Handler\CertificateEngine\IEngineHandler; use OCA\Libresign\Service\SetupCheckResultService; -use OCP\SetupCheck\ISetupCheckManager; +use OCA\Libresign\SetupCheck\ImagickSetupCheck; +use OCA\Libresign\SetupCheck\JavaSetupCheck; +use OCA\Libresign\SetupCheck\JSignPdfSetupCheck; +use OCA\Libresign\SetupCheck\PDFtkSetupCheck; +use OCA\Libresign\SetupCheck\PopplerSetupCheck; +use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class SetupCheckResultServiceTest extends TestCase { - /** @var ISetupCheckManager&MockObject */ - private $checkManager; /** @var CertificateEngineFactory&MockObject */ private $certificateEngineFactory; + /** @var JavaSetupCheck&MockObject */ + private $javaSetupCheck; + /** @var JSignPdfSetupCheck&MockObject */ + private $jSignPdfSetupCheck; + /** @var PDFtkSetupCheck&MockObject */ + private $pdftkSetupCheck; + /** @var PopplerSetupCheck&MockObject */ + private $popplerSetupCheck; + /** @var ImagickSetupCheck&MockObject */ + private $imagickSetupCheck; private SetupCheckResultService $service; public function setUp(): void { - $this->checkManager = $this->createMock(ISetupCheckManager::class); $this->certificateEngineFactory = $this->createMock(CertificateEngineFactory::class); $engine = $this->createMock(IEngineHandler::class); $engine->method('configureCheck')->willReturn([]); $this->certificateEngineFactory->method('getEngine')->willReturn($engine); - $this->service = new SetupCheckResultService($this->checkManager, $this->certificateEngineFactory); + + $this->javaSetupCheck = $this->createMock(JavaSetupCheck::class); + $this->jSignPdfSetupCheck = $this->createMock(JSignPdfSetupCheck::class); + $this->pdftkSetupCheck = $this->createMock(PDFtkSetupCheck::class); + $this->popplerSetupCheck = $this->createMock(PopplerSetupCheck::class); + $this->imagickSetupCheck = $this->createMock(ImagickSetupCheck::class); + + $this->configureCheck($this->javaSetupCheck, 'system', 'success', 'Java OK', 'https://example.com'); + $this->configureCheck($this->jSignPdfSetupCheck, 'system', 'success', 'JSignPdf OK', 'https://example.com/jsignpdf'); + $this->configureCheck($this->pdftkSetupCheck, 'system', 'success', 'PDFtk OK', 'https://example.com/pdftk'); + $this->configureCheck($this->popplerSetupCheck, 'system', 'success', 'Poppler OK', 'https://example.com/poppler'); + $this->configureCheck($this->imagickSetupCheck, 'system', 'success', 'Imagick OK', 'https://example.com/imagick'); + + $this->buildService(); + } + + private function buildService(): void { + $this->service = new SetupCheckResultService( + $this->certificateEngineFactory, + $this->javaSetupCheck, + $this->jSignPdfSetupCheck, + $this->pdftkSetupCheck, + $this->popplerSetupCheck, + $this->imagickSetupCheck, + ); } /** - * @return array + * @param ISetupCheck&MockObject $check */ - public static function providerGetFormattedChecks(): array { - return [ - 'only_libresign_checks' => [ - 'checkData' => [ - 'security' => [ - 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => [ - 'severity' => 'success', - 'description' => 'Java OK', - 'link' => 'https://example.com', - ], - ], - 'system' => [ - 'OCA\\OtherApp\\SetupCheck\\DatabaseCheck' => [], - ], - ], - 'expectedCount' => 1, - 'expectedFirstResource' => 'java', - ], - 'mixed_checks' => [ - 'checkData' => [ - 'system' => [ - 'OCA\\Libresign\\SetupCheck\\JSignPdfSetupCheck' => [ - 'severity' => 'success', - 'description' => 'JSignPdf OK', - 'link' => 'https://example.com/jsignpdf', - ], - 'OCA\\OtherApp\\SetupCheck\\DatabaseCheck' => [], - 'OCA\\Libresign\\SetupCheck\\ImagickSetupCheck' => [ - 'severity' => 'success', - 'description' => 'Imagick OK', - 'link' => 'https://example.com/imagick', - ], - ], - ], - 'expectedCount' => 2, - 'expectedFirstResource' => 'jsignpdf', - ], - ]; + private function configureCheck($check, string $category, string $severity, string $description, ?string $link): void { + $result = $this->createMock(SetupResult::class); + $result->method('getSeverity')->willReturn($severity); + $result->method('getDescription')->willReturn($description); + $result->method('getLinkToDoc')->willReturn($link); + $check->method('getCategory')->willReturn($category); + $check->method('run')->willReturn($result); } - /** @dataProvider providerGetFormattedChecks */ - public function testGetFormattedChecks(array $checkData, int $expectedCount, string $expectedFirstResource): void { - $checks = $this->buildCheckResults($checkData); - $this->checkManager->method('runAll')->willReturn($checks); - + public function testGetFormattedChecksReturnsOnlyLibresignChecks(): void { $result = $this->service->getFormattedChecks(); - $this->assertCount($expectedCount, $result); - if ($expectedCount > 0) { - $this->assertEquals($expectedFirstResource, $result[0]->getResource()); - $this->assertNotSame('', $result[0]->getCategory()); + $this->assertCount(5, $result); + $resources = array_map(static fn ($check) => $check->getResource(), $result); + $this->assertSame(['java', 'jsignpdf', 'pdftk', 'poppler', 'imagick'], $resources); + foreach ($result as $check) { + $this->assertNotSame('', $check->getCategory()); } } public function testJsonSerializeOmitsCategory(): void { - $checkData = [ - 'system' => [ - 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => [ - 'severity' => 'warning', - 'description' => 'Java Warning', - 'link' => null, - ], - ], - ]; - $checks = $this->buildCheckResults($checkData); - $this->checkManager->method('runAll')->willReturn($checks); + $this->javaSetupCheck = $this->createMock(JavaSetupCheck::class); + $this->configureCheck($this->javaSetupCheck, 'system', 'warning', 'Java Warning', null); + $this->buildService(); $result = $this->service->getFormattedChecks(); - $this->assertCount(1, $result); $this->assertSame('system', $result[0]->getCategory()); $serialized = $result[0]->jsonSerialize(); @@ -116,17 +109,9 @@ public function testJsonSerializeOmitsCategory(): void { * @dataProvider providerSeverityMapping */ public function testSeverityMapping(string $severity, string $expectedStatus): void { - $checkData = [ - 'system' => [ - 'OCA\\Libresign\\SetupCheck\\JavaSetupCheck' => [ - 'severity' => $severity, - 'description' => 'Message', - 'link' => null, - ], - ], - ]; - $checks = $this->buildCheckResults($checkData); - $this->checkManager->method('runAll')->willReturn($checks); + $this->javaSetupCheck = $this->createMock(JavaSetupCheck::class); + $this->configureCheck($this->javaSetupCheck, 'system', $severity, 'Message', null); + $this->buildService(); $result = $this->service->getFormattedChecks(); @@ -141,29 +126,4 @@ public static function providerSeverityMapping(): array { 'unknown' => ['unknown', 'info'], ]; } - - /** - * Constrói os mocks de SetupResult a partir dos dados fornecidos. - * - * @param array $checkData Estrutura com os dados das verificações - * @return array Array no formato esperado pelo ISetupCheckManager::runAll() - */ - private function buildCheckResults(array $checkData): array { - $checks = []; - foreach ($checkData as $category => $items) { - foreach ($items as $checkName => $data) { - if (!empty($data)) { - $mockResult = $this->createMock(SetupResult::class); - $mockResult->method('getSeverity')->willReturn($data['severity']); - $mockResult->method('getDescription')->willReturn($data['description']); - $mockResult->method('getLinkToDoc')->willReturn($data['link']); - $checks[$category][$checkName] = $mockResult; - } else { - $mockResult = $this->createMock(SetupResult::class); - $checks[$category][$checkName] = $mockResult; - } - } - } - return $checks; - } } From e2a290536c614574983b490d1cee0adc9be528ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Fri, 26 Jun 2026 08:34:08 -0400 Subject: [PATCH 32/68] refactor(setup-check): drop redundant comment in certificate engine check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/CertificateEngineSetupCheck.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/SetupCheck/CertificateEngineSetupCheck.php b/lib/SetupCheck/CertificateEngineSetupCheck.php index 34ffdc4f5c..fdda3d1067 100644 --- a/lib/SetupCheck/CertificateEngineSetupCheck.php +++ b/lib/SetupCheck/CertificateEngineSetupCheck.php @@ -49,7 +49,6 @@ public function run(): SetupResult { ); } - // Process results: if any error, return error; else if any warning, return warning; else success $hasError = false; $hasWarning = false; $messages = []; From 054575f83250111adb202bcdfe66df401aa7e313 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 08:54:09 -0400 Subject: [PATCH 33/68] refactor(setup-check): catch specific LibresignException in certificate engine check Signed-off-by: YvesCesar --- lib/SetupCheck/CertificateEngineSetupCheck.php | 3 ++- tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/SetupCheck/CertificateEngineSetupCheck.php b/lib/SetupCheck/CertificateEngineSetupCheck.php index fdda3d1067..7f0a1f1b08 100644 --- a/lib/SetupCheck/CertificateEngineSetupCheck.php +++ b/lib/SetupCheck/CertificateEngineSetupCheck.php @@ -8,6 +8,7 @@ namespace OCA\Libresign\SetupCheck; +use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory; use OCA\Libresign\Helper\ConfigureCheckHelper; use OCP\IL10N; @@ -41,7 +42,7 @@ public function run(): SetupResult { try { $engine = $this->certificateEngineFactory->getEngine(); $checkResults = $engine->configureCheck(); - } catch (\Throwable $e) { + } catch (LibresignException $e) { $engineName = $this->certificateEngineFactory->getEngine()->getName() ?? 'unknown'; return SetupResult::error( $this->l10n->t('Define the certificate engine to use'), diff --git a/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php b/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php index 4124bbcfd5..290f1430bf 100644 --- a/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php @@ -8,6 +8,7 @@ namespace OCA\Libresign\Tests\Unit\SetupCheck; +use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory; use OCA\Libresign\Handler\CertificateEngine\IEngineHandler; use OCA\Libresign\Helper\ConfigureCheckHelper; @@ -62,7 +63,7 @@ public function testRunWithException(): void { $this->certificateEngineFactory->expects($this->exactly(2)) ->method('getEngine') ->willReturnOnConsecutiveCalls( - $this->throwException(new \RuntimeException('Engine not found')), + $this->throwException(new LibresignException('Engine not found')), $engineMock ); From a69443dd4dea8f34139697514d9bcdb495b6cba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:17:22 -0400 Subject: [PATCH 34/68] refactor(setup-check): use placeholder for the JSignPdf install command tip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 1a727a1e01..c5527a6150 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -107,7 +107,8 @@ public function run(): SetupResult { if (!$currentVersion) { $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); - return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. + return SetupResult::error($msg, $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf'])); } if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { From 32bdababb8e37e78316664ed00591b609f6f7297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:18:24 -0400 Subject: [PATCH 35/68] refactor(setup-check): use placeholder for the debug mode command tip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index b2f8641e5f..6b57c54561 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -36,7 +36,7 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { return [ $l10n->t('Signature data not found.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: %s", ['occ config:system:set debug --value true --type boolean']), ]; } if (isset($result['EMPTY_SIGNATURE_DATA'])) { From 4a8f508e2a4948a09070a1715d32789033f61330 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 10:29:23 -0400 Subject: [PATCH 36/68] refactor(setup-check): apply occ command placeholders and translator hints across checks Signed-off-by: YvesCesar --- lib/SetupCheck/JSignPdfSetupCheck.php | 15 ++++++++++----- lib/SetupCheck/PopplerSetupCheck.php | 5 +++++ lib/SetupCheck/SetupCheckUtils.php | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index c5527a6150..7d154557b6 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -76,7 +76,8 @@ public function run(): SetupResult { if (!$jsignpdfJarPath) { return SetupResult::error( $this->l10n->t('JSignPdf not found'), - $this->l10n->t('Run occ libresign:install --jsignpdf') + // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. + $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf']) ); } @@ -89,7 +90,8 @@ public function run(): SetupResult { if (!file_exists($jsignpdfJarPath)) { return SetupResult::error( $this->l10n->t('JSignPdf binary not found: %s', [$jsignpdfJarPath]), - $this->l10n->t('Run occ libresign:install --jsignpdf') + // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. + $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf']) ); } @@ -97,7 +99,8 @@ public function run(): SetupResult { if (!$javaPath || !file_exists($javaPath)) { return SetupResult::error( $this->l10n->t('Necessary Java to run JSignPdf'), - $this->l10n->t('Run occ libresign:install --java') + // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. + $this->l10n->t('Run %s', ['occ libresign:install --java']) ); } @@ -113,13 +116,15 @@ public function run(): SetupResult { if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { $msg = $this->l10n->t('Necessary bump JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); - return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. + return SetupResult::error($msg, $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf'])); } if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '>')) { return SetupResult::error( $this->l10n->t('Necessary downgrade JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]), - $this->l10n->t('Run occ libresign:install --jsignpdf') + // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. + $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf']) ); } diff --git a/lib/SetupCheck/PopplerSetupCheck.php b/lib/SetupCheck/PopplerSetupCheck.php index 10cd6d2962..8285cd76e5 100644 --- a/lib/SetupCheck/PopplerSetupCheck.php +++ b/lib/SetupCheck/PopplerSetupCheck.php @@ -21,6 +21,7 @@ public function __construct(IL10N $l10n) { #[\Override] public function getName(): string { + // TRANSLATORS Name of the Poppler utilities setup check shown in the admin settings. return $this->l10n->t('Poppler utils'); } @@ -36,17 +37,21 @@ public function run(): SetupResult { if ($pdfsigOk && $pdfinfoOk) { return SetupResult::success( + // TRANSLATORS %s is the pdfsig version, the second %s is the pdfinfo version detected on the server. $this->l10n->t('pdfsig version: %s, pdfinfo version: %s', [$pdfsigOk, $pdfinfoOk]) ); } $messages = []; + // TRANSLATORS Hint shown to the administrator when poppler-utils is not installed on the server. $tip = $this->l10n->t('Install the package poppler-utils on your operating system to enable PDF signature validation and page dimension detection.'); if (!$pdfsigOk) { + // TRANSLATORS Shown when the pdfsig binary from poppler-utils is missing or not working. $messages[] = $this->l10n->t('pdfsig not installed or not working'); } if (!$pdfinfoOk) { + // TRANSLATORS Shown when the pdfinfo binary from poppler-utils is missing or not working. $messages[] = $this->l10n->t('pdfinfo not installed or not working'); } diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index 6b57c54561..c61dd717b1 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -42,7 +42,7 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool if (isset($result['EMPTY_SIGNATURE_DATA'])) { return [ $l10n->t('Your signature data is empty.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: %s", ['occ config:system:set debug --value true --type boolean']), ]; } } From 880a7a28ef24f4023a566259406b2a2e44749f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:41:28 -0400 Subject: [PATCH 37/68] test(setup-check): use native intersection type for Imagick test mock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php b/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php index c242afbcdc..4f3a832abf 100644 --- a/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php @@ -28,8 +28,7 @@ class ImagickSetupCheckTest extends TestCase { /** @var array */ public static array $mockExtensionLoaded = []; - /** @var IL10N&MockObject */ - private $l10n; + private IL10N&MockObject $l10n; /** @var ImagickSetupCheck */ private $check; From dc5860502e9fee22ed4269e88ce4b446aa48cf25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:42:13 -0400 Subject: [PATCH 38/68] test(setup-check): use native intersection type for result service mock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- tests/php/Unit/Service/SetupCheckResultServiceTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index ba03881bf8..d9d80b405f 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -22,8 +22,7 @@ use PHPUnit\Framework\TestCase; class SetupCheckResultServiceTest extends TestCase { - /** @var CertificateEngineFactory&MockObject */ - private $certificateEngineFactory; + private CertificateEngineFactory&MockObject $certificateEngineFactory; /** @var JavaSetupCheck&MockObject */ private $javaSetupCheck; /** @var JSignPdfSetupCheck&MockObject */ From c9b44d187b5c6000eac4641b5048e4b20cb98970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:43:12 -0400 Subject: [PATCH 39/68] test(setup-check): use native intersection type for JSignPdf test mock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index 6871981d46..0e76c5b229 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -33,8 +33,7 @@ function file_exists(string $filename): bool { use Psr\Log\LoggerInterface; class JSignPdfSetupCheckTest extends TestCase { - /** @var IL10N&MockObject */ - private $l10n; + private IL10N&MockObject $l10n; /** @var IAppConfig&MockObject */ private $appConfig; /** @var JSignPdfHandler&MockObject */ From 2f00ba50106aef6682feca27decdc7cf52947b5c Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 10:51:56 -0400 Subject: [PATCH 40/68] test(setup-check): use native intersection types for remaining test mocks Signed-off-by: YvesCesar --- .../Service/SetupCheckResultServiceTest.php | 15 ++++------ .../CertificateEngineSetupCheckTest.php | 6 ++-- .../Unit/SetupCheck/ImagickSetupCheckTest.php | 3 +- .../SetupCheck/JSignPdfSetupCheckTest.php | 29 +++++++------------ .../Unit/SetupCheck/PDFtkSetupCheckTest.php | 24 +++++---------- .../Unit/SetupCheck/PopplerSetupCheckTest.php | 6 ++-- 6 files changed, 28 insertions(+), 55 deletions(-) diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index d9d80b405f..f699928bb3 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -23,16 +23,11 @@ class SetupCheckResultServiceTest extends TestCase { private CertificateEngineFactory&MockObject $certificateEngineFactory; - /** @var JavaSetupCheck&MockObject */ - private $javaSetupCheck; - /** @var JSignPdfSetupCheck&MockObject */ - private $jSignPdfSetupCheck; - /** @var PDFtkSetupCheck&MockObject */ - private $pdftkSetupCheck; - /** @var PopplerSetupCheck&MockObject */ - private $popplerSetupCheck; - /** @var ImagickSetupCheck&MockObject */ - private $imagickSetupCheck; + private JavaSetupCheck&MockObject $javaSetupCheck; + private JSignPdfSetupCheck&MockObject $jSignPdfSetupCheck; + private PDFtkSetupCheck&MockObject $pdftkSetupCheck; + private PopplerSetupCheck&MockObject $popplerSetupCheck; + private ImagickSetupCheck&MockObject $imagickSetupCheck; private SetupCheckResultService $service; public function setUp(): void { diff --git a/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php b/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php index 290f1430bf..8263f86684 100644 --- a/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/CertificateEngineSetupCheckTest.php @@ -23,10 +23,8 @@ public function getName(): string; } final class CertificateEngineSetupCheckTest extends TestCase { - /** @var IL10N&MockObject */ - private $l10n; - /** @var CertificateEngineFactory&MockObject */ - private $certificateEngineFactory; + private IL10N&MockObject $l10n; + private CertificateEngineFactory&MockObject $certificateEngineFactory; protected function setUp(): void { parent::setUp(); diff --git a/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php b/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php index 4f3a832abf..05b24476a3 100644 --- a/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/ImagickSetupCheckTest.php @@ -30,8 +30,7 @@ class ImagickSetupCheckTest extends TestCase { private IL10N&MockObject $l10n; - /** @var ImagickSetupCheck */ - private $check; + private ImagickSetupCheck $check; protected function setUp(): void { parent::setUp(); diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index 0e76c5b229..5c05e4a6f4 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -34,25 +34,16 @@ function file_exists(string $filename): bool { class JSignPdfSetupCheckTest extends TestCase { private IL10N&MockObject $l10n; - /** @var IAppConfig&MockObject */ - private $appConfig; - /** @var JSignPdfHandler&MockObject */ - private $jSignPdfHandler; - /** @var SignSetupService&MockObject */ - private $signSetupService; - /** @var IURLGenerator&MockObject */ - private $urlGenerator; - /** @var IAppManager&MockObject */ - private $appManager; - /** @var LoggerInterface&MockObject */ - private $logger; - /** @var IConfig&MockObject */ - private $systemConfig; - /** @var JavaHelper&MockObject */ - private $javaHelper; - - /** @var JSignPdfSetupCheck */ - private $check; + private IAppConfig&MockObject $appConfig; + private JSignPdfHandler&MockObject $jSignPdfHandler; + private SignSetupService&MockObject $signSetupService; + private IURLGenerator&MockObject $urlGenerator; + private IAppManager&MockObject $appManager; + private LoggerInterface&MockObject $logger; + private IConfig&MockObject $systemConfig; + private JavaHelper&MockObject $javaHelper; + + private JSignPdfSetupCheck $check; protected function setUp(): void { parent::setUp(); diff --git a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php index 5a43703030..eed15e78b0 100644 --- a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php @@ -39,22 +39,14 @@ function exec(string $command, &$output = null, &$result_code = null): string|fa use Psr\Log\LoggerInterface; class PDFtkSetupCheckTest extends TestCase { - /** @var IL10N|MockObject */ - private $l10n; - /** @var IAppConfig|MockObject */ - private $appConfig; - /** @var SignSetupService|MockObject */ - private $signSetupService; - /** @var IURLGenerator|MockObject */ - private $urlGenerator; - /** @var IAppManager|MockObject */ - private $appManager; - /** @var LoggerInterface|MockObject */ - private $logger; - /** @var IConfig|MockObject */ - private $systemConfig; - /** @var JavaHelper|MockObject */ - private $javaHelper; + private IL10N&MockObject $l10n; + private IAppConfig&MockObject $appConfig; + private SignSetupService&MockObject $signSetupService; + private IURLGenerator&MockObject $urlGenerator; + private IAppManager&MockObject $appManager; + private LoggerInterface&MockObject $logger; + private IConfig&MockObject $systemConfig; + private JavaHelper&MockObject $javaHelper; protected function setUp(): void { parent::setUp(); diff --git a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php index 1329bfd2b6..517f9d5bd4 100644 --- a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php @@ -23,11 +23,9 @@ function exec(string $command, &$output = null, &$result_code = null): string|fa use PHPUnit\Framework\TestCase; class PopplerSetupCheckTest extends TestCase { - /** @var IL10N|MockObject */ - private $l10n; + private IL10N&MockObject $l10n; - /** @var PopplerSetupCheck */ - private $check; + private PopplerSetupCheck $check; protected function setUp(): void { parent::setUp(); From 2b6e2e256f23f832372b2aabe42e96dc9c0fc622 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 10:59:26 -0400 Subject: [PATCH 41/68] test(setup-check): use DataProvider attribute instead of docblock annotation Signed-off-by: YvesCesar --- tests/php/Unit/Service/SetupCheckResultServiceTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/php/Unit/Service/SetupCheckResultServiceTest.php b/tests/php/Unit/Service/SetupCheckResultServiceTest.php index f699928bb3..72e21de7a0 100644 --- a/tests/php/Unit/Service/SetupCheckResultServiceTest.php +++ b/tests/php/Unit/Service/SetupCheckResultServiceTest.php @@ -18,6 +18,7 @@ use OCA\Libresign\SetupCheck\PopplerSetupCheck; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -99,9 +100,7 @@ public function testJsonSerializeOmitsCategory(): void { $this->assertSame('info', $serialized['status']); } - /** - * @dataProvider providerSeverityMapping - */ + #[DataProvider('providerSeverityMapping')] public function testSeverityMapping(string $severity, string $expectedStatus): void { $this->javaSetupCheck = $this->createMock(JavaSetupCheck::class); $this->configureCheck($this->javaSetupCheck, 'system', $severity, 'Message', null); From 51ba253880352909eaee0f2d6c74b9e91943faea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 08:29:01 -0400 Subject: [PATCH 42/68] fix(setup-check): clarify JSignPdf file-not-found message and add translator context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 7d154557b6..215f2e6825 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -89,7 +89,10 @@ public function run(): SetupResult { if (!file_exists($jsignpdfJarPath)) { return SetupResult::error( - $this->l10n->t('JSignPdf binary not found: %s', [$jsignpdfJarPath]), + // TRANSLATORS JSignPdf is an optional external signing backend used by LibreSign. + // LibreSign also supports other signing methods, including its native PHP signer. + // %s is the configured JSignPdf path that could not be found. + $this->l10n->t('JSignPdf file not found: %s', [$jsignpdfJarPath]), // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf']) ); From 141410aeac2346be904625859ab4f3ece1e783c3 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 08:37:30 -0400 Subject: [PATCH 43/68] test(setup-check): align JSignPdf assertion with renamed file-not-found message Signed-off-by: YvesCesar --- tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index 5c05e4a6f4..8cbbe9c3b4 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -154,7 +154,7 @@ public function testRunBinaryNotFound(): void { $this->assertInstanceOf(SetupResult::class, $result); $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('JSignPdf binary not found', $result->getDescription()); + $this->assertStringContainsString('JSignPdf file not found', $result->getDescription()); } public function testRunJavaNotFound(): void { From 0d28a3c0acb7cada5e72287e2967d7a4c187cf83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 08:39:27 -0400 Subject: [PATCH 44/68] docs(setup-check): add translator context to JSignPdf check messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 215f2e6825..36727a92bd 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -101,6 +101,8 @@ public function run(): SetupResult { $javaPath = $this->javaHelper->getJavaPath(); if (!$javaPath || !file_exists($javaPath)) { return SetupResult::error( + // TRANSLATORS JSignPdf is an optional external signing backend that requires Java to run. + // LibreSign also supports other signing methods, including its native PHP signer. $this->l10n->t('Necessary Java to run JSignPdf'), // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. $this->l10n->t('Run %s', ['occ libresign:install --java']) From 8a57fcdc10bd6b999299ef5d9f37fcca0ead685d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 08:40:01 -0400 Subject: [PATCH 45/68] docs(setup-check): add translator context to JSignPdf check messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 36727a92bd..0168643688 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -114,6 +114,8 @@ public function run(): SetupResult { $currentVersion = $jsignPdf->getVersion(); if (!$currentVersion) { + // TRANSLATORS: JSignPdf is an optional external signing backend. + // LibreSign is tested/validated with a specific JSignPdf version. %s is the supported JSignPdf version. $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. return SetupResult::error($msg, $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf'])); From a818d13ff13628c25cd0ad5bd2c5abc80146dcf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 08:40:26 -0400 Subject: [PATCH 46/68] docs(setup-check): add translator context to JSignPdf check messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 0168643688..2c6487df39 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -122,7 +122,10 @@ public function run(): SetupResult { } if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { - $msg = $this->l10n->t('Necessary bump JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); + // TRANSLATORS JSignPdf is an optional external signing backend. + // LibreSign is tested/validated with a specific JSignPdf version. + // The first %s is the currently installed JSignPdf version; the second %s is the required supported version. + $msg = $this->l10n->t('JSignPdf must be updated from version %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. return SetupResult::error($msg, $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf'])); } From 512d6207de5d26f48133f7ae23eb6e8a26f88010 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 08:45:41 -0400 Subject: [PATCH 47/68] test(setup-check): align JSignPdf assertion with renamed version-update message Signed-off-by: YvesCesar --- tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index 8cbbe9c3b4..f616ceeb13 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -242,7 +242,7 @@ public function testRunVersionTooLow(): void { $this->assertInstanceOf(SetupResult::class, $result); $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('bump JSignPdf version', $result->getDescription()); + $this->assertStringContainsString('JSignPdf must be updated from version', $result->getDescription()); } public function testRunVersionTooHigh(): void { From 9ee26ec98ea1b091cb282d41fe9bedfe7a1c5124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 08:53:17 -0400 Subject: [PATCH 48/68] test(setup-check): align JSignPdf assertion with renamed version-downgrade message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 2c6487df39..18ab91865d 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -132,7 +132,13 @@ public function run(): SetupResult { if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '>')) { return SetupResult::error( - $this->l10n->t('Necessary downgrade JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]), + // TRANSLATORS JSignPdf is an optional external signing backend. + // LibreSign is tested/validated with a specific JSignPdf version. + // The first %s is the currently installed JSignPdf version; the second %s is the required supported version. + $this->l10n->t( + 'JSignPdf must be downgraded from version %s to %s', + [$currentVersion, InstallService::JSIGNPDF_VERSION], + ), // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. $this->l10n->t('Run %s', ['occ libresign:install --jsignpdf']) ); From 003b6e975d663b7f6227832a23941c97609cbe58 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 08:55:07 -0400 Subject: [PATCH 49/68] test(setup-check): align JSignPdf assertion with renamed version-downgrade message Signed-off-by: YvesCesar --- tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index f616ceeb13..674e478b11 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -272,7 +272,7 @@ public function testRunVersionTooHigh(): void { $this->assertInstanceOf(SetupResult::class, $result); $this->assertSame('error', $result->getSeverity()); - $this->assertStringContainsString('downgrade JSignPdf version', $result->getDescription()); + $this->assertStringContainsString('JSignPdf must be downgraded from version', $result->getDescription()); } public function testRunSuccess(): void { From f5891cdecf05b66e6a0e507ee2b10e976c7d57a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 08:59:46 -0400 Subject: [PATCH 50/68] docs(setup-check): add translator context to JSignPdf success messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 18ab91865d..b21d432446 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -145,8 +145,11 @@ public function run(): SetupResult { } $messages = [ + // TRANSLATORS: JSignPdf is an optional external signing backend. %s is the detected JSignPdf version. $this->l10n->t('JSignPdf version: %s', [$currentVersion]), - $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]) + + // TRANSLATORS: JSignPdf is an optional external signing backend. %s is the configured or detected path to the JSignPdf executable/JAR file. + $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]), ]; return SetupResult::success(implode("\n", $messages)); From 3d37a21266872aba36ddc464388f76ef0d24c1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:00:13 -0400 Subject: [PATCH 51/68] refactor(setup-check): do not translate JSignPdf product name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/JSignPdfSetupCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index b21d432446..5838b0338b 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -60,7 +60,7 @@ public function __construct( #[\Override] public function getName(): string { - return $this->l10n->t('JSignPdf'); + return 'JSignPdf'; } #[\Override] From c02ea5086d142b2d2698bd71f2a56cb05ccb0d1b Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:01:51 -0400 Subject: [PATCH 52/68] test(setup-check): align JSignPdf getName test with untranslated product name Signed-off-by: YvesCesar --- tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php index 674e478b11..3aa551278a 100644 --- a/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JSignPdfSetupCheckTest.php @@ -85,10 +85,6 @@ private function mockTranslation(): void { } public function testGetName(): void { - $this->l10n->expects($this->once()) - ->method('t') - ->with('JSignPdf') - ->willReturn('JSignPdf'); $this->assertSame('JSignPdf', $this->check->getName()); } From 72f8c6cbb0a2eb978a4c53c60a670c61378289a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:05:14 -0400 Subject: [PATCH 53/68] refactor(setup-check): clarify binary integrity signature-not-found message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index c61dd717b1..cb0b33c7f6 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -35,7 +35,8 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool if (count($result) === 1 && !$debugEnabled) { if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { return [ - $l10n->t('Signature data not found.'), + // TRANSLATORS This refers to LibreSign binary integrity verification metadata, not to a user's document signature. LibreSign validates approved signing binaries using maintainer-signed metadata shipped with the app. + $l10n->t('Binary integrity signature data not found.'), $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: %s", ['occ config:system:set debug --value true --type boolean']), ]; } From 0786d0e9954a4e64d30640d29350e681e91307a8 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:11:39 -0400 Subject: [PATCH 54/68] test(setup-check): align assertions with binary integrity not-found message Signed-off-by: YvesCesar --- tests/php/Unit/SetupCheck/JavaSetupCheckTest.php | 2 +- tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php index 86e4204f35..4846ea5fdf 100644 --- a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php @@ -336,7 +336,7 @@ public function testVerifyResourceIntegritySignatureDataNotFoundWithoutDebug(): $result = $instance->run(); $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Signature data not found', $result->getDescription()); + $this->assertStringContainsString('Binary integrity signature data not found', $result->getDescription()); $this->assertStringContainsString('running from source code', $result->getLinkToDoc()); } diff --git a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php index eed15e78b0..8571a202da 100644 --- a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php @@ -141,7 +141,7 @@ public function testRunWithVerifyError(): void { $this->assertInstanceOf(SetupResult::class, $result); $this->assertEquals(SetupResult::ERROR, $result->getSeverity()); - $this->assertStringContainsString('Signature data not found', $result->getDescription()); + $this->assertStringContainsString('Binary integrity signature data not found', $result->getDescription()); } public function testRunFileNotExists(): void { From d86873542c8834a1b909a23a5593069cb7f8c88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:15:06 -0400 Subject: [PATCH 55/68] refactor(setup-check): clarify binary integrity empty signature message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index cb0b33c7f6..176924f13f 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -42,7 +42,8 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool } if (isset($result['EMPTY_SIGNATURE_DATA'])) { return [ - $l10n->t('Your signature data is empty.'), + // TRANSLATORS This refers to LibreSign binary integrity verification metadata, not to a user's document signature. LibreSign validates approved signing binaries using maintainer-signed metadata shipped with the app. + $l10n->t('Binary integrity signature data is empty.'), $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: %s", ['occ config:system:set debug --value true --type boolean']), ]; } From b33254ee2d37eeb163f4a2048091419560487cdb Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:16:55 -0400 Subject: [PATCH 56/68] refactor(setup-check): clarify binary integrity empty signature message Signed-off-by: YvesCesar --- tests/php/Unit/SetupCheck/JavaSetupCheckTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php index 4846ea5fdf..af764d64a0 100644 --- a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php @@ -359,7 +359,7 @@ public function testVerifyResourceIntegrityEmptySignatureDataWithoutDebug(): voi $result = $instance->run(); $this->assertEquals('error', $result->getSeverity()); - $this->assertStringContainsString('Your signature data is empty', $result->getDescription()); + $this->assertStringContainsString('Binary integrity signature data is empty', $result->getDescription()); } public function testVerifyResourceIntegritySignatureDataNotFoundWithDebug(): void { From b81ca543f4b6988efbb60b74befa8db1d978b358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:21:26 -0400 Subject: [PATCH 57/68] refactor(setup-check): improve running-from-source debug hint wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index 176924f13f..d4e9a8e1ed 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -37,7 +37,8 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool return [ // TRANSLATORS This refers to LibreSign binary integrity verification metadata, not to a user's document signature. LibreSign validates approved signing binaries using maintainer-signed metadata shipped with the app. $l10n->t('Binary integrity signature data not found.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: %s", ['occ config:system:set debug --value true --type boolean']), + // TRANSLATORS This tip is shown when LibreSign seems to be running from source code instead of an official packaged release. %s is the occ command to enable debug mode. + $l10n->t("It looks like this LibreSign instance is running from source code.\nEnable debug mode by running %s", ['occ config:system:set debug --value true --type boolean']), ]; } if (isset($result['EMPTY_SIGNATURE_DATA'])) { From d05fc59270afdfe10c40b8bf754e3454eefce1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:21:49 -0400 Subject: [PATCH 58/68] refactor(setup-check): improve running-from-source debug hint wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index d4e9a8e1ed..8a107453dc 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -45,7 +45,8 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool return [ // TRANSLATORS This refers to LibreSign binary integrity verification metadata, not to a user's document signature. LibreSign validates approved signing binaries using maintainer-signed metadata shipped with the app. $l10n->t('Binary integrity signature data is empty.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: %s", ['occ config:system:set debug --value true --type boolean']), + // TRANSLATORS This tip is shown when LibreSign seems to be running from source code instead of an official packaged release. %s is the occ command to enable debug mode. + $l10n->t("It looks like this LibreSign instance is running from source code.\nEnable debug mode by running %s", ['occ config:system:set debug --value true --type boolean']), ]; } } From af2ec817812e0f4891b752c2523c23e8c9c794e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:23:25 -0400 Subject: [PATCH 59/68] docs(setup-check): add translator context to invalid-hash messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index 8a107453dc..3c88f80bb9 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -61,6 +61,7 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool $this->logger->error('Invalid hash of binaries files', ['result' => $result]); if ($this->appManager->isEnabledForUser('logreader')) { return [ + // TRANSLATORS This is a security/integrity check failure. LibreSign only accepts approved signing binaries whose hashes match maintainer-signed metadata shipped with the app. Even a one-bit change makes the binary invalid. $l10n->t('Invalid hash of binaries files.'), $l10n->t('Check your nextcloud.log file on %s and run occ libresign:install --all', [ $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) From bd2c5d36071db03c1adb17abaec29c989107f6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:23:42 -0400 Subject: [PATCH 60/68] docs(setup-check): add translator context to invalid-hash messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index 3c88f80bb9..48d47da239 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -53,6 +53,7 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool if (isset($result['HASH_FILE_ERROR'])) { if ($debugEnabled) { return [ + // TRANSLATORS This is a security/integrity check failure. LibreSign only accepts approved signing binaries whose hashes match maintainer-signed metadata shipped with the app. Even a one-bit change makes the binary invalid. $l10n->t('Invalid hash of binaries files.'), $l10n->t('Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --%s --use-local-cert', [$resource]), ]; From ec92d2ee768313b1e2670d87924e61ce7cc84bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:23:56 -0400 Subject: [PATCH 61/68] docs(setup-check): add translator context to invalid-hash messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index 48d47da239..8c5c4e268a 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -70,6 +70,7 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool ]; } return [ + // TRANSLATORS This is a security/integrity check failure. LibreSign only accepts approved signing binaries whose hashes match maintainer-signed metadata shipped with the app. Even a one-bit change makes the binary invalid. $l10n->t('Invalid hash of binaries files.'), $l10n->t('Check your nextcloud.log file and run occ libresign:install --all'), ]; From 7fd4885cdc96ee50069ee2bedd359f7fd0fd09ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:27:29 -0400 Subject: [PATCH 62/68] docs(setup-check): add translator context to invalid-hash messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index 8c5c4e268a..ec49db7dee 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -64,6 +64,7 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool return [ // TRANSLATORS This is a security/integrity check failure. LibreSign only accepts approved signing binaries whose hashes match maintainer-signed metadata shipped with the app. Even a one-bit change makes the binary invalid. $l10n->t('Invalid hash of binaries files.'), + // TRANSLATORS %s is the occ command that downloads/reinstalls the approved LibreSign binaries. $l10n->t('Check your nextcloud.log file on %s and run occ libresign:install --all', [ $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) ]), From 7f8ba72446c730174ee0408716d71970a6d03dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20C=C3=A9sar=20Amorim=20de=20Azevedo?= <48072419+YvesCesar@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:27:42 -0400 Subject: [PATCH 63/68] docs(setup-check): add translator context to invalid-hash messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Mattos Signed-off-by: Yves César Amorim de Azevedo <48072419+YvesCesar@users.noreply.github.com> --- lib/SetupCheck/SetupCheckUtils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index ec49db7dee..9d6d9d1a03 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -73,6 +73,7 @@ private function getErrorAndTipFromVerify(array $result, string $resource, bool return [ // TRANSLATORS This is a security/integrity check failure. LibreSign only accepts approved signing binaries whose hashes match maintainer-signed metadata shipped with the app. Even a one-bit change makes the binary invalid. $l10n->t('Invalid hash of binaries files.'), + // TRANSLATORS %s is the occ command that downloads/reinstalls the approved LibreSign binaries. $l10n->t('Check your nextcloud.log file and run occ libresign:install --all'), ]; } From c4a61e8833c5062f8e97d3e7825511a3a1c399d5 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:33:09 -0400 Subject: [PATCH 64/68] refactor(setup-check): do not translate Poppler, Java and PDFtk product names Signed-off-by: YvesCesar --- lib/SetupCheck/JavaSetupCheck.php | 2 +- lib/SetupCheck/PDFtkSetupCheck.php | 2 +- lib/SetupCheck/PopplerSetupCheck.php | 3 +-- tests/php/Unit/SetupCheck/JavaSetupCheckTest.php | 1 - tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php | 4 ---- tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php | 4 ---- 6 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/SetupCheck/JavaSetupCheck.php b/lib/SetupCheck/JavaSetupCheck.php index 864676db55..f59e8cc30b 100644 --- a/lib/SetupCheck/JavaSetupCheck.php +++ b/lib/SetupCheck/JavaSetupCheck.php @@ -51,7 +51,7 @@ public function __construct( #[\Override] public function getName(): string { - return $this->l10n->t('Java'); + return 'Java'; } #[\Override] diff --git a/lib/SetupCheck/PDFtkSetupCheck.php b/lib/SetupCheck/PDFtkSetupCheck.php index 8086b61dbc..ec1265d0e2 100644 --- a/lib/SetupCheck/PDFtkSetupCheck.php +++ b/lib/SetupCheck/PDFtkSetupCheck.php @@ -56,7 +56,7 @@ public function __construct( #[\Override] public function getName(): string { - return $this->l10n->t('PDFtk'); + return 'PDFtk'; } #[\Override] diff --git a/lib/SetupCheck/PopplerSetupCheck.php b/lib/SetupCheck/PopplerSetupCheck.php index 8285cd76e5..eae3e5ed78 100644 --- a/lib/SetupCheck/PopplerSetupCheck.php +++ b/lib/SetupCheck/PopplerSetupCheck.php @@ -21,8 +21,7 @@ public function __construct(IL10N $l10n) { #[\Override] public function getName(): string { - // TRANSLATORS Name of the Poppler utilities setup check shown in the admin settings. - return $this->l10n->t('Poppler utils'); + return 'Poppler utils'; } #[\Override] diff --git a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php index af764d64a0..0d7f528749 100644 --- a/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/JavaSetupCheckTest.php @@ -307,7 +307,6 @@ public function testJavaEncodingNonUtf8(): void { } public function testGetName(): void { - $this->l10n->method('t')->willReturnArgument(0); $instance = $this->getInstance(); $this->assertEquals('Java', $instance->getName()); } diff --git a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php index 8571a202da..e51ae481d1 100644 --- a/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PDFtkSetupCheckTest.php @@ -89,10 +89,6 @@ private function getInstance(): PDFtkSetupCheck { } public function testGetName(): void { - $this->l10n->expects($this->once()) - ->method('t') - ->with('PDFtk') - ->willReturn('PDFtk'); $check = $this->getInstance(); $this->assertEquals('PDFtk', $check->getName()); } diff --git a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php index 517f9d5bd4..a69866a72e 100644 --- a/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php +++ b/tests/php/Unit/SetupCheck/PopplerSetupCheckTest.php @@ -146,10 +146,6 @@ public function testBothToolsParseFails(): void { } public function testGetName(): void { - $this->l10n->expects($this->once()) - ->method('t') - ->with('Poppler utils') - ->willReturn('Poppler utils'); $this->assertEquals('Poppler utils', $this->check->getName()); } From 6a2633bcfe8acc7909c5dfca97e2987bbbdad22d Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 28 Jun 2026 17:18:43 -0300 Subject: [PATCH 65/68] chore: Update lib/SetupCheck/JSignPdfSetupCheck.php Signed-off-by: Vitor Mattos --- lib/SetupCheck/JSignPdfSetupCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 5838b0338b..05402c62ff 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -148,7 +148,7 @@ public function run(): SetupResult { // TRANSLATORS: JSignPdf is an optional external signing backend. %s is the detected JSignPdf version. $this->l10n->t('JSignPdf version: %s', [$currentVersion]), - // TRANSLATORS: JSignPdf is an optional external signing backend. %s is the configured or detected path to the JSignPdf executable/JAR file. + // TRANSLATORS JSignPdf is an optional external signing backend. %s is the configured or detected path to the JSignPdf executable/JAR file. $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]), ]; From 083047ba0baebaa9ce3fbc5db87829f57091cb08 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 28 Jun 2026 17:18:56 -0300 Subject: [PATCH 66/68] chore: Update lib/SetupCheck/JSignPdfSetupCheck.php Signed-off-by: Vitor Mattos --- lib/SetupCheck/JSignPdfSetupCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 05402c62ff..20312585bd 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -145,7 +145,7 @@ public function run(): SetupResult { } $messages = [ - // TRANSLATORS: JSignPdf is an optional external signing backend. %s is the detected JSignPdf version. + // TRANSLATORS JSignPdf is an optional external signing backend. %s is the detected JSignPdf version. $this->l10n->t('JSignPdf version: %s', [$currentVersion]), // TRANSLATORS JSignPdf is an optional external signing backend. %s is the configured or detected path to the JSignPdf executable/JAR file. From 14dda5c692c7ea0c2a6605e85b29065a6becce60 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 28 Jun 2026 17:19:10 -0300 Subject: [PATCH 67/68] chore: Update lib/SetupCheck/JSignPdfSetupCheck.php Signed-off-by: Vitor Mattos [skip ci] --- lib/SetupCheck/JSignPdfSetupCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 20312585bd..60f85d971e 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -114,7 +114,7 @@ public function run(): SetupResult { $currentVersion = $jsignPdf->getVersion(); if (!$currentVersion) { - // TRANSLATORS: JSignPdf is an optional external signing backend. + // TRANSLATORS JSignPdf is an optional external signing backend. // LibreSign is tested/validated with a specific JSignPdf version. %s is the supported JSignPdf version. $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); // TRANSLATORS Command to run into terminal using Nextcloud occ to configure LibreSign using CLI when the sysadmin want to do this by CLI. From 303d9c75f70933783bc1739df6fe1633cb5c24f1 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:51:01 -0300 Subject: [PATCH 68/68] chore: delete skipped configure check remnants Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/Install/ConfigureCheckService.php | 555 ------------------ .../Install/ConfigureCheckServiceTest.php | 105 ---- 2 files changed, 660 deletions(-) delete mode 100644 lib/Service/Install/ConfigureCheckService.php delete mode 100644 tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php diff --git a/lib/Service/Install/ConfigureCheckService.php b/lib/Service/Install/ConfigureCheckService.php deleted file mode 100644 index 30b2f069fb..0000000000 --- a/lib/Service/Install/ConfigureCheckService.php +++ /dev/null @@ -1,555 +0,0 @@ -architecture = php_uname('m'); - } - - public function disableCache(): void { - $this->isCacheDisabled = true; - } - - /** - * Get result of all checks - * - * @return ConfigureCheckHelper[] - */ - public function checkAll(): array { - if ($this->isCacheDisabled) { - $this->ocAppConfig->clearCache(); - } - $result = []; - $result = array_merge($result, $this->checkSign()); - $result = array_merge($result, $this->checkCertificate()); - return $result; - } - - /** - * Check all requirements to sign - * - * @return ConfigureCheckHelper[] - */ - public function checkSign(): array { - $return = []; - $return = array_merge($return, $this->checkJava()); - $return = array_merge($return, $this->checkPdftk()); - $return = array_merge($return, $this->checkJSignPdf()); - $return = array_merge($return, $this->checkPoppler()); - $return = array_merge($return, $this->checkImagick()); - return $return; - } - - public function checkPoppler(): array { - $return = $this->checkPdfSig(); - $return = array_merge($return, $this->checkPdfinfo()); - return $return; - } - - public function checkPdfSig(): array { - if (!empty($this->result['poppler'])) { - return $this->result['poppler']; - } - // The output of this command go to STDERR and exec get the STDOUT - // With 2>&1 the STRERR is redirected to STDOUT - exec('pdfsig -v 2>&1', $version, $retval); - if ($retval !== 0) { - return $this->result['poppler'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage('Poppler utils not installed') - ->setResource('pdfsig') - ->setTip('Install the package poppler-utils at your operational system to be possible get more details about validation of signatures.'), - ]; - } - if (!$version) { - return $this->result['poppler'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfsig version') - ->setResource('pdfsig') - ->setTip("The command executed by PHP haven't any output."), - ]; - } - $returnValue = preg_match('/pdfsig version (?.*)/', implode(PHP_EOL, $version), $matches); - if ($returnValue !== 1) { - return $this->result['poppler'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfsig version') - ->setResource('pdfsig') - ->setTip("This is a poppler-utils dependency and wasn't possible to parse the output of command pdfsig -v"), - ]; - } - return $this->result['poppler'] = [(new ConfigureCheckHelper()) - ->setSuccessMessage('pdfsig version: ' . $matches['version']) - ->setResource('pdfsig') - ]; - } - - public function checkPdfinfo(): array { - if (!empty($this->result['pdfinfo'])) { - return $this->result['pdfinfo']; - } - // The output of this command go to STDERR and exec get the STDOUT - // With 2>&1 the STRERR is redirected to STDOUT - exec('pdfinfo -v 2>&1', $version, $retval); - if ($retval !== 0) { - return $this->result['pdfinfo'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage('Poppler utils not installed') - ->setResource('pdfinfo') - ->setTip('Install the package poppler-utils at your operational system have a fallback to fetch page dimensions.'), - ]; - } - if (!$version) { - return $this->result['pdfinfo'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfinfo version') - ->setResource('pdfinfo') - ->setTip("The command executed by PHP haven't any output."), - ]; - } - $returnValue = preg_match('/pdfinfo version (?.*)/', implode(PHP_EOL, $version), $matches); - if (!$returnValue) { - return $this->result['pdfinfo'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfinfo version') - ->setResource('pdfinfo') - ->setTip("This is a poppler-utils dependency and wasn't possible to parse the output of command pdfinfo -v"), - ]; - } - return $this->result['pdfinfo'] = [(new ConfigureCheckHelper()) - ->setSuccessMessage('pdfinfo version: ' . $matches['version']) - ->setResource('pdfinfo') - ]; - } - - /** - * Check all requirements to use JSignPdf - * - * @return ConfigureCheckHelper[] - */ - public function checkJSignPdf(): array { - if (!empty($this->result['jsignpdf'])) { - return $this->result['jsignpdf']; - } - - $signatureEngine = $this->appConfig->getValueString(Application::APP_ID, 'signature_engine', 'JSignPdf'); - if ($signatureEngine !== 'JSignPdf') { - return []; - } - - $jsignpdJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path'); - if ($jsignpdJarPath) { - $resultOfVerify = $this->verify('jsignpdf'); - if (count($resultOfVerify)) { - [$errorMessage, $tip] = $this->getErrorAndTipToResultOfVerify($resultOfVerify, 'jsignpdf'); - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($errorMessage) - ->setResource('jsignpdf') - ->setTip($tip), - ]; - } - if (file_exists($jsignpdJarPath)) { - if (!$this->isJavaOk()) { - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Necessary Java to run JSignPdf') - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --java'), - ]; - } - $jsignPdf = $this->jSignPdfHandler->getJSignPdf(); - $jsignPdf->setParam($this->jSignPdfHandler->getJSignParam()); - $currentVersion = $jsignPdf->getVersion(); - $return = []; - if ($currentVersion < InstallService::JSIGNPDF_VERSION) { - if (!$currentVersion) { - $message = 'Necessary install the version ' . InstallService::JSIGNPDF_VERSION; - } else { - $message = 'Necessary bump JSignPdf versin from ' . $currentVersion . ' to ' . InstallService::JSIGNPDF_VERSION; - } - $return[] = (new ConfigureCheckHelper()) - ->setErrorMessage($message) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'); - } - if ($currentVersion > InstallService::JSIGNPDF_VERSION) { - $return[] = (new ConfigureCheckHelper()) - ->setErrorMessage('Necessary downgrade JSignPdf versin from ' . $currentVersion . ' to ' . InstallService::JSIGNPDF_VERSION) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'); - } - $return[] = (new ConfigureCheckHelper()) - ->setSuccessMessage('JSignPdf version: ' . $currentVersion) - ->setResource('jsignpdf'); - $return[] = (new ConfigureCheckHelper()) - ->setSuccessMessage('JSignPdf path: ' . $jsignpdJarPath) - ->setResource('jsignpdf'); - return $return; - } - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('JSignPdf binary not found: ' . $jsignpdJarPath) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'), - ]; - } - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('JSignPdf not found') - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'), - ]; - } - - /** - * Check all requirements to use PDFtk - * - * @return ConfigureCheckHelper[] - */ - public function checkPdftk(): array { - if (!empty($this->result['pdftk'])) { - return $this->result['pdftk']; - } - $pdftkPath = $this->appConfig->getValueString(Application::APP_ID, 'pdftk_path'); - if ($pdftkPath) { - $resultOfVerify = $this->verify('pdftk'); - if (count($resultOfVerify)) { - [$errorMessage, $tip] = $this->getErrorAndTipToResultOfVerify($resultOfVerify, 'pdftk'); - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($errorMessage) - ->setResource('pdftk') - ->setTip($tip), - ]; - } - if (file_exists($pdftkPath)) { - if (!$this->isJavaOk()) { - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Necessary Java to run PDFtk') - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --java'), - ]; - } - $javaPath = $this->javaHelper->getJavaPath(); - $version = []; - \exec($javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1', $version, $resultCode); - if ($resultCode !== 0) { - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Failure to check PDFtk version.') - ->setResource('java') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - if (isset($version[0])) { - preg_match('/pdftk port to java (?.*) a Handy Tool/', $version[0], $matches); - if (isset($matches['version'])) { - if ($matches['version'] === InstallService::PDFTK_VERSION) { - return $this->result['pdftk'] = [ - $this->result['pdftk'][] = (new ConfigureCheckHelper()) - ->setSuccessMessage('PDFtk version: ' . InstallService::PDFTK_VERSION) - ->setResource('pdftk'), - $this->result['pdftk'][] = (new ConfigureCheckHelper()) - ->setSuccessMessage('PDFtk path: ' . $pdftkPath) - ->setResource('pdftk'), - ]; - } - $message = 'Necessary install the version ' . InstallService::PDFTK_VERSION; - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($message) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf') - ]; - } - } - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('PDFtk binary is invalid: ' . $pdftkPath) - ->setResource('pdftk') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('PDFtk binary not found: ' . $pdftkPath) - ->setResource('pdftk') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('PDFtk not found') - ->setResource('pdftk') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - - public function isDebugEnabled(): bool { - return $this->systemConfig->getValue('debug', false) === true; - } - - private function verify(string $resource): array { - $this->signSetupService->willUseLocalCert($this->isDebugEnabled()); - $result = $this->signSetupService->verify($this->architecture, $resource); - if (count($result) === 1 && $this->isDebugEnabled()) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { - return []; - } - if (isset($result['EMPTY_SIGNATURE_DATA'])) { - return []; - } - } - return $result; - } - - private function getErrorAndTipToResultOfVerify(array $result, string $resource): array { - if (count($result) === 1 && !$this->isDebugEnabled()) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { - return [ - 'Signature data not found.', - "Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean", - ]; - } - if (isset($result['EMPTY_SIGNATURE_DATA'])) { - return [ - 'Your signature data is empty.', - "Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean", - ]; - } - } - if (isset($result['HASH_FILE_ERROR'])) { - if ($this->isDebugEnabled()) { - return [ - 'Invalid hash of binaries files.', - 'Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --' . $resource . ' --use-local-cert', - ]; - } - } - $this->logger->error('Invalid hash of binaries files', ['result' => $result]); - if ($this->appManager->isEnabledForUser('logreader')) { - return [ - 'Invalid hash of binaries files.', - 'Check your nextcloud.log file on ' - . $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) - . ' and run occ libresign:install --all', - ]; - } - return [ - 'Invalid hash of binaries files.', - 'Check your nextcloud.log file and run occ libresign:install --all', - ]; - } - - /** - * Check all requirements to use Java - * - * @return ConfigureCheckHelper[] - */ - private function checkJava(): array { - if (!empty($this->result['java'])) { - return $this->result['java']; - } - - $javaPath = $this->javaHelper->getJavaPath(); - if ($javaPath) { - $resultOfVerify = $this->verify('java'); - if (count($resultOfVerify)) { - [$errorMessage, $tip] = $this->getErrorAndTipToResultOfVerify($resultOfVerify, 'java'); - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($errorMessage) - ->setResource('java') - ->setTip($tip), - ]; - } - if (file_exists($javaPath)) { - \exec($javaPath . ' -version 2>&1', $javaVersion, $resultCode); - if (empty($javaVersion)) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage( - 'Failed to execute Java. Sounds that your operational system is blocking the JVM.' - ) - ->setResource('java') - ->setTip('https://github.com/LibreSign/libresign/issues/2327#issuecomment-1961988790'), - ]; - } - if ($resultCode !== 0) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Failure to check Java version.') - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - $javaVersion = current($javaVersion); - if ($javaVersion !== InstallService::JAVA_VERSION) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage( - sprintf( - 'Invalid java version. Found: %s expected: %s', - $javaVersion, - InstallService::JAVA_VERSION - ) - ) - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - \exec($javaPath . ' -XshowSettings:properties -version 2>&1', $output, $resultCode); - preg_match('/native.encoding = (?.*)\n/', implode("\n", $output), $matches); - if (!isset($matches['encoding'])) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Java encoding not found.') - ->setResource('java') - ->setTip(sprintf('The command %s need to have native.encoding', $javaPath . ' -XshowSettings:properties -version')), - ]; - } - if (!str_contains($matches['encoding'], 'UTF-8')) { - $detectedEncoding = trim($matches['encoding']); - $phpLocale = setlocale(LC_CTYPE, 0); - $phpLcAll = getenv('LC_ALL'); - $phpLang = getenv('LANG'); - - $tip = sprintf( - "Java detected encoding \"%s\" but UTF-8 is required.\n\n" - . "**Current PHP environment:**\n" - . "- LC_CTYPE: %s\n" - . "- LC_ALL: %s\n" - . "- LANG: %s\n\n" - . "**To fix this issue:**\n" - . "1. Set LC_ALL and LANG environment variables (e.g., LC_ALL=en_US.UTF-8) for your web server user\n" - . "2. Restart your web server after making changes\n" - . "3. Verify with command: `locale charmap` (should return UTF-8)\n\n" - . 'For more details, see: [Issue #4872](https://github.com/LibreSign/libresign/issues/4872)', - $detectedEncoding, - $phpLocale ?: 'not set', - $phpLcAll ?: 'not set', - $phpLang ?: 'not set' - ); - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage(sprintf( - 'Non-UTF-8 encoding detected: %s. This may cause issues with accented or special characters', - $detectedEncoding - )) - ->setResource('java') - ->setTip($tip), - ]; - } - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setSuccessMessage('Java version: ' . $javaVersion) - ->setResource('java'), - (new ConfigureCheckHelper()) - ->setSuccessMessage('Java binary: ' . $javaPath) - ->setResource('java'), - ]; - } - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Java binary not found: ' . $javaPath) - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Java not installed') - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - - private function isJavaOk() : bool { - $checkJava = $this->checkJava(); - $error = array_filter( - $checkJava, - fn (ConfigureCheckHelper $config) => $config->getStatus() === 'error' - ); - return empty($error); - } - - /** - * Check all requirements to use certificate - * - * @return ConfigureCheckHelper[] - */ - public function checkCertificate(): array { - try { - $return = $this->certificateEngineFactory->getEngine()->configureCheck(); - } catch (\Throwable) { - $return = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Define the certificate engine to use') - ->setResource('certificate-engine') - ->setTip(sprintf('Run occ libresign:configure:%s --help', - $this->certificateEngineFactory->getEngine()->getName() - )), - ]; - } - return $return; - } - - /** - * Check if Imagick extension is loaded - * - * @return ConfigureCheckHelper[] - */ - public function checkImagick(): array { - if (!empty($this->result['imagick'])) { - return $this->result['imagick']; - } - if (!extension_loaded('imagick')) { - return $this->result['imagick'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage('Imagick extension is not loaded') - ->setResource('imagick') - ->setTip('Install php-imagick to enable visible signatures, background images, and signature element rendering.'), - ]; - } - return $this->result['imagick'] = []; - } -} diff --git a/tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php b/tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php deleted file mode 100644 index dd089c2d4a..0000000000 --- a/tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php +++ /dev/null @@ -1,105 +0,0 @@ -appConfig = $this->getMockAppConfigWithReset(); - $this->systemConfig = $this->createMock(SystemConfig::class); - $this->ocAppConfig = $this->createMock(AppConfig::class); - $this->appManager = $this->createMock(IAppManager::class); - $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->jSignPdfHandler = $this->createMock(JSignPdfHandler::class); - $this->certificateEngineFactory = $this->createMock(CertificateEngineFactory::class); - $this->signSetupService = $this->createMock(SignSetupService::class); - $this->logger = $this->createMock(LoggerInterface::class); - $this->javaHelper = $this->createMock(JavaHelper::class); - } - - public function tearDown(): void { - self::$mockExtensionLoaded = []; - } - - private function getInstance(): ConfigureCheckService { - return new ConfigureCheckService( - $this->appConfig, - $this->systemConfig, - $this->ocAppConfig, - $this->appManager, - $this->urlGenerator, - $this->jSignPdfHandler, - $this->certificateEngineFactory, - $this->signSetupService, - $this->logger, - $this->javaHelper, - ); - } - - public static function providerCheckExtension(): array { - return [ - 'extension not loaded' => ['imagick', false, 1], - 'extension loaded' => ['imagick', true, 0], - ]; - } - - #[DataProvider('providerCheckExtension')] - public function testCheckExtension(string $extension, bool $extensionLoaded, int $expectedCount): void { - self::$mockExtensionLoaded[$extension] = $extensionLoaded; - - $service = $this->getInstance(); - $result = $service->checkImagick(); - - $this->assertIsArray($result); - $this->assertCount($expectedCount, $result); - - if ($expectedCount > 0) { - $this->assertInstanceOf(ConfigureCheckHelper::class, $result[0]); - $this->assertEquals($extension, $result[0]->getResource()); - } - } -}