diff --git a/.github/workflows/phpunit-integration.yml b/.github/workflows/phpunit-integration.yml
index 08b2bb8..9ba9329 100644
--- a/.github/workflows/phpunit-integration.yml
+++ b/.github/workflows/phpunit-integration.yml
@@ -45,8 +45,9 @@ jobs:
php-versions: ['8.3']
databases: ['mysql']
server-versions: ['stable31']
+ backend: ['remote', 'local'] # Do not change these names, they're used in the integration tests
- name: php-integrationtests${{ matrix.php-versions }}-${{ matrix.databases }}
+ name: php-integrationtests-${{ matrix.backend }}-${{ matrix.php-versions }}-${{ matrix.databases }}
steps:
- name: Checkout server
@@ -75,6 +76,11 @@ jobs:
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, gd, zip, imagick
coverage: none
+ - name: Install ocrmypdf
+ if: matrix.backend == 'local'
+ run: |
+ apt-get update && apt-get install -y ocrmypdf
+
- name: Install composer dependencies
working-directory: apps/${{ env.APP_NAME }}
run: composer i
@@ -93,12 +99,14 @@ jobs:
- name: Checkout AppApi
uses: actions/checkout@v4
+ if: matrix.backend == 'remote'
with:
repository: nextcloud/app_api
ref: ${{ matrix.server-versions }}
path: apps/app_api
- name: Set up AppApi/ExApp infrastructure
+ if: matrix.backend == 'remote'
run: |
./occ app:enable app_api
./occ app_api:daemon:register local_docker "docker-socket-proxy" \
@@ -110,10 +118,12 @@ jobs:
- name: PHPUnit
working-directory: apps/${{ env.APP_NAME }}
+ env:
+ GITHUB_MATRIX_BACKEND: ${{ matrix.backend }}
run: make php-integrationtest
- name: Write OCR Backend logs to file
- if: failure()
+ if: failure() && matrix.backend == 'remote'
run: |
docker logs nc_app_workflow_ocr_backend > data/ocr_backend.log
diff --git a/lib/Helper/SidecarFileAccessor.php b/lib/Helper/SidecarFileAccessor.php
index 0ce696e..dbf84c7 100644
--- a/lib/Helper/SidecarFileAccessor.php
+++ b/lib/Helper/SidecarFileAccessor.php
@@ -49,6 +49,8 @@ public function getOrCreateSidecarFile() {
$this->sidecarFilePath = $this->tempManager->getTemporaryFile('sidecar');
if (!$this->sidecarFilePath) {
$this->logger->warning('Could not create temporary sidecar file');
+ } else if (!is_writable($this->sidecarFilePath)) {
+ $this->logger->warning('Temporary sidecar file is not writable');
}
}
return $this->sidecarFilePath;
diff --git a/lib/OcrProcessors/CommandLineUtils.php b/lib/OcrProcessors/CommandLineUtils.php
index 6c9e52f..9ba1f9b 100644
--- a/lib/OcrProcessors/CommandLineUtils.php
+++ b/lib/OcrProcessors/CommandLineUtils.php
@@ -23,7 +23,6 @@
namespace OCA\WorkflowOcr\OcrProcessors;
-use OCA\WorkflowOcr\Helper\ISidecarFileAccessor;
use OCA\WorkflowOcr\Model\GlobalSettings;
use OCA\WorkflowOcr\Model\WorkflowSettings;
use OCA\WorkflowOcr\Service\IOcrBackendInfoService;
@@ -38,13 +37,12 @@ class CommandLineUtils implements ICommandLineUtils {
];
public function __construct(
- private ISidecarFileAccessor $sidecarFileAccessor,
private IOcrBackendInfoService $ocrBackendInfoService,
private LoggerInterface $logger,
) {
}
- public function getCommandlineArgs(WorkflowSettings $settings, GlobalSettings $globalSettings, array $additionalCommandlineArgs = []): string {
+ public function getCommandlineArgs(WorkflowSettings $settings, GlobalSettings $globalSettings, string $sidecarFile = null, array $additionalCommandlineArgs = []): string {
$isLocalExecution = !$this->ocrBackendInfoService->isRemoteBackend();
// Default setting is quiet
@@ -75,12 +73,9 @@ public function getCommandlineArgs(WorkflowSettings $settings, GlobalSettings $g
$args[] = '--jobs ' . $processorCount;
}
- if ($isLocalExecution) {
+ if ($isLocalExecution && $sidecarFile !== null) {
// Save recognized text in tempfile
- $sidecarFilePath = $this->sidecarFileAccessor->getOrCreateSidecarFile();
- if ($sidecarFilePath) {
- $args[] = '--sidecar ' . $sidecarFilePath;
- }
+ $args[] = '--sidecar ' . $sidecarFile;
}
$resultArgs = array_filter(array_merge(
diff --git a/lib/OcrProcessors/ICommandLineUtils.php b/lib/OcrProcessors/ICommandLineUtils.php
index 141c446..981b2a6 100644
--- a/lib/OcrProcessors/ICommandLineUtils.php
+++ b/lib/OcrProcessors/ICommandLineUtils.php
@@ -27,5 +27,5 @@
use OCA\WorkflowOcr\Model\WorkflowSettings;
interface ICommandLineUtils {
- public function getCommandlineArgs(WorkflowSettings $settings, GlobalSettings $globalSettings, array $additionalCommandlineArgs = []): string;
+ public function getCommandlineArgs(WorkflowSettings $settings, GlobalSettings $globalSettings, string $sidecarFile = null, array $additionalCommandlineArgs = []): string;
}
diff --git a/lib/OcrProcessors/Local/OcrMyPdfBasedProcessor.php b/lib/OcrProcessors/Local/OcrMyPdfBasedProcessor.php
index 5d2a88e..528ff66 100644
--- a/lib/OcrProcessors/Local/OcrMyPdfBasedProcessor.php
+++ b/lib/OcrProcessors/Local/OcrMyPdfBasedProcessor.php
@@ -46,7 +46,8 @@ public function __construct(
public function ocrFile(File $file, WorkflowSettings $settings, GlobalSettings $globalSettings): OcrProcessorResult {
$additionalCommandlineArgs = $this->getAdditionalCommandlineArgs($settings, $globalSettings);
- $commandStr = 'ocrmypdf ' . $this->commandLineUtils->getCommandlineArgs($settings, $globalSettings, $additionalCommandlineArgs) . ' - - || exit $? ; cat';
+ $sidecarFile = $this->sidecarFileAccessor->getOrCreateSidecarFile();
+ $commandStr = 'ocrmypdf ' . $this->commandLineUtils->getCommandlineArgs($settings, $globalSettings, $sidecarFile, $additionalCommandlineArgs) . ' - - || exit $? ; cat';
$inputFileContent = $file->getContent();
@@ -82,7 +83,7 @@ public function ocrFile(File $file, WorkflowSettings $settings, GlobalSettings $
$recognizedText = $this->sidecarFileAccessor->getSidecarFileContent();
if (!$recognizedText) {
- $this->logger->info('Temporary sidecar file at \'{path}\' was empty', ['path' => $this->sidecarFileAccessor->getOrCreateSidecarFile()]);
+ $this->logger->info('Temporary sidecar file at \'{path}\' was empty', ['path' => $sidecarFile]);
}
$this->logger->debug('OCR processing was successful');
diff --git a/tests/Integration/BackendTestBase.php b/tests/Integration/BackendTestBase.php
new file mode 100644
index 0000000..3dd0420
--- /dev/null
+++ b/tests/Integration/BackendTestBase.php
@@ -0,0 +1,215 @@
+
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+namespace OCA\WorkflowOcr\Tests\Integration;
+
+use CurlHandle;
+use DomainException;
+use OCA\WorkflowEngine\Helper\ScopeContext;
+use OCA\WorkflowEngine\Manager;
+use OCA\WorkflowOcr\AppInfo\Application;
+use OCA\WorkflowOcr\BackgroundJobs\ProcessFileJob;
+use OCA\WorkflowOcr\Operation;
+use OCA\WorkflowOcr\Service\IOcrBackendInfoService;
+use OCA\WorkflowOcr\Wrapper\CommandWrapper;
+use OCP\AppFramework\App;
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\WorkflowEngine\IManager;
+use Psr\Container\ContainerInterface;
+use Test\TestCase;
+
+abstract class BackendTestBase extends TestCase {
+ private IConfig $config;
+ private ScopeContext $context;
+ private Manager $workflowEngineManager;
+ private $oldLogLevel;
+ private $operationClass = Operation::class;
+ private array $uploadedFiles = [];
+
+ protected ContainerInterface $container;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $app = new App(Application::APP_NAME);
+ $this->container = $app->getContainer();
+ $this->config = $this->container->get(IConfig::class);
+ $this->workflowEngineManager = $this->container->get(Manager::class);
+ $this->context = new ScopeContext(IManager::SCOPE_ADMIN);
+
+ $this->setNextcloudLogLevel();
+ $this->deleteOperation();
+ }
+
+ protected function tearDown(): void {
+ $this->restoreNextcloudLogLevel();
+ $this->deleteTestFilesIfExist();
+ $this->deleteOperation();
+ parent::tearDown();
+ }
+
+ protected function setNextcloudLogLevel() : void {
+ $this->oldLogLevel = $this->config->getSystemValue('loglevel', 3);
+ $this->config->setSystemValue('loglevel', 0);
+ }
+
+ protected function restoreNextcloudLogLevel() : void {
+ $this->config->setSystemValue('loglevel', $this->oldLogLevel);
+ }
+
+ protected function runOcrBackgroundJob() {
+ /** @var IJoblist */
+ $jobList = $this->container->get(IJobList::class);
+ $job = $jobList->getNext(false, [ProcessFileJob::class]);
+ $this->assertNotNull($job, 'Expected one background job');
+ $job->start($jobList);
+ }
+
+ protected function checkOcrBackendServiceInstalled() : bool {
+ $ocrBackendInfoService = $this->container->get(IOcrBackendInfoService::class);
+ return $ocrBackendInfoService->isRemoteBackend();
+ }
+
+ protected function checkOcrMyPdfInstalled() : bool {
+ $command = new CommandWrapper();
+ $command->setCommand('ocrmypdf --version')->execute();
+ return $command->getExitCode() === 0;
+ }
+
+ protected function addOperation(string $mimeType) {
+ // NOTE :: we're creating the workflow operation via
+ // REST API because if we'd use the manager directly, we'd
+ // face some issues because of caching etc (test ist running
+ // in another process than webserver ...)
+ $url = $this->getNextcloudOcsApiUrl() . 'apps/workflowengine/api/v1/workflows/global?format=json';
+ $json = '
+ {
+ "id":-1,
+ "class":"' . str_replace('\\', '\\\\', $this->operationClass) . '",
+ "entity":"OCA\\\\WorkflowEngine\\\\Entity\\\\File",
+ "events":["\\\\OCP\\\\Files::postCreate"],
+ "name":"",
+ "checks":[
+ {
+ "class":"OCA\\\\WorkflowEngine\\\\Check\\\\FileMimeType",
+ "operator":"is",
+ "value":"' . $mimeType . '",
+ "invalid":false
+ }
+ ],
+ "operation":"",
+ "valid":true
+ }';
+
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, [
+ 'Content-Type: application/json',
+ 'OCS-APIREQUEST: true'
+ ]);
+
+ $this->executeCurl($ch);
+ }
+
+ private function deleteOperation() {
+ $operations = $this->workflowEngineManager->getOperations($this->operationClass, $this->context);
+ foreach ($operations as $operation) {
+ try {
+ $this->workflowEngineManager->deleteOperation($operation['id'], $this->context);
+ } catch (DomainException) {
+ // ignore
+ }
+ }
+
+ }
+
+ protected function uploadTestFile(string $testFile) {
+ $localFile = __DIR__ . "/testdata/" . $testFile;
+ $this->uploadedFiles[] = $localFile;
+ $file = fopen($localFile, 'r');
+
+ $ch = curl_init();
+
+ curl_setopt($ch, CURLOPT_URL, $this->getNextcloudWebdavUrl() . basename($localFile));
+ curl_setopt($ch, CURLOPT_PUT, true);
+ curl_setopt($ch, CURLOPT_INFILE, $file);
+ curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localFile));
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $this->executeCurl($ch);
+ fclose($file);
+ }
+
+ private function deleteTestFilesIfExist() {
+ foreach ($this->uploadedFiles as $localFile) {
+ $ch = curl_init();
+
+ curl_setopt($ch, CURLOPT_URL, $this->getNextcloudWebdavUrl() . basename($localFile));
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ $this->executeCurl($ch, [404]);
+ }
+ $this->uploadedFiles = [];
+ }
+
+ private function executeCurl(CurlHandle $ch, array $allowedNonSuccessResponseCodes = []) : string|bool {
+ curl_setopt($ch, CURLOPT_USERPWD, $this->getNextcloudCredentials());
+
+ $result = curl_exec($ch);
+
+ if (curl_errno($ch)) {
+ $this->fail('cURL Error: ' . curl_error($ch));
+ }
+
+ $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if ($responseCode >= 400 && !in_array($responseCode, $allowedNonSuccessResponseCodes)) {
+ $responseBody = curl_multi_getcontent($ch);
+ $this->fail('cURL HTTP Error ' . $responseCode . ': ' . $responseBody);
+ }
+
+ curl_close($ch);
+
+ return $result;
+ }
+
+ private function getNextcloudWebdavUrl() : string {
+ $port = getenv('NEXTCLOUD_PORT') ?: '80';
+ $user = getenv('NEXTCLOUD_USER') ?: 'admin';
+ return 'http://localhost:' . $port . '/remote.php/dav/files/' . $user . '/';
+ }
+
+ private function getNextcloudOcsApiUrl() : string {
+ $port = getenv('NEXTCLOUD_PORT') ?: '80';
+ return 'http://localhost:' . $port . '/ocs/v2.php/';
+ }
+
+ private function getNextcloudCredentials() : string {
+ $user = getenv('NEXTCLOUD_USER') ?: 'admin';
+ $pass = getenv('NEXTCLOUD_PASS') ?: 'admin';
+ return $user . ':' . $pass;
+ }
+}
\ No newline at end of file
diff --git a/tests/Integration/LocalBackendTest.php b/tests/Integration/LocalBackendTest.php
new file mode 100644
index 0000000..69b77de
--- /dev/null
+++ b/tests/Integration/LocalBackendTest.php
@@ -0,0 +1,95 @@
+
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+namespace OCA\WorkflowOcr\Tests\Integration;
+
+use OCA\WorkflowOcr\Events\TextRecognizedEvent;
+use OCA\WorkflowOcr\OcrProcessors\Remote\Client\IApiClient;
+use OCP\EventDispatcher\IEventDispatcher;
+
+/**
+ * Full test case for registering new OCR Workflow, uploading file and
+ * processing it via local OCR (ocrmypdf CLI) backend.
+ * @group DB
+ */
+class LocalBackendTest extends BackendTestBase {
+ private IntegrationTestApiClient $apiClient;
+ private IEventDispatcher $dispatcher;
+ private array $capturedEvents = [];
+
+ /** @var callable */
+ private $eventListener;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $githubActionsJob = getenv('GITHUB_JOB');
+ $githubActionsMatrixBackend = getenv('GITHUB_MATRIX_BACKEND');
+ $isOcrMyPdfInstalled = $this->checkOcrMyPdfInstalled();
+ $shouldRunOnCi = $githubActionsJob === 'github-php-integrationtests' && $githubActionsMatrixBackend == 'local';
+
+ if ($shouldRunOnCi && !$isOcrMyPdfInstalled) {
+ $this->fail('Running Github Actions Integrationtests but ocrmypdf CLI is not installed');
+ return;
+ }
+
+ if (!$isOcrMyPdfInstalled) {
+ $this->markTestSkipped('ocrmypdf is not installed');
+ return;
+ }
+
+ if ($this->checkOcrBackendServiceInstalled()) {
+ $this->markTestSkipped('OCR Backend service is installed, cannot use local backend (ocrmypdf)');
+ return;
+ }
+
+ $this->dispatcher = $this->container->get(IEventDispatcher::class);
+ $this->apiClient = $this->container->get(IntegrationTestApiClient::class);
+ $this->overwriteService(IApiClient::class, $this->apiClient);
+
+ $this->eventListener = function(TextRecognizedEvent $event) {
+ $this->capturedEvents[] = $event;
+ };
+ $this->dispatcher->addListener(TextRecognizedEvent::class, $this->eventListener);
+ }
+
+ protected function tearDown(): void {
+ parent::tearDown();
+ $this->dispatcher->removeListener(TextRecognizedEvent::class, $this->eventListener);
+ }
+
+ /**
+ * Test processing a file via ocrmypdf CLI.
+ */
+ public function testWorkflowOcrLocalBackend(): void {
+ $this->addOperation('application/pdf');
+ $this->uploadTestFile('document-ready-for-ocr.pdf');
+ $this->runOcrBackgroundJob();
+
+ $this->assertEmpty($this->apiClient->getRequests(), 'Expected no OCR Backend Service requests');
+ $this->assertEquals(1, count($this->capturedEvents), 'Expected 1 TextRecognizedEvent');
+ $textRecognizedEvent = $this->capturedEvents[0];
+ $this->assertInstanceOf(TextRecognizedEvent::class, $textRecognizedEvent, 'Expected TextRecognizedEvent instance');
+ $this->assertEquals('This document is ready for OCR', trim($textRecognizedEvent->getRecognizedText()), 'Expected recognized text');
+ }
+}
\ No newline at end of file
diff --git a/tests/Integration/OcrBackendServiceTest.php b/tests/Integration/OcrBackendServiceTest.php
index 704d9e7..c1a3235 100644
--- a/tests/Integration/OcrBackendServiceTest.php
+++ b/tests/Integration/OcrBackendServiceTest.php
@@ -23,83 +23,43 @@
namespace OCA\WorkflowOcr\Tests\Integration;
-use CurlHandle;
-use DomainException;
-use OCA\WorkflowEngine\Helper\ScopeContext;
-use OCA\WorkflowEngine\Manager;
-use OCA\WorkflowOcr\AppInfo\Application;
-use OCA\WorkflowOcr\BackgroundJobs\ProcessFileJob;
use OCA\WorkflowOcr\OcrProcessors\Remote\Client\IApiClient;
use OCA\WorkflowOcr\OcrProcessors\Remote\Client\Model\OcrResult;
-use OCA\WorkflowOcr\Operation;
-use OCA\WorkflowOcr\Service\IOcrBackendInfoService;
-use OCP\AppFramework\App;
-use OCP\BackgroundJob\IJobList;
-use OCP\IConfig;
-use OCP\WorkflowEngine\IManager;
-use Psr\Container\ContainerInterface;
-use Test\TestCase;
/**
* Full test case for registering new OCR Workflow, uploading file and
* processing it via OCR Backend Service.
* @group DB
*/
-class OcrBackendServiceTest extends TestCase {
- private ContainerInterface $container;
- private Manager $workflowEngineManager;
+class OcrBackendServiceTest extends BackendTestBase {
private IntegrationTestApiClient $apiClient;
- private ScopeContext $context;
- private IConfig $config;
- private $operationClass = Operation::class;
- private $oldLogLevel;
protected function setUp(): void {
parent::setUp();
- $app = new App(Application::APP_NAME);
- $this->container = $app->getContainer();
- $this->workflowEngineManager = $this->container->get(Manager::class);
- $this->apiClient = $this->container->get(IntegrationTestApiClient::class);
- $this->config = $this->container->get(IConfig::class);
- $this->context = new ScopeContext(IManager::SCOPE_ADMIN);
-
- $this->overwriteService(IApiClient::class, $this->apiClient);
-
$githubActionsJob = getenv('GITHUB_JOB');
- $isOcrBackendInstalled = $this->checkOcrBackendInstalled();
+ $githubActionsMatrixBackend = getenv('GITHUB_MATRIX_BACKEND');
+ $isOcrBackendServiceInstalled = $this->checkOcrBackendServiceInstalled();
+ $shouldRunOnCi = $githubActionsJob === 'github-php-integrationtests' && $githubActionsMatrixBackend == 'remote';
- if ($githubActionsJob === 'github-php-integrationtests' && !$isOcrBackendInstalled) {
+ if ($shouldRunOnCi && !$isOcrBackendServiceInstalled) {
$this->fail('Running Github Actions Integrationtests but OCR Backend is not installed');
return;
}
- if (!$isOcrBackendInstalled) {
+ if (!$isOcrBackendServiceInstalled) {
$this->markTestSkipped('OCR Backend is not installed');
return;
}
- $this->setNextcloudLogLevel();
- $this->deleteTestFileIfExists();
- $this->deleteOperation();
- }
-
- protected function tearDown(): void {
- if (!$this->checkOcrBackendInstalled()) {
- return;
- }
-
- $this->deleteTestFileIfExists();
- $this->deleteOperation();
- $this->restoreNextcloudLogLevel();
-
- parent::tearDown();
+ $this->apiClient = $this->container->get(IntegrationTestApiClient::class);
+ $this->overwriteService(IApiClient::class, $this->apiClient);
}
public function testWorkflowOcrBackendService() {
- $this->addOperation();
- $this->uploadTestFile();
+ $this->addOperation('application/pdf');
+ $this->uploadTestFile('document-ready-for-ocr.pdf');
$this->runOcrBackgroundJob();
$requests = $this->apiClient->getRequests();
@@ -117,144 +77,4 @@ public function testWorkflowOcrBackendService() {
$this->assertEquals('application/pdf', $ocrResult->getContentType(), 'Expected content type in response');
$this->assertTrue(strpos($ocrResult->getRecognizedText(), 'This document is ready for OCR') >= 0, 'Expected recognized text in response');
}
-
- private function addOperation() {
- // NOTE :: we're creating the workflow operation via
- // REST API because if we'd use the manager directly, we'd
- // face some issues because of caching etc (test ist running
- // in another process than webserver ...)
- $url = $this->getNextcloudOcsApiUrl() . 'apps/workflowengine/api/v1/workflows/global?format=json';
- $json = '
- {
- "id":-1,
- "class":"' . str_replace('\\', '\\\\', $this->operationClass) . '",
- "entity":"OCA\\\\WorkflowEngine\\\\Entity\\\\File",
- "events":["\\\\OCP\\\\Files::postCreate"],
- "name":"",
- "checks":[
- {
- "class":"OCA\\\\WorkflowEngine\\\\Check\\\\FileMimeType",
- "operator":"is",
- "value":"application/pdf",
- "invalid":false
- }
- ],
- "operation":"",
- "valid":true
- }';
-
- $ch = curl_init($url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
- curl_setopt($ch, CURLOPT_HTTPHEADER, [
- 'Content-Type: application/json',
- 'OCS-APIREQUEST: true'
- ]);
-
- $this->executeCurl($ch);
- }
-
- private function deleteOperation() {
- $operations = $this->workflowEngineManager->getOperations($this->operationClass, $this->context);
- foreach ($operations as $operation) {
- try {
- $this->workflowEngineManager->deleteOperation($operation['id'], $this->context);
- } catch (DomainException) {
- // ignore
- }
- }
-
- }
-
- private function uploadTestFile() {
- $localFile = $this->getTestFileReadyForOcr();
- $file = fopen($localFile, 'r');
-
- $ch = curl_init();
-
- curl_setopt($ch, CURLOPT_URL, $this->getNextcloudWebdavUrl() . basename($localFile));
- curl_setopt($ch, CURLOPT_PUT, true);
- curl_setopt($ch, CURLOPT_INFILE, $file);
- curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localFile));
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-
- $this->executeCurl($ch);
- fclose($file);
- }
-
- private function deleteTestFileIfExists() {
- $localFile = $this->getTestFileReadyForOcr();
-
- $ch = curl_init();
-
- curl_setopt($ch, CURLOPT_URL, $this->getNextcloudWebdavUrl() . basename($localFile));
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-
- $this->executeCurl($ch, [404]);
- }
-
- private function executeCurl(CurlHandle $ch, array $allowedNonSuccessResponseCodes = []) : string|bool {
- curl_setopt($ch, CURLOPT_USERPWD, $this->getNextcloudCredentials());
-
- $result = curl_exec($ch);
-
- if (curl_errno($ch)) {
- $this->fail('cURL Error: ' . curl_error($ch));
- }
-
- $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- if ($responseCode >= 400 && !in_array($responseCode, $allowedNonSuccessResponseCodes)) {
- $responseBody = curl_multi_getcontent($ch);
- $this->fail('cURL HTTP Error ' . $responseCode . ': ' . $responseBody);
- }
-
- curl_close($ch);
-
- return $result;
- }
-
- private function runOcrBackgroundJob() {
- /** @var IJoblist */
- $jobList = $this->container->get(IJobList::class);
- $job = $jobList->getNext(false, [ProcessFileJob::class]);
- $this->assertNotNull($job);
- $job->start($jobList);
- }
-
- private function checkOcrBackendInstalled() : bool {
- $ocrBackendInfoService = $this->container->get(IOcrBackendInfoService::class);
- return $ocrBackendInfoService->isRemoteBackend();
- }
-
- private function getNextcloudWebdavUrl() : string {
- $port = getenv('NEXTCLOUD_PORT') ?: '80';
- $user = getenv('NEXTCLOUD_USER') ?: 'admin';
- return 'http://localhost:' . $port . '/remote.php/dav/files/' . $user . '/';
- }
-
- private function getNextcloudOcsApiUrl() : string {
- $port = getenv('NEXTCLOUD_PORT') ?: '80';
- return 'http://localhost:' . $port . '/ocs/v2.php/';
- }
-
- private function getNextcloudCredentials() : string {
- $user = getenv('NEXTCLOUD_USER') ?: 'admin';
- $pass = getenv('NEXTCLOUD_PASS') ?: 'admin';
- return $user . ':' . $pass;
- }
-
- private function getTestFileReadyForOcr() : string {
- return __DIR__ . '/testdata/document-ready-for-ocr.pdf';
- }
-
- private function setNextcloudLogLevel() : void {
- $this->oldLogLevel = $this->config->getSystemValue('loglevel', 3);
- $this->config->setSystemValue('loglevel', 0);
- }
-
- private function restoreNextcloudLogLevel() : void {
- $this->config->setSystemValue('loglevel', $this->oldLogLevel);
- }
}
diff --git a/tests/Unit/Helper/SidecarFileAccessorTest.php b/tests/Unit/Helper/SidecarFileAccessorTest.php
index 2fe03ed..8beb16c 100644
--- a/tests/Unit/Helper/SidecarFileAccessorTest.php
+++ b/tests/Unit/Helper/SidecarFileAccessorTest.php
@@ -42,19 +42,20 @@ class SidecarFileAccessorTest extends TestCase {
/** @var string */
private $tmpFilePath;
+ /** @var resource */
+ private $tmpFileHandle;
+
public function setUp(): void {
$this->tempManager = $this->createMock(ITempManager::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->accessor = new SidecarFileAccessor($this->tempManager, $this->logger);
- $tmpFile = tmpfile();
- $this->tmpFilePath = stream_get_meta_data($tmpFile)['uri'];
+ $this->tmpFileHandle = tmpfile();
+ $this->tmpFilePath = stream_get_meta_data($this->tmpFileHandle)['uri'];
parent::setUp();
}
public function tearDown(): void {
- if (file_exists($this->tmpFilePath)) {
- unlink($this->tmpFilePath);
- }
+ fclose($this->tmpFileHandle);
parent::tearDown();
}
@@ -94,4 +95,19 @@ public function testLogsWarningIfCreateFails() {
$sidecarFileContent = $this->accessor->getSidecarFileContent();
$this->assertEquals('', $sidecarFileContent);
}
+
+ public function testLogsWarningIfFileIsNotWriteable() {
+ chmod($this->tmpFilePath, 0111);
+ $this->tempManager->expects($this->once())
+ ->method('getTemporaryFile')
+ ->with('sidecar')
+ ->willReturn($this->tmpFilePath);
+
+ $this->logger->expects($this->once())
+ ->method('warning')
+ ->with('Temporary sidecar file is not writable');
+
+ $sidecarFilePath = $this->accessor->getOrCreateSidecarFile();
+ $this->assertEquals($this->tmpFilePath, $sidecarFilePath);
+ }
}
diff --git a/tests/Unit/OcrProcessors/Local/ImageOcrProcessorTest.php b/tests/Unit/OcrProcessors/Local/ImageOcrProcessorTest.php
index d0a0567..46a9a4c 100644
--- a/tests/Unit/OcrProcessors/Local/ImageOcrProcessorTest.php
+++ b/tests/Unit/OcrProcessors/Local/ImageOcrProcessorTest.php
@@ -47,7 +47,7 @@ public function testOcrFileSetsImageDpi() {
/** @var ICommandLineUtils|MockObject $commandLineUtils */
$commandLineUtils = $this->createMock(ICommandLineUtils::class);
$commandLineUtils->method('getCommandlineArgs')
- ->willReturnCallback(fn ($settings, $globalSettings, $additionalCommandlineArgs) => implode(' ', $additionalCommandlineArgs));
+ ->willReturnCallback(fn ($settings, $globalSettings, $sidecarFile, $additionalCommandlineArgs) => implode(' ', $additionalCommandlineArgs));
$processor = new ImageOcrProcessor($command, $logger, $sidecarFileAccessor, $commandLineUtils);
diff --git a/tests/Unit/OcrProcessors/Local/PdfOcrProcessorTest.php b/tests/Unit/OcrProcessors/Local/PdfOcrProcessorTest.php
index c064527..9ab7444 100644
--- a/tests/Unit/OcrProcessors/Local/PdfOcrProcessorTest.php
+++ b/tests/Unit/OcrProcessors/Local/PdfOcrProcessorTest.php
@@ -70,7 +70,7 @@ protected function setUp(): void {
$this->logger = $this->createMock(LoggerInterface::class);
$this->sidecarFileAccessor = $this->createMock(ISidecarFileAccessor::class);
$this->ocrBackendInfoService = $this->createMock(IOcrBackendInfoService::class);
- $this->commandLineUtils = new CommandLineUtils($this->sidecarFileAccessor, $this->ocrBackendInfoService, $this->logger);
+ $this->commandLineUtils = new CommandLineUtils($this->ocrBackendInfoService, $this->logger);
$this->defaultSettings = new WorkflowSettings();
$this->defaultGlobalSettings = new GlobalSettings();