mirror of
https://github.com/LibreSign/libresign.git
synced 2025-12-18 05:20:45 +01:00
Merge pull request #6184 from LibreSign/refactor/extract-status-services
refactor: extract status services
This commit is contained in:
commit
7a542b3a88
8 changed files with 452 additions and 109 deletions
32
lib/Service/FileStatusService.php
Normal file
32
lib/Service/FileStatusService.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Libresign\Service;
|
||||
|
||||
use OCA\Libresign\Db\File as FileEntity;
|
||||
use OCA\Libresign\Db\FileMapper;
|
||||
|
||||
class FileStatusService {
|
||||
public function __construct(
|
||||
private FileMapper $fileMapper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function updateFileStatusIfUpgrade(FileEntity $file, int $newStatus): FileEntity {
|
||||
$currentStatus = $file->getStatus();
|
||||
if ($newStatus > $currentStatus) {
|
||||
$file->setStatus($newStatus);
|
||||
$this->fileMapper->update($file);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
public function canNotifySigners(?int $fileStatus): bool {
|
||||
return $fileStatus === FileEntity::STATUS_ABLE_TO_SIGN;
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,8 @@ class RequestSignatureService {
|
|||
protected SequentialSigningService $sequentialSigningService,
|
||||
protected IAppConfig $appConfig,
|
||||
protected IEventDispatcher $eventDispatcher,
|
||||
protected FileStatusService $fileStatusService,
|
||||
protected SignRequestStatusService $signRequestStatusService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +78,7 @@ class RequestSignatureService {
|
|||
public function saveFile(array $data): FileEntity {
|
||||
if (!empty($data['uuid'])) {
|
||||
$file = $this->fileMapper->getByUuid($data['uuid']);
|
||||
return $this->updateStatus($file, $data['status'] ?? 0);
|
||||
return $this->fileStatusService->updateFileStatusIfUpgrade($file, $data['status'] ?? 0);
|
||||
}
|
||||
$fileId = null;
|
||||
if (isset($data['file']['fileNode']) && $data['file']['fileNode'] instanceof Node) {
|
||||
|
|
@ -87,7 +89,7 @@ class RequestSignatureService {
|
|||
if (!is_null($fileId)) {
|
||||
try {
|
||||
$file = $this->fileMapper->getByFileId($fileId);
|
||||
return $this->updateStatus($file, $data['status'] ?? 0);
|
||||
return $this->fileStatusService->updateFileStatusIfUpgrade($file, $data['status'] ?? 0);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
|
@ -136,15 +138,6 @@ class RequestSignatureService {
|
|||
$file->setSignatureFlowEnum($globalFlow);
|
||||
}
|
||||
|
||||
private function updateStatus(FileEntity $file, int $status): FileEntity {
|
||||
if ($status > $file->getStatus()) {
|
||||
$file->setStatus($status);
|
||||
/** @var FileEntity */
|
||||
return $this->fileMapper->update($file);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function getFileMetadata(\OCP\Files\Node $node): array {
|
||||
$metadata = [];
|
||||
if ($extension = strtolower($node->getExtension())) {
|
||||
|
|
@ -244,7 +237,7 @@ class RequestSignatureService {
|
|||
],
|
||||
displayName: $user['displayName'] ?? '',
|
||||
description: $user['description'] ?? '',
|
||||
notify: empty($user['notify']) && $this->isStatusAbleToNotify($fileStatus),
|
||||
notify: empty($user['notify']),
|
||||
fileId: $fileId,
|
||||
signingOrder: $signingOrder,
|
||||
fileStatus: $fileStatus,
|
||||
|
|
@ -256,7 +249,7 @@ class RequestSignatureService {
|
|||
identifyMethods: $user['identify'],
|
||||
displayName: $user['displayName'] ?? '',
|
||||
description: $user['description'] ?? '',
|
||||
notify: empty($user['notify']) && $this->isStatusAbleToNotify($fileStatus),
|
||||
notify: empty($user['notify']),
|
||||
fileId: $fileId,
|
||||
signingOrder: $signingOrder,
|
||||
fileStatus: $fileStatus,
|
||||
|
|
@ -268,13 +261,6 @@ class RequestSignatureService {
|
|||
return $return;
|
||||
}
|
||||
|
||||
private function isStatusAbleToNotify(?int $status): bool {
|
||||
return in_array($status, [
|
||||
FileEntity::STATUS_ABLE_TO_SIGN,
|
||||
FileEntity::STATUS_PARTIAL_SIGNED,
|
||||
]);
|
||||
}
|
||||
|
||||
private function associateToSigner(
|
||||
array $identifyMethods,
|
||||
string $displayName,
|
||||
|
|
@ -302,13 +288,16 @@ class RequestSignatureService {
|
|||
$currentStatus = $signRequest->getStatusEnum();
|
||||
|
||||
if ($isNewSignRequest || $currentStatus === \OCA\Libresign\Enum\SignRequestStatus::DRAFT) {
|
||||
$desiredStatus = $this->determineInitialStatus($signingOrder, $fileStatus, $signerStatus, $currentStatus, $fileId);
|
||||
$this->updateStatusIfAllowed($signRequest, $currentStatus, $desiredStatus, $isNewSignRequest);
|
||||
$desiredStatus = $this->signRequestStatusService->determineInitialStatus($signingOrder, $fileId, $fileStatus, $signerStatus, $currentStatus);
|
||||
$this->signRequestStatusService->updateStatusIfAllowed($signRequest, $currentStatus, $desiredStatus, $isNewSignRequest);
|
||||
}
|
||||
|
||||
$this->saveSignRequest($signRequest);
|
||||
|
||||
$shouldNotify = $notify && $signRequest->getStatusEnum() === \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN;
|
||||
$shouldNotify = $notify && $this->signRequestStatusService->shouldNotifySignRequest(
|
||||
$signRequest->getStatusEnum(),
|
||||
$fileStatus
|
||||
);
|
||||
|
||||
foreach ($identifyMethodsIncances as $identifyMethod) {
|
||||
$identifyMethod->getEntity()->setSignRequestId($signRequest->getId());
|
||||
|
|
@ -318,68 +307,6 @@ class RequestSignatureService {
|
|||
return $signRequest;
|
||||
}
|
||||
|
||||
private function updateStatusIfAllowed(
|
||||
SignRequestEntity $signRequest,
|
||||
\OCA\Libresign\Enum\SignRequestStatus $currentStatus,
|
||||
\OCA\Libresign\Enum\SignRequestStatus $desiredStatus,
|
||||
bool $isNewSignRequest,
|
||||
): void {
|
||||
if ($isNewSignRequest || $this->sequentialSigningService->isStatusUpgrade($currentStatus, $desiredStatus)) {
|
||||
$signRequest->setStatusEnum($desiredStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private function determineInitialStatus(
|
||||
int $signingOrder,
|
||||
?int $fileStatus = null,
|
||||
?int $signerStatus = null,
|
||||
?\OCA\Libresign\Enum\SignRequestStatus $currentStatus = null,
|
||||
?int $fileId = null,
|
||||
): \OCA\Libresign\Enum\SignRequestStatus {
|
||||
// If fileStatus is explicitly DRAFT (0), keep signer as DRAFT
|
||||
// This allows adding new signers in DRAFT mode even when file is not in DRAFT status
|
||||
if ($fileStatus === FileEntity::STATUS_DRAFT) {
|
||||
return \OCA\Libresign\Enum\SignRequestStatus::DRAFT;
|
||||
}
|
||||
|
||||
// If file status is ABLE_TO_SIGN, apply flow-based logic
|
||||
if ($fileStatus === FileEntity::STATUS_ABLE_TO_SIGN) {
|
||||
if ($this->sequentialSigningService->isOrderedNumericFlow()) {
|
||||
// In ordered flow, only first signer (order 1) should be ABLE_TO_SIGN
|
||||
// Others remain DRAFT until their turn
|
||||
return $signingOrder === 1
|
||||
? \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN
|
||||
: \OCA\Libresign\Enum\SignRequestStatus::DRAFT;
|
||||
}
|
||||
// In parallel flow, all can sign - ignore individual signer status if file is ABLE_TO_SIGN
|
||||
return \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN;
|
||||
}
|
||||
|
||||
// Handle explicit signer status when file status is not DRAFT or ABLE_TO_SIGN
|
||||
if ($signerStatus !== null) {
|
||||
$desiredStatus = \OCA\Libresign\Enum\SignRequestStatus::from($signerStatus);
|
||||
if ($currentStatus !== null && !$this->sequentialSigningService->isStatusUpgrade($currentStatus, $desiredStatus)) {
|
||||
return $currentStatus;
|
||||
}
|
||||
|
||||
// Validate status transition based on signing order
|
||||
if ($fileId !== null) {
|
||||
return $this->sequentialSigningService->validateStatusByOrder($desiredStatus, $signingOrder, $fileId);
|
||||
}
|
||||
|
||||
return $desiredStatus;
|
||||
}
|
||||
|
||||
// Default fallback based on flow type
|
||||
if (!$this->sequentialSigningService->isOrderedNumericFlow()) {
|
||||
return \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN;
|
||||
}
|
||||
|
||||
return $signingOrder === 1
|
||||
? \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN
|
||||
: \OCA\Libresign\Enum\SignRequestStatus::DRAFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IIdentifyMethod[] $identifyMethodsIncances
|
||||
* @param string $displayName
|
||||
|
|
|
|||
|
|
@ -12,14 +12,12 @@ use OCA\Libresign\Db\File as FileEntity;
|
|||
use OCA\Libresign\Db\SignRequestMapper;
|
||||
use OCA\Libresign\Enum\SignatureFlow;
|
||||
use OCA\Libresign\Enum\SignRequestStatus;
|
||||
use OCP\IAppConfig;
|
||||
|
||||
class SequentialSigningService {
|
||||
private int $currentOrder = 1;
|
||||
private ?FileEntity $file = null;
|
||||
|
||||
public function __construct(
|
||||
private IAppConfig $appConfig,
|
||||
private SignRequestMapper $signRequestMapper,
|
||||
private IdentifyMethodService $identifyMethodService,
|
||||
) {
|
||||
|
|
|
|||
93
lib/Service/SignRequestStatusService.php
Normal file
93
lib/Service/SignRequestStatusService.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Libresign\Service;
|
||||
|
||||
use OCA\Libresign\Db\File as FileEntity;
|
||||
use OCA\Libresign\Db\SignRequest as SignRequestEntity;
|
||||
use OCA\Libresign\Enum\SignRequestStatus;
|
||||
|
||||
class SignRequestStatusService {
|
||||
public function __construct(
|
||||
private SequentialSigningService $sequentialSigningService,
|
||||
private FileStatusService $fileStatusService,
|
||||
) {
|
||||
}
|
||||
|
||||
public function shouldNotifySignRequest(SignRequestStatus $signRequestStatus, ?int $fileStatus): bool {
|
||||
return $this->fileStatusService->canNotifySigners($fileStatus)
|
||||
&& $this->canNotifySignRequest($signRequestStatus);
|
||||
}
|
||||
|
||||
public function canNotifySignRequest(SignRequestStatus $status): bool {
|
||||
return $status === SignRequestStatus::ABLE_TO_SIGN;
|
||||
}
|
||||
|
||||
public function updateStatusIfAllowed(
|
||||
SignRequestEntity $signRequest,
|
||||
SignRequestStatus $currentStatus,
|
||||
SignRequestStatus $desiredStatus,
|
||||
bool $isNewSignRequest,
|
||||
): void {
|
||||
if ($isNewSignRequest || $this->sequentialSigningService->isStatusUpgrade($currentStatus, $desiredStatus)) {
|
||||
$signRequest->setStatusEnum($desiredStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public function determineInitialStatus(
|
||||
int $signingOrder,
|
||||
int $fileId,
|
||||
?int $fileStatus = null,
|
||||
?int $signerStatus = null,
|
||||
?SignRequestStatus $currentStatus = null,
|
||||
): SignRequestStatus {
|
||||
if ($fileStatus === FileEntity::STATUS_DRAFT) {
|
||||
return SignRequestStatus::DRAFT;
|
||||
}
|
||||
|
||||
if ($fileStatus === FileEntity::STATUS_ABLE_TO_SIGN) {
|
||||
return $this->determineStatusForAbleToSignFile($signingOrder);
|
||||
}
|
||||
|
||||
if ($signerStatus !== null) {
|
||||
return $this->handleExplicitSignerStatus($signerStatus, $signingOrder, $fileId, $currentStatus);
|
||||
}
|
||||
|
||||
return $this->getDefaultStatusByFlow($signingOrder);
|
||||
}
|
||||
|
||||
private function determineStatusForAbleToSignFile(int $signingOrder): SignRequestStatus {
|
||||
if ($this->sequentialSigningService->isOrderedNumericFlow()) {
|
||||
return $signingOrder === 1 ? SignRequestStatus::ABLE_TO_SIGN : SignRequestStatus::DRAFT;
|
||||
}
|
||||
return SignRequestStatus::ABLE_TO_SIGN;
|
||||
}
|
||||
|
||||
private function handleExplicitSignerStatus(
|
||||
int $signerStatus,
|
||||
int $signingOrder,
|
||||
int $fileId,
|
||||
?SignRequestStatus $currentStatus,
|
||||
): SignRequestStatus {
|
||||
$desiredStatus = SignRequestStatus::from($signerStatus);
|
||||
|
||||
if ($currentStatus !== null && !$this->sequentialSigningService->isStatusUpgrade($currentStatus, $desiredStatus)) {
|
||||
return $currentStatus;
|
||||
}
|
||||
|
||||
return $this->sequentialSigningService->validateStatusByOrder($desiredStatus, $signingOrder, $fileId);
|
||||
}
|
||||
|
||||
private function getDefaultStatusByFlow(int $signingOrder): SignRequestStatus {
|
||||
if (!$this->sequentialSigningService->isOrderedNumericFlow()) {
|
||||
return SignRequestStatus::ABLE_TO_SIGN;
|
||||
}
|
||||
|
||||
return $signingOrder === 1 ? SignRequestStatus::ABLE_TO_SIGN : SignRequestStatus::DRAFT;
|
||||
}
|
||||
}
|
||||
91
tests/php/Unit/Service/FileStatusServiceTest.php
Normal file
91
tests/php/Unit/Service/FileStatusServiceTest.php
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Libresign\Tests\Unit\Service;
|
||||
|
||||
use OCA\Libresign\Db\File as FileEntity;
|
||||
use OCA\Libresign\Db\FileMapper;
|
||||
use OCA\Libresign\Service\FileStatusService;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class FileStatusServiceTest extends TestCase {
|
||||
private FileMapper $fileMapper;
|
||||
private FileStatusService $service;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->fileMapper = $this->createMock(FileMapper::class);
|
||||
$this->service = new FileStatusService($this->fileMapper);
|
||||
}
|
||||
|
||||
#[DataProvider('fileStatusUpgradeScenarios')]
|
||||
public function testUpdateFileStatusIfUpgrade(int $currentStatus, int $newStatus, bool $shouldUpdate): void {
|
||||
$file = new FileEntity();
|
||||
$file->setStatus($currentStatus);
|
||||
|
||||
if ($shouldUpdate) {
|
||||
$this->fileMapper->expects($this->once())
|
||||
->method('update')
|
||||
->with($file)
|
||||
->willReturn($file);
|
||||
} else {
|
||||
$this->fileMapper->expects($this->never())->method('update');
|
||||
}
|
||||
|
||||
$result = $this->service->updateFileStatusIfUpgrade($file, $newStatus);
|
||||
|
||||
$expectedStatus = $shouldUpdate ? $newStatus : $currentStatus;
|
||||
$this->assertEquals($expectedStatus, $result->getStatus());
|
||||
}
|
||||
|
||||
public static function fileStatusUpgradeScenarios(): array {
|
||||
$draft = FileEntity::STATUS_DRAFT;
|
||||
$able = FileEntity::STATUS_ABLE_TO_SIGN;
|
||||
$partial = FileEntity::STATUS_PARTIAL_SIGNED;
|
||||
$signed = FileEntity::STATUS_SIGNED;
|
||||
$deleted = FileEntity::STATUS_DELETED;
|
||||
|
||||
return [
|
||||
[$draft, $able, true],
|
||||
[$draft, $partial, true],
|
||||
[$draft, $signed, true],
|
||||
[$draft, $deleted, true],
|
||||
[$able, $partial, true],
|
||||
[$able, $signed, true],
|
||||
[$able, $deleted, true],
|
||||
[$partial, $signed, true],
|
||||
[$partial, $deleted, true],
|
||||
[$signed, $deleted, true],
|
||||
[$able, $draft, false],
|
||||
[$partial, $draft, false],
|
||||
[$partial, $able, false],
|
||||
[$signed, $draft, false],
|
||||
[$signed, $able, false],
|
||||
[$signed, $partial, false],
|
||||
[$deleted, $draft, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('fileStatusNotificationScenarios')]
|
||||
public function testCanNotifySigners(?int $fileStatus, bool $expected): void {
|
||||
$result = $this->service->canNotifySigners($fileStatus);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
public static function fileStatusNotificationScenarios(): array {
|
||||
return [
|
||||
[FileEntity::STATUS_DRAFT, false],
|
||||
[FileEntity::STATUS_ABLE_TO_SIGN, true],
|
||||
[FileEntity::STATUS_PARTIAL_SIGNED, false],
|
||||
[FileEntity::STATUS_SIGNED, false],
|
||||
[FileEntity::STATUS_DELETED, false],
|
||||
[null, false],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -14,11 +14,13 @@ use OCA\Libresign\Db\SignRequestMapper;
|
|||
use OCA\Libresign\Handler\DocMdpHandler;
|
||||
use OCA\Libresign\Helper\ValidateHelper;
|
||||
use OCA\Libresign\Service\FileElementService;
|
||||
use OCA\Libresign\Service\FileStatusService;
|
||||
use OCA\Libresign\Service\FolderService;
|
||||
use OCA\Libresign\Service\IdentifyMethodService;
|
||||
use OCA\Libresign\Service\PdfParserService;
|
||||
use OCA\Libresign\Service\RequestSignatureService;
|
||||
use OCA\Libresign\Service\SequentialSigningService;
|
||||
use OCA\Libresign\Service\SignRequestStatusService;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\IMimeTypeDetector;
|
||||
use OCP\Http\Client\IClient;
|
||||
|
|
@ -52,6 +54,8 @@ final class RequestSignatureServiceTest extends \OCA\Libresign\Tests\Unit\TestCa
|
|||
private SequentialSigningService&MockObject $sequentialSigningService;
|
||||
private IAppConfig&MockObject $appConfig;
|
||||
private IEventDispatcher&MockObject $eventDispatcher;
|
||||
private FileStatusService&MockObject $fileStatusService;
|
||||
private SignRequestStatusService&MockObject $signRequestStatusService;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
|
@ -79,6 +83,8 @@ final class RequestSignatureServiceTest extends \OCA\Libresign\Tests\Unit\TestCa
|
|||
$this->sequentialSigningService = $this->createMock(SequentialSigningService::class);
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->fileStatusService = $this->createMock(FileStatusService::class);
|
||||
$this->signRequestStatusService = $this->createMock(SignRequestStatusService::class);
|
||||
}
|
||||
|
||||
private function getService(?SequentialSigningService $sequentialSigningService = null): RequestSignatureService {
|
||||
|
|
@ -101,6 +107,8 @@ final class RequestSignatureServiceTest extends \OCA\Libresign\Tests\Unit\TestCa
|
|||
$sequentialSigningService ?? $this->sequentialSigningService,
|
||||
$this->appConfig,
|
||||
$this->eventDispatcher,
|
||||
$this->fileStatusService,
|
||||
$this->signRequestStatusService,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -261,22 +269,21 @@ final class RequestSignatureServiceTest extends \OCA\Libresign\Tests\Unit\TestCa
|
|||
->method('isOrderedNumericFlow')
|
||||
->willReturn(false); // Parallel flow
|
||||
|
||||
$fileStatusService = $this->createMock(FileStatusService::class);
|
||||
$statusService = new SignRequestStatusService($sequentialSigningService, $fileStatusService);
|
||||
|
||||
// File status is ABLE_TO_SIGN (1)
|
||||
$fileStatus = \OCA\Libresign\Db\File::STATUS_ABLE_TO_SIGN;
|
||||
|
||||
// Signer status is DRAFT (0) - as sent by frontend
|
||||
$signerStatus = \OCA\Libresign\Enum\SignRequestStatus::DRAFT->value;
|
||||
|
||||
$result = self::invokePrivate(
|
||||
$this->getService($sequentialSigningService),
|
||||
'determineInitialStatus',
|
||||
[
|
||||
1, // signingOrder
|
||||
$fileStatus,
|
||||
$signerStatus,
|
||||
null, // currentStatus
|
||||
null, // fileId
|
||||
]
|
||||
$result = $statusService->determineInitialStatus(
|
||||
1, // signingOrder
|
||||
123, // fileId
|
||||
$fileStatus,
|
||||
$signerStatus,
|
||||
null, // currentStatus
|
||||
);
|
||||
|
||||
// In parallel flow with ABLE_TO_SIGN file status, signer should be ABLE_TO_SIGN
|
||||
|
|
@ -296,14 +303,15 @@ final class RequestSignatureServiceTest extends \OCA\Libresign\Tests\Unit\TestCa
|
|||
->method('isOrderedNumericFlow')
|
||||
->willReturn(true); // Ordered flow
|
||||
|
||||
$fileStatusService = $this->createMock(FileStatusService::class);
|
||||
$statusService = new SignRequestStatusService($sequentialSigningService, $fileStatusService);
|
||||
|
||||
$fileStatus = \OCA\Libresign\Db\File::STATUS_ABLE_TO_SIGN;
|
||||
$signerStatus = \OCA\Libresign\Enum\SignRequestStatus::DRAFT->value;
|
||||
|
||||
// First signer (order 1) should be ABLE_TO_SIGN
|
||||
$result1 = self::invokePrivate(
|
||||
$this->getService($sequentialSigningService),
|
||||
'determineInitialStatus',
|
||||
[1, $fileStatus, $signerStatus, null, null]
|
||||
$result1 = $statusService->determineInitialStatus(
|
||||
1, 123, $fileStatus, $signerStatus, null
|
||||
);
|
||||
$this->assertEquals(
|
||||
\OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN,
|
||||
|
|
@ -312,10 +320,8 @@ final class RequestSignatureServiceTest extends \OCA\Libresign\Tests\Unit\TestCa
|
|||
);
|
||||
|
||||
// Second signer (order 2) should remain DRAFT
|
||||
$result2 = self::invokePrivate(
|
||||
$this->getService($sequentialSigningService),
|
||||
'determineInitialStatus',
|
||||
[2, $fileStatus, $signerStatus, null, null]
|
||||
$result2 = $statusService->determineInitialStatus(
|
||||
2, 123, $fileStatus, $signerStatus, null
|
||||
);
|
||||
$this->assertEquals(
|
||||
\OCA\Libresign\Enum\SignRequestStatus::DRAFT,
|
||||
|
|
|
|||
|
|
@ -14,24 +14,20 @@ use OCA\Libresign\Enum\SignatureFlow;
|
|||
use OCA\Libresign\Service\IdentifyMethodService;
|
||||
use OCA\Libresign\Service\SequentialSigningService;
|
||||
use OCA\Libresign\Tests\Unit\TestCase;
|
||||
use OCP\IAppConfig;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
final class SequentialSigningServiceTest extends TestCase {
|
||||
private IAppConfig&MockObject $appConfig;
|
||||
private SignRequestMapper&MockObject $signRequestMapper;
|
||||
private IdentifyMethodService&MockObject $identifyMethodService;
|
||||
private SequentialSigningService $service;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->signRequestMapper = $this->createMock(SignRequestMapper::class);
|
||||
$this->identifyMethodService = $this->createMock(IdentifyMethodService::class);
|
||||
|
||||
$this->service = new SequentialSigningService(
|
||||
$this->appConfig,
|
||||
$this->signRequestMapper,
|
||||
$this->identifyMethodService
|
||||
);
|
||||
|
|
|
|||
200
tests/php/Unit/Service/SignRequestStatusServiceTest.php
Normal file
200
tests/php/Unit/Service/SignRequestStatusServiceTest.php
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
use OCA\Libresign\Db\File as FileEntity;
|
||||
use OCA\Libresign\Db\SignRequest as SignRequestEntity;
|
||||
use OCA\Libresign\Enum\SignRequestStatus;
|
||||
use OCA\Libresign\Service\FileStatusService;
|
||||
use OCA\Libresign\Service\SequentialSigningService;
|
||||
use OCA\Libresign\Service\SignRequestStatusService;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
final class SignRequestStatusServiceTest extends \OCA\Libresign\Tests\Unit\TestCase {
|
||||
private SequentialSigningService&MockObject $sequentialSigningService;
|
||||
private FileStatusService&MockObject $fileStatusService;
|
||||
private SignRequestStatusService $service;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->sequentialSigningService = $this->createMock(SequentialSigningService::class);
|
||||
$this->fileStatusService = $this->createMock(FileStatusService::class);
|
||||
$this->service = new SignRequestStatusService(
|
||||
$this->sequentialSigningService,
|
||||
$this->fileStatusService
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider('signRequestStatusNotificationScenarios')]
|
||||
public function testCanNotifySignRequest(SignRequestStatus $status, bool $canNotify): void {
|
||||
$this->assertSame($canNotify, $this->service->canNotifySignRequest($status));
|
||||
}
|
||||
|
||||
public static function signRequestStatusNotificationScenarios(): array {
|
||||
return [
|
||||
[SignRequestStatus::DRAFT, false],
|
||||
[SignRequestStatus::ABLE_TO_SIGN, true],
|
||||
[SignRequestStatus::SIGNED, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('shouldNotifyScenarios')]
|
||||
public function testShouldNotifySignRequest(
|
||||
SignRequestStatus $signRequestStatus,
|
||||
?int $fileStatus,
|
||||
bool $fileCanNotify,
|
||||
bool $signRequestCanNotify,
|
||||
bool $expected,
|
||||
): void {
|
||||
$this->fileStatusService
|
||||
->expects($this->once())
|
||||
->method('canNotifySigners')
|
||||
->with($fileStatus)
|
||||
->willReturn($fileCanNotify);
|
||||
|
||||
$result = $this->service->shouldNotifySignRequest($signRequestStatus, $fileStatus);
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
public static function shouldNotifyScenarios(): array {
|
||||
return [
|
||||
[SignRequestStatus::ABLE_TO_SIGN, FileEntity::STATUS_ABLE_TO_SIGN, true, true, true],
|
||||
[SignRequestStatus::ABLE_TO_SIGN, FileEntity::STATUS_DRAFT, false, true, false],
|
||||
[SignRequestStatus::DRAFT, FileEntity::STATUS_ABLE_TO_SIGN, true, false, false],
|
||||
[SignRequestStatus::SIGNED, FileEntity::STATUS_ABLE_TO_SIGN, true, false, false],
|
||||
[SignRequestStatus::DRAFT, FileEntity::STATUS_DRAFT, false, false, false],
|
||||
[SignRequestStatus::ABLE_TO_SIGN, null, false, true, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('signRequestStatusUpdateScenarios')]
|
||||
public function testUpdateStatusIfAllowed(
|
||||
SignRequestStatus $current,
|
||||
SignRequestStatus $desired,
|
||||
bool $isNew,
|
||||
bool $isUpgrade,
|
||||
bool $shouldUpdate,
|
||||
): void {
|
||||
$signRequest = $this->createMock(SignRequestEntity::class);
|
||||
|
||||
if (!$isNew) {
|
||||
$this->sequentialSigningService
|
||||
->expects($this->once())
|
||||
->method('isStatusUpgrade')
|
||||
->with($current, $desired)
|
||||
->willReturn($isUpgrade);
|
||||
}
|
||||
|
||||
$signRequest
|
||||
->expects($shouldUpdate ? $this->once() : $this->never())
|
||||
->method('setStatusEnum')
|
||||
->with($desired);
|
||||
|
||||
$this->service->updateStatusIfAllowed($signRequest, $current, $desired, $isNew);
|
||||
}
|
||||
|
||||
public static function signRequestStatusUpdateScenarios(): array {
|
||||
$draft = SignRequestStatus::DRAFT;
|
||||
$able = SignRequestStatus::ABLE_TO_SIGN;
|
||||
$signed = SignRequestStatus::SIGNED;
|
||||
|
||||
return [
|
||||
[$draft, $able, true, true, true],
|
||||
[$draft, $signed, true, true, true],
|
||||
[$able, $signed, true, true, true],
|
||||
[$draft, $able, false, true, true],
|
||||
[$draft, $signed, false, true, true],
|
||||
[$able, $signed, false, true, true],
|
||||
[$able, $draft, false, false, false],
|
||||
[$signed, $draft, false, false, false],
|
||||
[$signed, $able, false, false, false],
|
||||
[$draft, $draft, false, false, false],
|
||||
[$able, $able, false, false, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('initialStatusScenarios')]
|
||||
public function testDetermineInitialStatus(
|
||||
int $order,
|
||||
int $fileId,
|
||||
?int $fileStatus,
|
||||
?int $signerStatus,
|
||||
?SignRequestStatus $currentStatus,
|
||||
bool $isOrdered,
|
||||
SignRequestStatus $expected,
|
||||
): void {
|
||||
$this->sequentialSigningService->method('isOrderedNumericFlow')->willReturn($isOrdered);
|
||||
|
||||
if ($signerStatus !== null && $currentStatus !== null) {
|
||||
$desired = SignRequestStatus::from($signerStatus);
|
||||
$this->sequentialSigningService
|
||||
->method('isStatusUpgrade')
|
||||
->with($currentStatus, $desired)
|
||||
->willReturn($expected !== $currentStatus);
|
||||
}
|
||||
|
||||
if ($signerStatus !== null) {
|
||||
$desired = SignRequestStatus::from($signerStatus);
|
||||
$this->sequentialSigningService
|
||||
->method('validateStatusByOrder')
|
||||
->with($desired, $order, $fileId)
|
||||
->willReturn($expected);
|
||||
}
|
||||
|
||||
$result = $this->service->determineInitialStatus($order, $fileId, $fileStatus, $signerStatus, $currentStatus);
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
public static function initialStatusScenarios(): array {
|
||||
$draft = FileEntity::STATUS_DRAFT;
|
||||
$able = FileEntity::STATUS_ABLE_TO_SIGN;
|
||||
$draftStatus = SignRequestStatus::DRAFT;
|
||||
$ableStatus = SignRequestStatus::ABLE_TO_SIGN;
|
||||
$signedStatus = SignRequestStatus::SIGNED;
|
||||
|
||||
return [
|
||||
[1, 123, $draft, null, null, false, $draftStatus],
|
||||
[1, 123, $draft, null, null, true, $draftStatus],
|
||||
[2, 123, $draft, null, null, false, $draftStatus],
|
||||
[2, 123, $draft, null, null, true, $draftStatus],
|
||||
[1, 123, $draft, $ableStatus->value, null, false, $draftStatus],
|
||||
[1, 123, $draft, $ableStatus->value, null, true, $draftStatus],
|
||||
|
||||
[1, 123, $able, null, null, false, $ableStatus],
|
||||
[2, 123, $able, null, null, false, $ableStatus],
|
||||
[3, 123, $able, null, null, false, $ableStatus],
|
||||
[1, 123, $able, null, null, true, $ableStatus],
|
||||
[2, 123, $able, null, null, true, $draftStatus],
|
||||
[3, 123, $able, null, null, true, $draftStatus],
|
||||
[10, 123, $able, null, null, true, $draftStatus],
|
||||
[1, 123, $able, $draftStatus->value, null, false, $ableStatus],
|
||||
[2, 123, $able, $draftStatus->value, null, false, $ableStatus],
|
||||
[1, 123, $able, $draftStatus->value, null, true, $ableStatus],
|
||||
[2, 123, $able, $draftStatus->value, null, true, $draftStatus],
|
||||
|
||||
[1, 123, null, $ableStatus->value, null, false, $ableStatus],
|
||||
[1, 123, null, $ableStatus->value, null, true, $ableStatus],
|
||||
[1, 123, null, $draftStatus->value, null, false, $draftStatus],
|
||||
[1, 123, null, $signedStatus->value, null, false, $signedStatus],
|
||||
[2, 123, null, $ableStatus->value, null, true, $ableStatus],
|
||||
[2, 123, null, $ableStatus->value, null, false, $ableStatus],
|
||||
|
||||
[1, 123, null, $ableStatus->value, $draftStatus, false, $ableStatus],
|
||||
[1, 123, null, $draftStatus->value, $ableStatus, false, $ableStatus],
|
||||
[1, 123, null, $ableStatus->value, $ableStatus, false, $ableStatus],
|
||||
|
||||
[1, 123, null, null, null, false, $ableStatus],
|
||||
[2, 123, null, null, null, false, $ableStatus],
|
||||
[10, 123, null, null, null, false, $ableStatus],
|
||||
[1, 123, null, null, null, true, $ableStatus],
|
||||
[2, 123, null, null, null, true, $draftStatus],
|
||||
[3, 123, null, null, null, true, $draftStatus],
|
||||
[10, 123, null, null, null, true, $draftStatus],
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue