Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions tests/integration/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion tests/integration/config/behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ default:
base_url: http://mailpit:8025
PhpBuiltin\Server:
runAs: www-data
workers: 10
# Keep the minimum workers needed for occasional nested HTTP to the same
# PHP built-in server (single-process deadlocks on self-requests). Prefer
# inline <SMALL_VALID_PDF_BASE64> fixtures over url→/develop/pdf to avoid load-driven
# cURL 52 flakes from higher PHP_CLI_SERVER_WORKERS values.
workers: 2
jarnaiz\JUnitFormatter\JUnitFormatterExtension:
filename: report.xml
outputDir: '%paths.base%/../output/'
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/features/account/signature.feature
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Feature: account/signature
And sending "post" to ocs "/apps/libresign/api/v1/policies/system/identify_methods"
| value | (string){"can_create_account":false,"factors":[{"name":"email","enabled":true,"requirement":"required"}]} |
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"email","value":"signer@test.coop"}]}] |
| name | document |
When as user ""
Expand Down
23 changes: 23 additions & 0 deletions tests/integration/features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

use Behat\Gherkin\Node\TableNode;
use Behat\Hook\AfterSuite;
use Behat\Hook\BeforeScenario;
use Behat\Hook\BeforeSuite;
use Behat\Step\Given;
Expand All @@ -25,11 +26,18 @@ class FeatureContext extends NextcloudApiContext implements OpenedEmailStorageAw
#[BeforeSuite()]
public static function beforeSuite(BeforeSuiteScope $scope):void {
parent::beforeSuite($scope);
FixtureHttpServer::start();
self::runCommand('config:system:set debug --value true --type boolean');
self::runCommand('config:system:set allow_local_remote_servers --value true --type boolean');
self::runCommand('app:enable --force libresign');
self::runCommand('app:enable --force notifications');
}

#[AfterSuite()]
public static function afterSuite(): void {
FixtureHttpServer::stop();
}

#[BeforeScenario()]
public static function beforeScenario(): void {
parent::beforeScenario();
Expand All @@ -51,6 +59,8 @@ protected function parseText(string $text): string {
$fields = $this->fields;
$fields['BASE_URL'] = $this->baseUrl . '/index.php';
$fields['TSA_URL'] = getenv('LIBRESIGN_TSA_URL') ?: 'https://freetsa.org/tsr';
$fields['SMALL_VALID_PDF_BASE64'] = $this->getSmallValidPdfBase64();
$fields['SMALL_VALID_PDF_URL'] = FixtureHttpServer::getSmallValidPdfUrl();
$patterns = [];
$replacements = [];
foreach ($fields as $key => $value) {
Expand All @@ -62,6 +72,19 @@ protected function parseText(string $text): string {
return $text;
}

/**
* Inline PDF fixture for Behat requests that do not need to exercise url download.
*
* Prefer this over url→the Behat Nextcloud server. For url coverage use <SMALL_VALID_PDF_URL>,
* which is served by FixtureHttpServer on a separate local port.
*/
private function getSmallValidPdfBase64(): string {
$pdfPath = __DIR__ . '/../../../php/fixtures/pdfs/small_valid.pdf';
$contents = file_get_contents($pdfPath);
Assert::assertNotFalse($contents, 'Demo PDF fixture missing: ' . $pdfPath);
return 'data:application/pdf;base64,' . base64_encode($contents);
}

#[Given('I fetch the signer UUID from opened email')]
public function iFetchTheLinkOnOpenedEmail(): void {
if (!$this->openedEmailStorage->hasOpenedEmail()) {
Expand Down
107 changes: 107 additions & 0 deletions tests/integration/features/bootstrap/FixtureHttpServer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

/**
* Serves Behat PDF fixtures on a separate PHP built-in server.
*
* This must not be the same process as the Nextcloud Behat server, so
* request-signature can exercise {"url": ...} without nested self-HTTP.
*/
final class FixtureHttpServer {
private static string $pid = '0';
private static string $host = '127.0.0.1';
private static int $port = 0;
private static string $documentRoot = '';

public static function start(): void {
if (self::isRunning()) {
return;
}

self::$documentRoot = realpath(__DIR__ . '/../../../php/fixtures/pdfs');
if (self::$documentRoot === false || !is_file(self::$documentRoot . '/small_valid.pdf')) {
throw new RuntimeException('PDF fixture directory or small_valid.pdf is missing.');
}

self::$port = self::findOpenPort(self::$host);
$cmd = sprintf(
'php -S %s:%d -t %s > /dev/null 2>&1 & echo $!',
escapeshellarg(self::$host),
self::$port,
escapeshellarg(self::$documentRoot)
);

self::$pid = trim((string)shell_exec($cmd));
if (self::$pid === '' || !ctype_digit(self::$pid)) {
throw new RuntimeException('Failed to start fixture HTTP server process.');
}

for ($attempt = 0; $attempt < 30; $attempt++) {
usleep(100000);
$socket = @fsockopen(self::$host, self::$port);
if (is_resource($socket)) {
fclose($socket);
register_shutdown_function(static function (): void {
self::stop();
});
return;
}
}

self::stop();
throw new RuntimeException('Fixture HTTP server did not become ready in time.');
}

public static function stop(): void {
if (self::isRunning()) {
exec('kill ' . self::$pid);
}
self::$pid = '0';
self::$port = 0;
}

public static function getSmallValidPdfUrl(): string {
if (!self::isRunning() || self::$port <= 0) {
throw new RuntimeException('Fixture HTTP server is not running.');
}

return sprintf('http://%s:%d/small_valid.pdf', self::$host, self::$port);
}

private static function isRunning(): bool {
if (self::$pid === '' || self::$pid === '0') {
return false;
}

exec(sprintf('ps %d', (int)self::$pid), $result);

return count($result) > 1;
}

private static function findOpenPort(string $host): int {
$server = @stream_socket_server('tcp://' . $host . ':0', $errno, $errstr);
if ($server === false) {
throw new RuntimeException('Unable to allocate local port for fixture HTTP server: ' . $errstr);
}

$name = stream_socket_get_name($server, false);
fclose($server);

if (!is_string($name) || !str_contains($name, ':')) {
throw new RuntimeException('Unable to detect allocated local port for fixture HTTP server.');
}

$parts = explode(':', $name);
$port = (int)end($parts);
if ($port <= 0) {
throw new RuntimeException('Invalid allocated port for fixture HTTP server.');
}

return $port;
}
}
6 changes: 3 additions & 3 deletions tests/integration/features/file/envelope.feature
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Feature: envelope
And sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
| rootCert | {"commonName":"test"} |
When sending "post" to ocs "/apps/libresign/api/v1/file"
| files | [{"url":"<BASE_URL>/apps/libresign/develop/pdf"},{"url":"<BASE_URL>/apps/libresign/develop/pdf"}] |
| files | [{"base64":"<SMALL_VALID_PDF_BASE64>"},{"base64":"<SMALL_VALID_PDF_BASE64>"}] |
| name | Contract Package |
Then the response should have a status code 422
And the response should be a JSON array with the following mandatory values
Expand Down Expand Up @@ -43,7 +43,7 @@ Feature: envelope
And sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
| rootCert | {"commonName":"test"} |
When sending "post" to ocs "/apps/libresign/api/v1/file"
| files | [{"url":"<BASE_URL>/apps/libresign/develop/pdf"},{"url":"<BASE_URL>/apps/libresign/develop/pdf"},{"url":"<BASE_URL>/apps/libresign/develop/pdf"}] |
| files | [{"base64":"<SMALL_VALID_PDF_BASE64>"},{"base64":"<SMALL_VALID_PDF_BASE64>"},{"base64":"<SMALL_VALID_PDF_BASE64>"}] |
| name | Too Many Files |
Then the response should have a status code 422
And the response should be a JSON array with the following mandatory values
Expand All @@ -55,7 +55,7 @@ Feature: envelope
And sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
| rootCert | {"commonName":"test"} |
When sending "post" to ocs "/apps/libresign/api/v1/file"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| name | Single Document |
Then the response should have a status code 200
And the response should be a JSON array with the following mandatory values
Expand Down
12 changes: 6 additions & 6 deletions tests/integration/features/file/list.feature
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Feature: file-list
And set the email of user "signer1" to "signer1@domain.test"
And set the email of user "signer2" to ""
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"email","value":"signer1@domain.test"}]},{"identifyMethods":[{"method":"account","value":"signer2"}]}] |
| name | document |
And the response should have a status code 200
Expand Down Expand Up @@ -47,25 +47,25 @@ Feature: file-list
And sending "post" to ocs "/apps/libresign/api/v1/policies/system/identify_methods"
| value | (string){"can_create_account":false,"factors":[{"name":"email","enabled":true,"requirement":"required"}]} |
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"email","value":"signer1@domain.test"}]}] |
| name | document |
And the response should have a status code 200
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"email","value":"signer1@domain.test"}]}] |
| name | document |
And the response should have a status code 200
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"email","value":"signer1@domain.test"}]}] |
| name | document |
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"email","value":"signer1@domain.test"}]}] |
| name | document |
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"email","value":"signer1@domain.test"}]}] |
| name | document |
And the response should have a status code 200
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/features/file/thumbnail.feature
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Feature: file-thumbnail
And sending "post" to ocs "/apps/libresign/api/v1/policies/system/identify_methods"
| value | (string){"factors":[{"name":"account","enabled":true,"requirement":"required"}]} |
And sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]}] |
| name | document |
And the response should have a status code 200
Expand Down
10 changes: 5 additions & 5 deletions tests/integration/features/file/validate.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: validate
And sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
| rootCert | {"commonName":"test"} |
When sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"account","value":"admin"}]},{"identifyMethods":[{"method":"account","value":"signer1"}]}] |
| status | 0 |
| name | Visible elements validation |
Expand Down Expand Up @@ -76,7 +76,7 @@ Feature: validate
| value | (string){"factors":[{"name":"account","enabled":true,"requirement":"required","signatureMethods":{"clickToSign":{"enabled":true}}}]} |
And user "signer1" exists
When sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]}] |
| name | Document Name |
Then the response should have a status code 200
Expand Down Expand Up @@ -122,7 +122,7 @@ Feature: validate
And the response should have a status code 200

When sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"account","value":"admin"}]}] |
| name | document |
And the response should have a status code 200
Expand All @@ -144,7 +144,7 @@ Feature: validate
And the response should have a status code 200

When sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"account","value":"admin"}]}] |
| name | Requester legal information |
Then the response should have a status code 200
Expand All @@ -168,7 +168,7 @@ Feature: validate
And the response should have a status code 200

When sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| file | {"base64":"<SMALL_VALID_PDF_BASE64>"} |
| signers | [{"identifyMethods":[{"method":"account","value":"admin"}]}] |
| name | Validation page requester legal information |
Then the response should have a status code 200
Expand Down
Loading
Loading