From e5b8e10306e435590c6ff1ebf955294f7f38aa11 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 1c995cb6c6eb3c20588dd3066a1e4f709284a568 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 2f0d0065f0f1d15628af081f50033b5cb7d249fb 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 ea874c751f0f9de8c834305882fa948a19f2dc87 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 d950a1c4aa0fc662620061f2c6ddc2504c43ebf9 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 b7580b64e51fa4eea3106fa0f0a9e4cfc24c7da7 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 1f939e1303c355d9a15ff153dbca73b098e37f99 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 3cf41c4fcefb69e1c248ee405faac43e00ccb822 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 1a047045438be12fca98556c9c886964ee86d58a 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 2c835691f074f9ca3752e81f649f3eea6c6a8917 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 2017353558..ec3a8a6d4d 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 ee38a972390721d342757e66fe036daa6cb639ec 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 4794c3a11314d00f1de2dc007b6f01c41b722e0f 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 ec3a8a6d4d..9453a9428c 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 f18b4dd4ae9f2e7ca4fc4908f2283869ac9a9bc0 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 d5651564c909963a35ceb9b45df96d26d1b42143 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 1d236451fe3758285c25c5a9441c75e1419a8fd6 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 e749f655ccec5f7df2c4fca64845fbf266bcc154 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 f11fa788facd5a04acaa65ec5d94d9bb06650d82 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 a216cc8c8b..09f7efef8e 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 4d3242beb6..46f94a1ff5 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 59fddbfc01159dc70d3574695d69de64ff710959 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 b318cbe2b40dbb008e07ec3f612ff99c9117dd6b 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 c93cba0c162d46a20a71123e422141c21411778c 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 4fb87d80db3069aa9a0bbf6d2f8cb11f6fab123e 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 6b28842d740130d7a1431952874b601a7779ab92 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 d4a418deb87a3059203550dfdcf7676b59cc3e45 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 09f7efef8e..15684efa3d 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 de76bcbd78774b59e9ceae356fb1f07e830e30a6 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 2d9c549b762337fdd849b062a52275bd84ac1519 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 566b68aebd..419420bfab 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 a1af774f75..fa0210670f 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -448,6 +448,7 @@ "type": "string", "enum": [ "error", + "info", "success" ] }, diff --git a/src/types/openapi/openapi-administration.ts b/src/types/openapi/openapi-administration.ts index 7f4288913e..6d5bc96ba0 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 65e4421a53..3f3cb45bf5 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -1656,7 +1656,7 @@ export type components = { message: string; resource: string; /** @enum {string} */ - status: "error" | "success"; + status: "error" | "info" | "success"; tip: string; }; ConfigureChecksResponse: components["schemas"]["ConfigureCheck"][]; From 3ced85d09525578c0c05e3d25672439927d9a8b9 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 02b6f70d71cfdf9857e289cb2a3e0e707519951b 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 0262acb49d955e07f85b2bcf4f658f43107f964c 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 de09e063b352f177399e864ea6a8705245007dc3 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 9af337e7f5cfcfa42ca4c8f9e999395bcf3e671a 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 2c8ca07e2bc70441d6085e4cc2fcfb721fcaf154 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 2047dd7d3d65ee96db01b8b7f16a7c1fc18bed81 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 4dbfb6f5a7824511ace209c7211247e084057e7c 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 fa0937af79cfb38dfaecc9bd7b0946cfab5a34e3 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 22b4ac74be97690f59eff2a5096e33520046dae7 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 aee82f0e2487b279a8451356b805035d5a968031 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 dc8a9279329bfbb03b5beddf268b631932e5f91e 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 db5e2f7949e9221a2b048531c8610184efbc30f7 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 89fb5ef38eb2cb8ff097d93789b59b61182d53c4 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 4f5e0f1dc6b00d1e94c0ebb8d354841c4b5215e9 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 0a8490ce93323c1dc2d5e1ceb2aa059df883975f 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 e3fb5cdf8f2b02edfa13ceffc6eb89dadf810b2e 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 21678527f70de2dedc9acbe2ab5a04863da12d28 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 ad94e8f6cdc3aecf1516870b46cfb1b31a6d8a7f 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 ec9708e4918376a3e1f1f432b98d9f17d5df7aed 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 85046c65a0e09e0cb673aac7647c482e66d4c9a9 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 598fb69cf87bfd5f4c42564f420b0f233e6cb772 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 449319af1716359e967bf810c6967b074e96c282 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 e7b82db9f7b30ebc35cd28bf8eda8b1bd90344c7 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 885a8cc94a153f8befb66b3e4dccc8edc7fac20e 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 1cd4d34ffbac18333314036ea4eaae258a92a2fe 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 73ad1ae54ffdfe93e1104a9b1bc3a939d7f49309 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 21f4c96e60ff2de0df1a8f36c4ddb952f3c1e34b 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 2b749cf1844a828c3ec1569d81b66a7404ca582b 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 b7f00ef187c3e9050a17489628289f91d5c6639a 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 fdda051716e626641624b5052ed9d6d04ad3cb18 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 1fe4ec1bc36d4543bec3b98e0ad1898763f4597f 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 04aee88ba1f52c8b4ae8eec850976d11ef8e923b 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 18458a33baf7fb4823928f7020875b998d11ba26 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 d87f69f025749af9c2e8814a3d2fab003e8cbce9 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 93e346c38649b19e0758de9d02c0bc925d006e09 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 062320dd4ccd444afaadc91d2f4c759c956fa944 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 7d2c383de3c2a13894b198e8a52c689d5f2fa7fd 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 09e70222f2ba01cca2efc19bf590e83e00ba2081 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 67380e74b842c8b630938faaede8e4c25d0c3a82 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 5ee34968767014e6795b7c2b463ee105af3f16ca Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:50:45 -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 | 550 ------------------ .../Install/ConfigureCheckServiceTest.php | 105 ---- 2 files changed, 655 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 9453a9428c..0000000000 --- a/lib/Service/Install/ConfigureCheckService.php +++ /dev/null @@ -1,550 +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']; - } - - $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()); - } - } -}