From 33b0f25aa4b2846a5bd47482a44efb5944fc9ab7 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 dfe28ccaa90b7b9c7a3c7a0aa591bf9bac625c63 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 5467f9799dab4be863423f423eb7b13258893eb5 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 0a12981d07264b6c417f27d686afe8bcd8da8e0a 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 a1106ff1c30e3f8a7c40fea322d9733b52b562fd 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 f33c7ed2c29eb4a3d0f70ab4c931d76a1429e638 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 500992cb115df528e4ace239a8fa12da835480a2 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 f94c723f4d15b595b69c95a6504aa20c435136ac 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 329ca4f5d6522692d0249c6c85fefd3e77287bbc 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 e20e9bc7fa37aa91c28e912d6988aeeae5c36013 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Tue, 3 Mar 2026 19:51:25 +0000 Subject: [PATCH 10/68] refactor: deprecate ConfigureCheckService Adds @deprecated annotation to ConfigureCheckService pointing to the new individual setup checks. Related issue: #6590 Type: Maintenance Checklist: - [x] Add @deprecated tag with version 13.0.4 - [x] Mention replacement classes (JavaSetupCheck, JSignPdfSetupCheck, etc.) Signed-off-by: Guilherme Carvalho --- lib/Service/Install/ConfigureCheckService.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Service/Install/ConfigureCheckService.php b/lib/Service/Install/ConfigureCheckService.php index 8002343591..9e482b1f1d 100644 --- a/lib/Service/Install/ConfigureCheckService.php +++ b/lib/Service/Install/ConfigureCheckService.php @@ -20,6 +20,10 @@ use OCP\IURLGenerator; use Psr\Log\LoggerInterface; +/** +* @deprecated 13.0.4 Use the individual SetupCheck classes instead +* (JavaSetupCheck, JSignPdfSetupCheck, etc.). +*/ class ConfigureCheckService { private string $architecture; private bool $isCacheDisabled = false; From 678f84233f48d44bf8f69b3835e56872b9d38f20 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 bc674fa4919512de5d6ae2815800aa2d6bd18520 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Thu, 5 Mar 2026 19:31:00 +0000 Subject: [PATCH 12/68] style(setup-check): apply code style fixes and improve Psalm annotations This commit applies automated code style fixes using `composer cs:fix` and addresses issues identified by Psalm static analysis across the SetupCheck classes and their related tests. The changes include: - Normalizing indentation (tabs vs spaces) and adding missing trailing commas in argument lists across all SetupCheck classes and `SetupCheckUtils.php`. - Fixing the formatting of the `@deprecated` comment in `ConfigureCheckService.php`. - Adding `#[\Override]` attributes to all methods implementing `ISetupCheck`. Related issue: #6590 Type: Maintenance Checklist: - [x] Run `composer cs:fix` to apply coding standards - [x] Address Psalm static analysis findings - [x] Add `#[\Override]` attributes to interface methods - [x] Fix deprecation comment formatting Signed-off-by: Guilherme Carvalho --- lib/Service/Install/ConfigureCheckService.php | 6 +- .../CertificateEngineSetupCheck.php | 133 +++++---- lib/SetupCheck/ImagickSetupCheck.php | 3 + lib/SetupCheck/JSignPdfSetupCheck.php | 225 ++++++++------- lib/SetupCheck/JavaSetupCheck.php | 269 +++++++++--------- lib/SetupCheck/PDFtkSetupCheck.php | 23 +- lib/SetupCheck/PopplerSetupCheck.php | 3 + lib/SetupCheck/SetupCheckUtils.php | 111 ++++---- 8 files changed, 388 insertions(+), 385 deletions(-) diff --git a/lib/Service/Install/ConfigureCheckService.php b/lib/Service/Install/ConfigureCheckService.php index 9e482b1f1d..30b2f069fb 100644 --- a/lib/Service/Install/ConfigureCheckService.php +++ b/lib/Service/Install/ConfigureCheckService.php @@ -21,9 +21,9 @@ use Psr\Log\LoggerInterface; /** -* @deprecated 13.0.4 Use the individual SetupCheck classes instead -* (JavaSetupCheck, JSignPdfSetupCheck, etc.). -*/ + * @deprecated 13.0.4 Use the individual SetupCheck classes instead + * (JavaSetupCheck, JSignPdfSetupCheck, etc.). + */ class ConfigureCheckService { private string $architecture; private bool $isCacheDisabled = false; diff --git a/lib/SetupCheck/CertificateEngineSetupCheck.php b/lib/SetupCheck/CertificateEngineSetupCheck.php index 78dd75e183..34ffdc4f5c 100644 --- a/lib/SetupCheck/CertificateEngineSetupCheck.php +++ b/lib/SetupCheck/CertificateEngineSetupCheck.php @@ -14,81 +14,80 @@ use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -class CertificateEngineSetupCheck implements ISetupCheck -{ - private IL10N $l10n; - private CertificateEngineFactory $certificateEngineFactory; +class CertificateEngineSetupCheck implements ISetupCheck { + private IL10N $l10n; + private CertificateEngineFactory $certificateEngineFactory; - public function __construct( - IL10N $l10n, - CertificateEngineFactory $certificateEngineFactory - ) { - $this->l10n = $l10n; - $this->certificateEngineFactory = $certificateEngineFactory; - } + public function __construct( + IL10N $l10n, + CertificateEngineFactory $certificateEngineFactory, + ) { + $this->l10n = $l10n; + $this->certificateEngineFactory = $certificateEngineFactory; + } - public function getName(): string - { - return $this->l10n->t('Certificate engine'); - } + #[\Override] + public function getName(): string { + return $this->l10n->t('Certificate engine'); + } - public function getCategory(): string - { - return 'security'; - } + #[\Override] + public function getCategory(): string { + return 'security'; + } - public function run(): SetupResult - { - try { - $engine = $this->certificateEngineFactory->getEngine(); - $checkResults = $engine->configureCheck(); - } catch (\Throwable $e) { - $engineName = $this->certificateEngineFactory->getEngine()->getName() ?? 'unknown'; - return SetupResult::error( - $this->l10n->t('Define the certificate engine to use'), - $this->l10n->t('Run occ libresign:configure:%s --help', [$engineName]) - ); - } + #[\Override] + public function run(): SetupResult { + try { + $engine = $this->certificateEngineFactory->getEngine(); + $checkResults = $engine->configureCheck(); + } catch (\Throwable $e) { + $engineName = $this->certificateEngineFactory->getEngine()->getName() ?? 'unknown'; + return SetupResult::error( + $this->l10n->t('Define the certificate engine to use'), + $this->l10n->t('Run occ libresign:configure:%s --help', [$engineName]) + ); + } - // Process results: if any error, return error; else if any warning, return warning; else success - $hasError = false; - $hasWarning = false; - $messages = []; - $tips = []; + // Process results: if any error, return error; else if any warning, return warning; else success + $hasError = false; + $hasWarning = false; + $messages = []; + $tips = []; - foreach ($checkResults as $result) { - if ($result instanceof ConfigureCheckHelper) { - $status = $result->getStatus(); - $msg = $result->getMessage(); - $tip = $result->getTip(); + foreach ($checkResults as $result) { + if ($result instanceof ConfigureCheckHelper) { + $status = $result->getStatus(); + $msg = $result->getMessage(); + $tip = $result->getTip(); - if ($status === 'error') { - $hasError = true; - $messages[] = "[ERROR] $msg"; - } elseif ($status === 'warning') { - $hasWarning = true; - $messages[] = "[WARNING] $msg"; - } else { - $messages[] = $msg; - } + if ($status === 'error') { + $hasError = true; + $messages[] = "[ERROR] $msg"; + } elseif ($status === 'warning') { + $hasWarning = true; + $messages[] = "[WARNING] $msg"; + } else { + $messages[] = $msg; + } - if (!empty($tip)) { - $tips[] = $tip; - } - } - } + if (!empty($tip)) { + $tips[] = $tip; + } + } + } - $tip = ''; - if (!empty($tips)) { - $tip = implode("\n", array_unique($tips)); - } + $tip = ''; + if (!empty($tips)) { + $tip = implode("\n", array_unique($tips)); + } - if ($hasError) { - return SetupResult::error(implode("\n", $messages), $tip); - } elseif ($hasWarning) { - return SetupResult::warning(implode("\n", $messages), $tip); - } else { - return SetupResult::success(implode("\n", $messages) ?: $this->l10n->t('Certificate engine is configured correctly')); - } - } + if ($hasError) { + return SetupResult::error(implode("\n", $messages), $tip); + } elseif ($hasWarning) { + return SetupResult::warning(implode("\n", $messages), $tip); + } else { + return SetupResult::success(implode("\n", $messages) ?: $this->l10n->t('Certificate engine is configured correctly')); + } + } } diff --git a/lib/SetupCheck/ImagickSetupCheck.php b/lib/SetupCheck/ImagickSetupCheck.php index 48ac35a527..d24446bf15 100644 --- a/lib/SetupCheck/ImagickSetupCheck.php +++ b/lib/SetupCheck/ImagickSetupCheck.php @@ -19,14 +19,17 @@ public function __construct(IL10N $l10n) { $this->l10n = $l10n; } + #[\Override] public function getName(): string { return $this->l10n->t('Imagick PHP extension'); } + #[\Override] public function getCategory(): string { return 'system'; } + #[\Override] public function run(): SetupResult { if (!extension_loaded('imagick')) { return SetupResult::info( diff --git a/lib/SetupCheck/JSignPdfSetupCheck.php b/lib/SetupCheck/JSignPdfSetupCheck.php index 2c94d3c38d..1a727a1e01 100644 --- a/lib/SetupCheck/JSignPdfSetupCheck.php +++ b/lib/SetupCheck/JSignPdfSetupCheck.php @@ -8,126 +8,125 @@ namespace OCA\Libresign\SetupCheck; -use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\AppInfo\Application; use OCA\Libresign\Handler\SignEngine\JSignPdfHandler; -use OCA\Libresign\Service\Install\SignSetupService; +use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; -use OCP\IL10N; +use OCA\Libresign\Service\Install\SignSetupService; +use OCP\App\IAppManager; use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IL10N; use OCP\IURLGenerator; -use OCP\App\IAppManager; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -use OCP\IConfig; use Psr\Log\LoggerInterface; -class JSignPdfSetupCheck implements ISetupCheck -{ - use SetupCheckUtils; - - private IL10N $l10n; - private IAppConfig $appConfig; - private JSignPdfHandler $jSignPdfHandler; - private IConfig $systemConfig; - private JavaHelper $javaHelper; - - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; - - public function __construct( - IL10N $l10n, - IAppConfig $appConfig, - JSignPdfHandler $jSignPdfHandler, - SignSetupService $signSetupService, - IURLGenerator $urlGenerator, - IAppManager $appManager, - LoggerInterface $logger, - IConfig $systemConfig, - JavaHelper $javaHelper - ) { - $this->l10n = $l10n; - $this->appConfig = $appConfig; - $this->jSignPdfHandler = $jSignPdfHandler; - $this->signSetupService = $signSetupService; - $this->urlGenerator = $urlGenerator; - $this->appManager = $appManager; - $this->logger = $logger; - $this->systemConfig = $systemConfig; - $this->javaHelper = $javaHelper; - } - - public function getName(): string - { - return $this->l10n->t('JSignPdf'); - } - - public function getCategory(): string - { - return 'system'; - } - - public function run(): SetupResult - { - $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); - $jsignpdfJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path'); - - if (!$jsignpdfJarPath) { - return SetupResult::error( - $this->l10n->t('JSignPdf not found'), - $this->l10n->t('Run occ libresign:install --jsignpdf') - ); - } - - $verifyResult = $this->verifyResourceIntegrity('jsignpdf', $debugEnabled); - if (!empty($verifyResult)) { - [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'jsignpdf', $debugEnabled, $this->l10n); - return SetupResult::error($errorMsg, $tip); - } - - if (!file_exists($jsignpdfJarPath)) { - return SetupResult::error( - $this->l10n->t('JSignPdf binary not found: %s', [$jsignpdfJarPath]), - $this->l10n->t('Run occ libresign:install --jsignpdf') - ); - } - - $javaPath = $this->javaHelper->getJavaPath(); - if (!$javaPath || !file_exists($javaPath)) { - return SetupResult::error( - $this->l10n->t('Necessary Java to run JSignPdf'), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - $jsignPdf = $this->jSignPdfHandler->getJSignPdf(); - $jsignPdf->setParam($this->jSignPdfHandler->getJSignParam()); - $currentVersion = $jsignPdf->getVersion(); - - if (!$currentVersion) { - $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); - return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); - } - - if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { - $msg = $this->l10n->t('Necessary bump JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); - return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); - } - - if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '>')) { - return SetupResult::error( - $this->l10n->t('Necessary downgrade JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]), - $this->l10n->t('Run occ libresign:install --jsignpdf') - ); - } - - $messages = [ - $this->l10n->t('JSignPdf version: %s', [$currentVersion]), - $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]) - ]; - - return SetupResult::success(implode("\n", $messages)); - } +class JSignPdfSetupCheck implements ISetupCheck { + use SetupCheckUtils; + + private IL10N $l10n; + private IAppConfig $appConfig; + private JSignPdfHandler $jSignPdfHandler; + private IConfig $systemConfig; + private JavaHelper $javaHelper; + + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; + + public function __construct( + IL10N $l10n, + IAppConfig $appConfig, + JSignPdfHandler $jSignPdfHandler, + SignSetupService $signSetupService, + IURLGenerator $urlGenerator, + IAppManager $appManager, + LoggerInterface $logger, + IConfig $systemConfig, + JavaHelper $javaHelper, + ) { + $this->l10n = $l10n; + $this->appConfig = $appConfig; + $this->jSignPdfHandler = $jSignPdfHandler; + $this->signSetupService = $signSetupService; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; + $this->logger = $logger; + $this->systemConfig = $systemConfig; + $this->javaHelper = $javaHelper; + } + + #[\Override] + public function getName(): string { + return $this->l10n->t('JSignPdf'); + } + + #[\Override] + public function getCategory(): string { + return 'system'; + } + + #[\Override] + public function run(): SetupResult { + $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); + $jsignpdfJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path'); + + if (!$jsignpdfJarPath) { + return SetupResult::error( + $this->l10n->t('JSignPdf not found'), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $verifyResult = $this->verifyResourceIntegrity('jsignpdf', $debugEnabled); + if (!empty($verifyResult)) { + [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'jsignpdf', $debugEnabled, $this->l10n); + return SetupResult::error($errorMsg, $tip); + } + + if (!file_exists($jsignpdfJarPath)) { + return SetupResult::error( + $this->l10n->t('JSignPdf binary not found: %s', [$jsignpdfJarPath]), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $javaPath = $this->javaHelper->getJavaPath(); + if (!$javaPath || !file_exists($javaPath)) { + return SetupResult::error( + $this->l10n->t('Necessary Java to run JSignPdf'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $jsignPdf = $this->jSignPdfHandler->getJSignPdf(); + $jsignPdf->setParam($this->jSignPdfHandler->getJSignParam()); + $currentVersion = $jsignPdf->getVersion(); + + if (!$currentVersion) { + $msg = $this->l10n->t('Necessary install the version %s', [InstallService::JSIGNPDF_VERSION]); + return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + } + + if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '<')) { + $msg = $this->l10n->t('Necessary bump JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]); + return SetupResult::error($msg, $this->l10n->t('Run occ libresign:install --jsignpdf')); + } + + if (version_compare($currentVersion, InstallService::JSIGNPDF_VERSION, '>')) { + return SetupResult::error( + $this->l10n->t('Necessary downgrade JSignPdf version from %s to %s', [$currentVersion, InstallService::JSIGNPDF_VERSION]), + $this->l10n->t('Run occ libresign:install --jsignpdf') + ); + } + + $messages = [ + $this->l10n->t('JSignPdf version: %s', [$currentVersion]), + $this->l10n->t('JSignPdf path: %s', [$jsignpdfJarPath]) + ]; + + return SetupResult::success(implode("\n", $messages)); + } } diff --git a/lib/SetupCheck/JavaSetupCheck.php b/lib/SetupCheck/JavaSetupCheck.php index 8577f78957..8a03674c04 100644 --- a/lib/SetupCheck/JavaSetupCheck.php +++ b/lib/SetupCheck/JavaSetupCheck.php @@ -9,146 +9,145 @@ namespace OCA\Libresign\SetupCheck; use OCA\Libresign\Helper\JavaHelper; -use OCA\Libresign\Service\Install\SignSetupService; use OCA\Libresign\Service\Install\InstallService; +use OCA\Libresign\Service\Install\SignSetupService; +use OCP\App\IAppManager; +use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; -use OCP\App\IAppManager; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -use OCP\IConfig; use Psr\Log\LoggerInterface; -class JavaSetupCheck implements ISetupCheck -{ - use SetupCheckUtils; - - private IL10N $l10n; - private JavaHelper $javaHelper; - private IConfig $systemConfig; - - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; - - public function __construct( - IL10N $l10n, - JavaHelper $javaHelper, - SignSetupService $signSetupService, - IURLGenerator $urlGenerator, - IAppManager $appManager, - LoggerInterface $logger, - IConfig $systemConfig - ) { - $this->l10n = $l10n; - $this->javaHelper = $javaHelper; - $this->signSetupService = $signSetupService; - $this->urlGenerator = $urlGenerator; - $this->appManager = $appManager; - $this->logger = $logger; - $this->systemConfig = $systemConfig; - } - - public function getName(): string - { - return $this->l10n->t('Java'); - } - - public function getCategory(): string - { - return 'system'; - } - - public function run(): SetupResult - { - $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); - $javaPath = $this->javaHelper->getJavaPath(); - - if (!$javaPath) { - return SetupResult::error( - $this->l10n->t('Java not installed'), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - $verifyResult = $this->verifyResourceIntegrity('java', $debugEnabled); - if (!empty($verifyResult)) { - [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'java', $debugEnabled, $this->l10n); - return SetupResult::error($errorMsg, $tip); - } - - if (!file_exists($javaPath)) { - return SetupResult::error( - $this->l10n->t('Java binary not found: %s', [$javaPath]), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - exec($javaPath . ' -version 2>&1', $output, $returnCode); - if (empty($output)) { - return SetupResult::error( - $this->l10n->t('Failed to execute Java. Sounds that your operational system is blocking the JVM.'), - 'https://github.com/LibreSign/libresign/issues/2327#issuecomment-1961988790' - ); - } - if ($returnCode !== 0) { - return SetupResult::error( - $this->l10n->t('Failure to check Java version.'), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - $javaVersion = trim($output[0] ?? ''); - if ($javaVersion !== InstallService::JAVA_VERSION) { - return SetupResult::error( - $this->l10n->t('Invalid java version. Found: %s expected: %s', [$javaVersion, InstallService::JAVA_VERSION]), - $this->l10n->t('Run occ libresign:install --java') - ); - } - - exec($javaPath . ' -XshowSettings:properties -version 2>&1', $encodingOutput); - $fullOutput = implode("\n", $encodingOutput); - preg_match('/native.encoding = (?.*?)(\n|$)/', $fullOutput, $matches); - $encoding = $matches['encoding'] ?? null; - - if (!$encoding) { - return SetupResult::error( - $this->l10n->t('Java encoding not found.'), - sprintf('The command %s need to have native.encoding', $javaPath . ' -XshowSettings:properties -version') - ); - } - - if (!str_contains($encoding, 'UTF-8')) { - $detectedEncoding = trim($encoding); - $phpLocale = setlocale(LC_CTYPE, 0) ?: 'not set'; - $phpLcAll = getenv('LC_ALL') ?: 'not set'; - $phpLang = getenv('LANG') ?: 'not set'; - - $tip = sprintf( - "Java detected encoding \"%s\" but UTF-8 is required.\n\n" - . "**Current PHP environment:**\n" - . "- LC_CTYPE: %s\n" - . "- LC_ALL: %s\n" - . "- LANG: %s\n\n" - . "**To fix this issue:**\n" - . "1. Set LC_ALL and LANG environment variables (e.g., LC_ALL=en_US.UTF-8) for your web server user\n" - . "2. Restart your web server after making changes\n" - . "3. Verify with command: `locale charmap` (should return UTF-8)\n\n" - . 'For more details, see: [Issue #4872](https://github.com/LibreSign/libresign/issues/4872)', - $detectedEncoding, - $phpLocale, - $phpLcAll, - $phpLang - ); - return SetupResult::info( - $this->l10n->t('Non-UTF-8 encoding detected: %s. This may cause issues with accented or special characters', [$detectedEncoding]), - $tip - ); - } - - $message = $this->l10n->t('Java version: %s', [$javaVersion]) . "\n" . - $this->l10n->t('Java binary: %s', [$javaPath]); - return SetupResult::success($message); - } +class JavaSetupCheck implements ISetupCheck { + use SetupCheckUtils; + + private IL10N $l10n; + private JavaHelper $javaHelper; + private IConfig $systemConfig; + + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; + + public function __construct( + IL10N $l10n, + JavaHelper $javaHelper, + SignSetupService $signSetupService, + IURLGenerator $urlGenerator, + IAppManager $appManager, + LoggerInterface $logger, + IConfig $systemConfig, + ) { + $this->l10n = $l10n; + $this->javaHelper = $javaHelper; + $this->signSetupService = $signSetupService; + $this->urlGenerator = $urlGenerator; + $this->appManager = $appManager; + $this->logger = $logger; + $this->systemConfig = $systemConfig; + } + + #[\Override] + public function getName(): string { + return $this->l10n->t('Java'); + } + + #[\Override] + public function getCategory(): string { + return 'system'; + } + + #[\Override] + public function run(): SetupResult { + $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); + $javaPath = $this->javaHelper->getJavaPath(); + + if (!$javaPath) { + return SetupResult::error( + $this->l10n->t('Java not installed'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $verifyResult = $this->verifyResourceIntegrity('java', $debugEnabled); + if (!empty($verifyResult)) { + [$errorMsg, $tip] = $this->getErrorAndTipFromVerify($verifyResult, 'java', $debugEnabled, $this->l10n); + return SetupResult::error($errorMsg, $tip); + } + + if (!file_exists($javaPath)) { + return SetupResult::error( + $this->l10n->t('Java binary not found: %s', [$javaPath]), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + exec($javaPath . ' -version 2>&1', $output, $returnCode); + if (empty($output)) { + return SetupResult::error( + $this->l10n->t('Failed to execute Java. Sounds that your operational system is blocking the JVM.'), + 'https://github.com/LibreSign/libresign/issues/2327#issuecomment-1961988790' + ); + } + if ($returnCode !== 0) { + return SetupResult::error( + $this->l10n->t('Failure to check Java version.'), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + $javaVersion = trim($output[0] ?? ''); + if ($javaVersion !== InstallService::JAVA_VERSION) { + return SetupResult::error( + $this->l10n->t('Invalid java version. Found: %s expected: %s', [$javaVersion, InstallService::JAVA_VERSION]), + $this->l10n->t('Run occ libresign:install --java') + ); + } + + exec($javaPath . ' -XshowSettings:properties -version 2>&1', $encodingOutput); + $fullOutput = implode("\n", $encodingOutput); + preg_match('/native.encoding = (?.*?)(\n|$)/', $fullOutput, $matches); + $encoding = $matches['encoding'] ?? null; + + if (!$encoding) { + return SetupResult::error( + $this->l10n->t('Java encoding not found.'), + sprintf('The command %s need to have native.encoding', $javaPath . ' -XshowSettings:properties -version') + ); + } + + if (!str_contains($encoding, 'UTF-8')) { + $detectedEncoding = trim($encoding); + $phpLocale = setlocale(LC_CTYPE, 0) ?: 'not set'; + $phpLcAll = getenv('LC_ALL') ?: 'not set'; + $phpLang = getenv('LANG') ?: 'not set'; + + $tip = sprintf( + "Java detected encoding \"%s\" but UTF-8 is required.\n\n" + . "**Current PHP environment:**\n" + . "- LC_CTYPE: %s\n" + . "- LC_ALL: %s\n" + . "- LANG: %s\n\n" + . "**To fix this issue:**\n" + . "1. Set LC_ALL and LANG environment variables (e.g., LC_ALL=en_US.UTF-8) for your web server user\n" + . "2. Restart your web server after making changes\n" + . "3. Verify with command: `locale charmap` (should return UTF-8)\n\n" + . 'For more details, see: [Issue #4872](https://github.com/LibreSign/libresign/issues/4872)', + $detectedEncoding, + $phpLocale, + $phpLcAll, + $phpLang + ); + return SetupResult::info( + $this->l10n->t('Non-UTF-8 encoding detected: %s. This may cause issues with accented or special characters', [$detectedEncoding]), + $tip + ); + } + + $message = $this->l10n->t('Java version: %s', [$javaVersion]) . "\n" + . $this->l10n->t('Java binary: %s', [$javaPath]); + return SetupResult::success($message); + } } diff --git a/lib/SetupCheck/PDFtkSetupCheck.php b/lib/SetupCheck/PDFtkSetupCheck.php index efcbdb4c53..d03db7d4a9 100644 --- a/lib/SetupCheck/PDFtkSetupCheck.php +++ b/lib/SetupCheck/PDFtkSetupCheck.php @@ -8,17 +8,17 @@ namespace OCA\Libresign\SetupCheck; -use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\AppInfo\Application; -use OCA\Libresign\Service\Install\SignSetupService; +use OCA\Libresign\Helper\JavaHelper; use OCA\Libresign\Service\Install\InstallService; -use OCP\IL10N; +use OCA\Libresign\Service\Install\SignSetupService; +use OCP\App\IAppManager; use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IL10N; use OCP\IURLGenerator; -use OCP\App\IAppManager; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; -use OCP\IConfig; use Psr\Log\LoggerInterface; class PDFtkSetupCheck implements ISetupCheck { @@ -29,10 +29,10 @@ class PDFtkSetupCheck implements ISetupCheck { private IConfig $systemConfig; private JavaHelper $javaHelper; - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; public function __construct( IL10N $l10n, @@ -42,7 +42,7 @@ public function __construct( IAppManager $appManager, LoggerInterface $logger, IConfig $systemConfig, - JavaHelper $javaHelper + JavaHelper $javaHelper, ) { $this->l10n = $l10n; $this->appConfig = $appConfig; @@ -54,14 +54,17 @@ public function __construct( $this->javaHelper = $javaHelper; } + #[\Override] public function getName(): string { return $this->l10n->t('PDFtk'); } + #[\Override] public function getCategory(): string { return 'system'; } + #[\Override] public function run(): SetupResult { $debugEnabled = $this->systemConfig->getSystemValueBool('debug', false); $pdftkPath = $this->appConfig->getValueString(Application::APP_ID, 'pdftk_path'); diff --git a/lib/SetupCheck/PopplerSetupCheck.php b/lib/SetupCheck/PopplerSetupCheck.php index b0f92d66a3..10cd6d2962 100644 --- a/lib/SetupCheck/PopplerSetupCheck.php +++ b/lib/SetupCheck/PopplerSetupCheck.php @@ -19,14 +19,17 @@ public function __construct(IL10N $l10n) { $this->l10n = $l10n; } + #[\Override] public function getName(): string { return $this->l10n->t('Poppler utils'); } + #[\Override] public function getCategory(): string { return 'system'; } + #[\Override] public function run(): SetupResult { $pdfsigOk = $this->checkPdfSig(); $pdfinfoOk = $this->checkPdfinfo(); diff --git a/lib/SetupCheck/SetupCheckUtils.php b/lib/SetupCheck/SetupCheckUtils.php index cf63bcccfa..b2f8641e5f 100644 --- a/lib/SetupCheck/SetupCheckUtils.php +++ b/lib/SetupCheck/SetupCheckUtils.php @@ -9,66 +9,63 @@ namespace OCA\Libresign\SetupCheck; use OCA\Libresign\Service\Install\SignSetupService; -use OCP\IURLGenerator; use OCP\App\IAppManager; -use Psr\Log\LoggerInterface; use OCP\IL10N; +use OCP\IURLGenerator; +use Psr\Log\LoggerInterface; -trait SetupCheckUtils -{ - private SignSetupService $signSetupService; - private IURLGenerator $urlGenerator; - private IAppManager $appManager; - private LoggerInterface $logger; +trait SetupCheckUtils { + private SignSetupService $signSetupService; + private IURLGenerator $urlGenerator; + private IAppManager $appManager; + private LoggerInterface $logger; - private function verifyResourceIntegrity(string $resource, bool $debugEnabled): array - { - $this->signSetupService->willUseLocalCert($debugEnabled); - $result = $this->signSetupService->verify(php_uname('m'), $resource); - if (count($result) === 1 && $debugEnabled) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND']) || isset($result['EMPTY_SIGNATURE_DATA'])) { - return []; - } - } - return $result; - } + private function verifyResourceIntegrity(string $resource, bool $debugEnabled): array { + $this->signSetupService->willUseLocalCert($debugEnabled); + $result = $this->signSetupService->verify(php_uname('m'), $resource); + if (count($result) === 1 && $debugEnabled) { + if (isset($result['SIGNATURE_DATA_NOT_FOUND']) || isset($result['EMPTY_SIGNATURE_DATA'])) { + return []; + } + } + return $result; + } - private function getErrorAndTipFromVerify(array $result, string $resource, bool $debugEnabled, IL10N $l10n): array - { - if (count($result) === 1 && !$debugEnabled) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { - return [ - $l10n->t('Signature data not found.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), - ]; - } - if (isset($result['EMPTY_SIGNATURE_DATA'])) { - return [ - $l10n->t('Your signature data is empty.'), - $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), - ]; - } - } - if (isset($result['HASH_FILE_ERROR'])) { - if ($debugEnabled) { - return [ - $l10n->t('Invalid hash of binaries files.'), - $l10n->t('Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --%s --use-local-cert', [$resource]), - ]; - } - } - $this->logger->error('Invalid hash of binaries files', ['result' => $result]); - if ($this->appManager->isEnabledForUser('logreader')) { - return [ - $l10n->t('Invalid hash of binaries files.'), - $l10n->t('Check your nextcloud.log file on %s and run occ libresign:install --all', [ - $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) - ]), - ]; - } - return [ - $l10n->t('Invalid hash of binaries files.'), - $l10n->t('Check your nextcloud.log file and run occ libresign:install --all'), - ]; - } + private function getErrorAndTipFromVerify(array $result, string $resource, bool $debugEnabled, IL10N $l10n): array { + if (count($result) === 1 && !$debugEnabled) { + if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { + return [ + $l10n->t('Signature data not found.'), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + ]; + } + if (isset($result['EMPTY_SIGNATURE_DATA'])) { + return [ + $l10n->t('Your signature data is empty.'), + $l10n->t("Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean"), + ]; + } + } + if (isset($result['HASH_FILE_ERROR'])) { + if ($debugEnabled) { + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --%s --use-local-cert', [$resource]), + ]; + } + } + $this->logger->error('Invalid hash of binaries files', ['result' => $result]); + if ($this->appManager->isEnabledForUser('logreader')) { + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Check your nextcloud.log file on %s and run occ libresign:install --all', [ + $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) + ]), + ]; + } + return [ + $l10n->t('Invalid hash of binaries files.'), + $l10n->t('Check your nextcloud.log file and run occ libresign:install --all'), + ]; + } } From da24f3f4aa4644b48b274f17c117679d6582eaff 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 2535d6d591e7fe854adce055d6fb91f27c1b7663 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 1f1e39662338b9f17ee49b9f9422a97df26cbe28 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 b5cced6fac29f06ae7d5a96c4e160963d0872eef 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 db1124d5dd03b670fbb47d7f4fdaf150747b2682 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 c316335f26..dc7fc63e70 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; @@ -71,7 +71,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, @@ -86,6 +85,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(); @@ -258,11 +258,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()); } /** @@ -301,8 +297,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(); @@ -313,7 +308,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; } } @@ -327,7 +322,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 @@ -564,7 +559,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 a3d3f045e3..bc4ddaaedb 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -266,7 +266,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 c5db494aa3d5b5294a6998efc462e8d17f68fe36 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 2a542e0f36..90bdb7acd4 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 61d984c1df4d063855f49ee864230ce794952a77 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 16:20:42 +0000 Subject: [PATCH 19/68] refactor(service): remove deprecated ConfigureCheckService The ConfigureCheckService has been fully replaced by the new ISetupCheck implementations (JavaSetupCheck, JSignPdfSetupCheck, etc.) and the SetupCheckResultService aggregator. This cleanup removes the old service and its test. Refs: #6590 Signed-off-by: Guilherme Carvalho --- lib/Service/Install/ConfigureCheckService.php | 555 ------------------ .../Install/ConfigureCheckServiceTest.php | 107 ---- 2 files changed, 662 deletions(-) delete mode 100644 lib/Service/Install/ConfigureCheckService.php delete mode 100644 tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php diff --git a/lib/Service/Install/ConfigureCheckService.php b/lib/Service/Install/ConfigureCheckService.php deleted file mode 100644 index 30b2f069fb..0000000000 --- a/lib/Service/Install/ConfigureCheckService.php +++ /dev/null @@ -1,555 +0,0 @@ -architecture = php_uname('m'); - } - - public function disableCache(): void { - $this->isCacheDisabled = true; - } - - /** - * Get result of all checks - * - * @return ConfigureCheckHelper[] - */ - public function checkAll(): array { - if ($this->isCacheDisabled) { - $this->ocAppConfig->clearCache(); - } - $result = []; - $result = array_merge($result, $this->checkSign()); - $result = array_merge($result, $this->checkCertificate()); - return $result; - } - - /** - * Check all requirements to sign - * - * @return ConfigureCheckHelper[] - */ - public function checkSign(): array { - $return = []; - $return = array_merge($return, $this->checkJava()); - $return = array_merge($return, $this->checkPdftk()); - $return = array_merge($return, $this->checkJSignPdf()); - $return = array_merge($return, $this->checkPoppler()); - $return = array_merge($return, $this->checkImagick()); - return $return; - } - - public function checkPoppler(): array { - $return = $this->checkPdfSig(); - $return = array_merge($return, $this->checkPdfinfo()); - return $return; - } - - public function checkPdfSig(): array { - if (!empty($this->result['poppler'])) { - return $this->result['poppler']; - } - // The output of this command go to STDERR and exec get the STDOUT - // With 2>&1 the STRERR is redirected to STDOUT - exec('pdfsig -v 2>&1', $version, $retval); - if ($retval !== 0) { - return $this->result['poppler'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage('Poppler utils not installed') - ->setResource('pdfsig') - ->setTip('Install the package poppler-utils at your operational system to be possible get more details about validation of signatures.'), - ]; - } - if (!$version) { - return $this->result['poppler'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfsig version') - ->setResource('pdfsig') - ->setTip("The command executed by PHP haven't any output."), - ]; - } - $returnValue = preg_match('/pdfsig version (?.*)/', implode(PHP_EOL, $version), $matches); - if ($returnValue !== 1) { - return $this->result['poppler'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfsig version') - ->setResource('pdfsig') - ->setTip("This is a poppler-utils dependency and wasn't possible to parse the output of command pdfsig -v"), - ]; - } - return $this->result['poppler'] = [(new ConfigureCheckHelper()) - ->setSuccessMessage('pdfsig version: ' . $matches['version']) - ->setResource('pdfsig') - ]; - } - - public function checkPdfinfo(): array { - if (!empty($this->result['pdfinfo'])) { - return $this->result['pdfinfo']; - } - // The output of this command go to STDERR and exec get the STDOUT - // With 2>&1 the STRERR is redirected to STDOUT - exec('pdfinfo -v 2>&1', $version, $retval); - if ($retval !== 0) { - return $this->result['pdfinfo'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage('Poppler utils not installed') - ->setResource('pdfinfo') - ->setTip('Install the package poppler-utils at your operational system have a fallback to fetch page dimensions.'), - ]; - } - if (!$version) { - return $this->result['pdfinfo'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfinfo version') - ->setResource('pdfinfo') - ->setTip("The command executed by PHP haven't any output."), - ]; - } - $returnValue = preg_match('/pdfinfo version (?.*)/', implode(PHP_EOL, $version), $matches); - if (!$returnValue) { - return $this->result['pdfinfo'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Fail to retrieve pdfinfo version') - ->setResource('pdfinfo') - ->setTip("This is a poppler-utils dependency and wasn't possible to parse the output of command pdfinfo -v"), - ]; - } - return $this->result['pdfinfo'] = [(new ConfigureCheckHelper()) - ->setSuccessMessage('pdfinfo version: ' . $matches['version']) - ->setResource('pdfinfo') - ]; - } - - /** - * Check all requirements to use JSignPdf - * - * @return ConfigureCheckHelper[] - */ - public function checkJSignPdf(): array { - if (!empty($this->result['jsignpdf'])) { - return $this->result['jsignpdf']; - } - - $signatureEngine = $this->appConfig->getValueString(Application::APP_ID, 'signature_engine', 'JSignPdf'); - if ($signatureEngine !== 'JSignPdf') { - return []; - } - - $jsignpdJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path'); - if ($jsignpdJarPath) { - $resultOfVerify = $this->verify('jsignpdf'); - if (count($resultOfVerify)) { - [$errorMessage, $tip] = $this->getErrorAndTipToResultOfVerify($resultOfVerify, 'jsignpdf'); - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($errorMessage) - ->setResource('jsignpdf') - ->setTip($tip), - ]; - } - if (file_exists($jsignpdJarPath)) { - if (!$this->isJavaOk()) { - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Necessary Java to run JSignPdf') - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --java'), - ]; - } - $jsignPdf = $this->jSignPdfHandler->getJSignPdf(); - $jsignPdf->setParam($this->jSignPdfHandler->getJSignParam()); - $currentVersion = $jsignPdf->getVersion(); - $return = []; - if ($currentVersion < InstallService::JSIGNPDF_VERSION) { - if (!$currentVersion) { - $message = 'Necessary install the version ' . InstallService::JSIGNPDF_VERSION; - } else { - $message = 'Necessary bump JSignPdf versin from ' . $currentVersion . ' to ' . InstallService::JSIGNPDF_VERSION; - } - $return[] = (new ConfigureCheckHelper()) - ->setErrorMessage($message) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'); - } - if ($currentVersion > InstallService::JSIGNPDF_VERSION) { - $return[] = (new ConfigureCheckHelper()) - ->setErrorMessage('Necessary downgrade JSignPdf versin from ' . $currentVersion . ' to ' . InstallService::JSIGNPDF_VERSION) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'); - } - $return[] = (new ConfigureCheckHelper()) - ->setSuccessMessage('JSignPdf version: ' . $currentVersion) - ->setResource('jsignpdf'); - $return[] = (new ConfigureCheckHelper()) - ->setSuccessMessage('JSignPdf path: ' . $jsignpdJarPath) - ->setResource('jsignpdf'); - return $return; - } - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('JSignPdf binary not found: ' . $jsignpdJarPath) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'), - ]; - } - return $this->result['jsignpdf'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('JSignPdf not found') - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf'), - ]; - } - - /** - * Check all requirements to use PDFtk - * - * @return ConfigureCheckHelper[] - */ - public function checkPdftk(): array { - if (!empty($this->result['pdftk'])) { - return $this->result['pdftk']; - } - $pdftkPath = $this->appConfig->getValueString(Application::APP_ID, 'pdftk_path'); - if ($pdftkPath) { - $resultOfVerify = $this->verify('pdftk'); - if (count($resultOfVerify)) { - [$errorMessage, $tip] = $this->getErrorAndTipToResultOfVerify($resultOfVerify, 'pdftk'); - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($errorMessage) - ->setResource('pdftk') - ->setTip($tip), - ]; - } - if (file_exists($pdftkPath)) { - if (!$this->isJavaOk()) { - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Necessary Java to run PDFtk') - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --java'), - ]; - } - $javaPath = $this->javaHelper->getJavaPath(); - $version = []; - \exec($javaPath . ' -jar ' . $pdftkPath . ' --version 2>&1', $version, $resultCode); - if ($resultCode !== 0) { - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Failure to check PDFtk version.') - ->setResource('java') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - if (isset($version[0])) { - preg_match('/pdftk port to java (?.*) a Handy Tool/', $version[0], $matches); - if (isset($matches['version'])) { - if ($matches['version'] === InstallService::PDFTK_VERSION) { - return $this->result['pdftk'] = [ - $this->result['pdftk'][] = (new ConfigureCheckHelper()) - ->setSuccessMessage('PDFtk version: ' . InstallService::PDFTK_VERSION) - ->setResource('pdftk'), - $this->result['pdftk'][] = (new ConfigureCheckHelper()) - ->setSuccessMessage('PDFtk path: ' . $pdftkPath) - ->setResource('pdftk'), - ]; - } - $message = 'Necessary install the version ' . InstallService::PDFTK_VERSION; - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($message) - ->setResource('jsignpdf') - ->setTip('Run occ libresign:install --jsignpdf') - ]; - } - } - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('PDFtk binary is invalid: ' . $pdftkPath) - ->setResource('pdftk') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('PDFtk binary not found: ' . $pdftkPath) - ->setResource('pdftk') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - return $this->result['pdftk'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('PDFtk not found') - ->setResource('pdftk') - ->setTip('Run occ libresign:install --pdftk'), - ]; - } - - public function isDebugEnabled(): bool { - return $this->systemConfig->getValue('debug', false) === true; - } - - private function verify(string $resource): array { - $this->signSetupService->willUseLocalCert($this->isDebugEnabled()); - $result = $this->signSetupService->verify($this->architecture, $resource); - if (count($result) === 1 && $this->isDebugEnabled()) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { - return []; - } - if (isset($result['EMPTY_SIGNATURE_DATA'])) { - return []; - } - } - return $result; - } - - private function getErrorAndTipToResultOfVerify(array $result, string $resource): array { - if (count($result) === 1 && !$this->isDebugEnabled()) { - if (isset($result['SIGNATURE_DATA_NOT_FOUND'])) { - return [ - 'Signature data not found.', - "Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean", - ]; - } - if (isset($result['EMPTY_SIGNATURE_DATA'])) { - return [ - 'Your signature data is empty.', - "Sounds that you are running from source code of LibreSign.\nEnable debug mode by: occ config:system:set debug --value true --type boolean", - ]; - } - } - if (isset($result['HASH_FILE_ERROR'])) { - if ($this->isDebugEnabled()) { - return [ - 'Invalid hash of binaries files.', - 'Debug mode is enabled at your config.php and your LibreSign app was signed using a production signature. If you are not working at development of LibreSign, disable your debug mode or run the command: occ libresign install --' . $resource . ' --use-local-cert', - ]; - } - } - $this->logger->error('Invalid hash of binaries files', ['result' => $result]); - if ($this->appManager->isEnabledForUser('logreader')) { - return [ - 'Invalid hash of binaries files.', - 'Check your nextcloud.log file on ' - . $this->urlGenerator->linkToRouteAbsolute('settings.adminsettings.form', ['section' => 'logging']) - . ' and run occ libresign:install --all', - ]; - } - return [ - 'Invalid hash of binaries files.', - 'Check your nextcloud.log file and run occ libresign:install --all', - ]; - } - - /** - * Check all requirements to use Java - * - * @return ConfigureCheckHelper[] - */ - private function checkJava(): array { - if (!empty($this->result['java'])) { - return $this->result['java']; - } - - $javaPath = $this->javaHelper->getJavaPath(); - if ($javaPath) { - $resultOfVerify = $this->verify('java'); - if (count($resultOfVerify)) { - [$errorMessage, $tip] = $this->getErrorAndTipToResultOfVerify($resultOfVerify, 'java'); - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage($errorMessage) - ->setResource('java') - ->setTip($tip), - ]; - } - if (file_exists($javaPath)) { - \exec($javaPath . ' -version 2>&1', $javaVersion, $resultCode); - if (empty($javaVersion)) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage( - 'Failed to execute Java. Sounds that your operational system is blocking the JVM.' - ) - ->setResource('java') - ->setTip('https://github.com/LibreSign/libresign/issues/2327#issuecomment-1961988790'), - ]; - } - if ($resultCode !== 0) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Failure to check Java version.') - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - $javaVersion = current($javaVersion); - if ($javaVersion !== InstallService::JAVA_VERSION) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage( - sprintf( - 'Invalid java version. Found: %s expected: %s', - $javaVersion, - InstallService::JAVA_VERSION - ) - ) - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - \exec($javaPath . ' -XshowSettings:properties -version 2>&1', $output, $resultCode); - preg_match('/native.encoding = (?.*)\n/', implode("\n", $output), $matches); - if (!isset($matches['encoding'])) { - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Java encoding not found.') - ->setResource('java') - ->setTip(sprintf('The command %s need to have native.encoding', $javaPath . ' -XshowSettings:properties -version')), - ]; - } - if (!str_contains($matches['encoding'], 'UTF-8')) { - $detectedEncoding = trim($matches['encoding']); - $phpLocale = setlocale(LC_CTYPE, 0); - $phpLcAll = getenv('LC_ALL'); - $phpLang = getenv('LANG'); - - $tip = sprintf( - "Java detected encoding \"%s\" but UTF-8 is required.\n\n" - . "**Current PHP environment:**\n" - . "- LC_CTYPE: %s\n" - . "- LC_ALL: %s\n" - . "- LANG: %s\n\n" - . "**To fix this issue:**\n" - . "1. Set LC_ALL and LANG environment variables (e.g., LC_ALL=en_US.UTF-8) for your web server user\n" - . "2. Restart your web server after making changes\n" - . "3. Verify with command: `locale charmap` (should return UTF-8)\n\n" - . 'For more details, see: [Issue #4872](https://github.com/LibreSign/libresign/issues/4872)', - $detectedEncoding, - $phpLocale ?: 'not set', - $phpLcAll ?: 'not set', - $phpLang ?: 'not set' - ); - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage(sprintf( - 'Non-UTF-8 encoding detected: %s. This may cause issues with accented or special characters', - $detectedEncoding - )) - ->setResource('java') - ->setTip($tip), - ]; - } - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setSuccessMessage('Java version: ' . $javaVersion) - ->setResource('java'), - (new ConfigureCheckHelper()) - ->setSuccessMessage('Java binary: ' . $javaPath) - ->setResource('java'), - ]; - } - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Java binary not found: ' . $javaPath) - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - return $this->result['java'] = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Java not installed') - ->setResource('java') - ->setTip('Run occ libresign:install --java'), - ]; - } - - private function isJavaOk() : bool { - $checkJava = $this->checkJava(); - $error = array_filter( - $checkJava, - fn (ConfigureCheckHelper $config) => $config->getStatus() === 'error' - ); - return empty($error); - } - - /** - * Check all requirements to use certificate - * - * @return ConfigureCheckHelper[] - */ - public function checkCertificate(): array { - try { - $return = $this->certificateEngineFactory->getEngine()->configureCheck(); - } catch (\Throwable) { - $return = [ - (new ConfigureCheckHelper()) - ->setErrorMessage('Define the certificate engine to use') - ->setResource('certificate-engine') - ->setTip(sprintf('Run occ libresign:configure:%s --help', - $this->certificateEngineFactory->getEngine()->getName() - )), - ]; - } - return $return; - } - - /** - * Check if Imagick extension is loaded - * - * @return ConfigureCheckHelper[] - */ - public function checkImagick(): array { - if (!empty($this->result['imagick'])) { - return $this->result['imagick']; - } - if (!extension_loaded('imagick')) { - return $this->result['imagick'] = [ - (new ConfigureCheckHelper()) - ->setInfoMessage('Imagick extension is not loaded') - ->setResource('imagick') - ->setTip('Install php-imagick to enable visible signatures, background images, and signature element rendering.'), - ]; - } - return $this->result['imagick'] = []; - } -} diff --git a/tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php b/tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php deleted file mode 100644 index 326833091f..0000000000 --- a/tests/php/Unit/Service/Install/ConfigureCheckServiceTest.php +++ /dev/null @@ -1,107 +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); - } - - #[\Override] - 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()); - } - } -} From 0fa908beee5611489df7c2245b05a53ebf8bd9e5 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 16:22:43 +0000 Subject: [PATCH 20/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 99d89e3f1a5d9ee1b81cd2dc1580abbeab3d6e92 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 16:24:42 +0000 Subject: [PATCH 21/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 16caa26cd3526131438bf4ab134c5ee329fc3463 Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 17:47:50 +0000 Subject: [PATCH 22/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 78ccfc5991..aa40806213 100644 --- a/tests/php/Unit/Handler/CertificateEngine/AEngineHandlerTest.php +++ b/tests/php/Unit/Handler/CertificateEngine/AEngineHandlerTest.php @@ -310,7 +310,7 @@ public function testToArrayFiltersConfigPathWhenNotGenerated( public function testToArrayFiltersCaIdFromOrganizationalUnitWhenNotGenerated( bool $certificateGenerated, array $organizationalUnits, - array $expectedOuValues, + ?string $expectedOuValues, string $description, ): void { $instance = $this->getInstance(); @@ -391,37 +391,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 79c5673398eee2ae6e6a654bfa0236df96c30d1d Mon Sep 17 00:00:00 2001 From: Guilherme Carvalho Date: Sat, 7 Mar 2026 19:27:16 +0000 Subject: [PATCH 23/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 9a58d2d554a21c99498b308d5fa0e1636155b0c8 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 20 Jun 2026 10:44:07 -0400 Subject: [PATCH 24/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 25/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 dc7fc63e70..46726a0b43 100644 --- a/lib/Controller/AdminController.php +++ b/lib/Controller/AdminController.php @@ -258,7 +258,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); } /** @@ -297,7 +299,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(); @@ -308,7 +310,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; } } @@ -322,7 +324,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 762efd7befe6e1e39e716d62c4dc6d3a6ec17311 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 22 Jun 2026 07:54:11 -0400 Subject: [PATCH 26/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 43745f84293bf34db4252a167a9c9bfaa156df3a Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 22 Jun 2026 08:31:07 -0400 Subject: [PATCH 27/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 28/68] chore(openapi): regenerate specs for 'info' setup check status Signed-off-by: YvesCesar --- openapi-administration.json | 1 + openapi-full.json | 1 + src/types/openapi/openapi-administration.ts | 2 +- src/types/openapi/openapi-full.ts | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openapi-administration.json b/openapi-administration.json index 6168edf453..186927ebfa 100644 --- a/openapi-administration.json +++ b/openapi-administration.json @@ -231,6 +231,7 @@ "type": "string", "enum": [ "error", + "info", "success" ] }, diff --git a/openapi-full.json b/openapi-full.json index 3b626dd27e..5e60e26445 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -449,6 +449,7 @@ "type": "string", "enum": [ "error", + "info", "success" ] }, diff --git a/src/types/openapi/openapi-administration.ts b/src/types/openapi/openapi-administration.ts index b11d2f0fb9..4f46b46aa7 100644 --- a/src/types/openapi/openapi-administration.ts +++ b/src/types/openapi/openapi-administration.ts @@ -564,7 +564,7 @@ export type components = { message: string; resource: string; /** @enum {string} */ - status: "error" | "success"; + status: "error" | "info" | "success"; tip: string; }; ConfigureChecksResponse: components["schemas"]["ConfigureCheck"][]; diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index f6ff45dcad..15e945f5dd 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -1362,7 +1362,7 @@ export type components = { message: string; resource: string; /** @enum {string} */ - status: "error" | "success"; + status: "error" | "info" | "success"; tip: string; }; ConfigureChecksResponse: components["schemas"]["ConfigureCheck"][]; From 23c9e7025d77c4be1a782f792b0ab6394eee84ea Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Mon, 22 Jun 2026 08:58:07 -0400 Subject: [PATCH 29/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 9a06f610f5c5972840e93e25d3ea1683ae0bcd9a Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Wed, 24 Jun 2026 07:24:28 -0400 Subject: [PATCH 30/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 27befa747a0e27cb5549e17aa93c5354f626113b Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Wed, 24 Jun 2026 08:33:57 -0400 Subject: [PATCH 31/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 33022ea74dec787dff69023afe606e204b213ba9 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Thu, 25 Jun 2026 08:07:04 -0400 Subject: [PATCH 32/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 a69da54c608e84168482d277c3bdbdfe2554ca8c 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 33/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 77a61e7d3f97f001815d81e1acea6eef72269aa2 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 08:54:09 -0400 Subject: [PATCH 34/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 ec8bbea463ef3e553eebcab90e60cea394c391e7 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 35/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 154a1a0af771bfab86dd500998ed4607ba462ec8 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 36/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 ab4ac77f69347a7695be74b9aeaa2e3267d10205 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 10:29:23 -0400 Subject: [PATCH 37/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 7a5feb841d623b404cd48e31c758ccf328cc4ee1 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 38/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 f1a94e1bc104c6d4bb1f9883614eb9c51b42dc26 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 39/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 3b3d1a1789877ef7e414984aad9ade3d50329d5c 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 40/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 a3d1947350b7ba23933624da87aeb15f6407c7af Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 10:51:56 -0400 Subject: [PATCH 41/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 9d84756bb225d503dd8bf9fbf9c5ec3df826aa09 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Fri, 26 Jun 2026 10:59:26 -0400 Subject: [PATCH 42/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 29c63a3c7e64c230e8bad9a3188c7bd80787dc16 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 43/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 6d0918050e63d423d8ef349c4afe97decce842fe Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 08:37:30 -0400 Subject: [PATCH 44/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 1b056063c4ec02a24be5a3bd6b52704dc657081a 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 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 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 d3abec3801a1eed45559095bfb918b5cbafb03d7 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 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 | 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 2b2958631064fc8f01091419afcbf8bbebbb34c2 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 47/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 0a645890370fa9d8fe0d93cac9c148fac473bec3 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 08:45:41 -0400 Subject: [PATCH 48/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 124e6e48d848bdba1cf611464dc5ed1510a4f2b2 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 49/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 008b1360ac6e73c1cbd690dd75ea895fe2a0f3a1 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 08:55:07 -0400 Subject: [PATCH 50/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 6d1b4c550d7286c0bf936d5a1abeff4c786f6199 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 51/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 3745ecbeb534c44cfdd7cde7556089aa462ffc9c 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 52/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 da3583a599f9cf0a93174269e90e204b534f7761 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:01:51 -0400 Subject: [PATCH 53/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 f8b729102f42397c333787c7852d87693f99804e 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 54/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 f9e72d476c5e3c34d1f080032f8d8ee0de11852a Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:11:39 -0400 Subject: [PATCH 55/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 2d426edd21f132d3dfe51fc3edcfa4c5c7368fe6 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 56/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 5fbf9a0e4c2bcf761ddc8d94f9075af1beee8e4c Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:16:55 -0400 Subject: [PATCH 57/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 44f57798d902d69fb1a4c386d5d0574357756410 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 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 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 e155012a26104e95238958ed6677518b7050ac19 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 59/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 7f4533cee9da68f51f30a318acf7a28a5838b92a 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 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 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 0f670ca9771111b666d045c67c1b8e2ccc5bee9e 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 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 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 eb32e97b5960ab21a9288abc54307941c114013d 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 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 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 64e151906c4d8de1acb425ff4c527107cd8e453b 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 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 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 44c924ca6c0821a2c17e2e6211e4cb6144d0a532 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 64/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 b945d8c34c5d4968cda4beb098125f54da3d2826 Mon Sep 17 00:00:00 2001 From: YvesCesar Date: Sat, 27 Jun 2026 09:33:09 -0400 Subject: [PATCH 65/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 e37796ad1364c6ac060e989cd05bb62a3fb99f97 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 28 Jun 2026 17:18:43 -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 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 4b3799433fb853fb799de1aa434398bcb6edfbf4 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 28 Jun 2026 17:18:56 -0300 Subject: [PATCH 67/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 9e022ba6655e82c22383d077658945cc9b63c932 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 28 Jun 2026 17:19:10 -0300 Subject: [PATCH 68/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 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.