diff options
Diffstat (limited to 'apps/workflowengine/tests/ManagerTest.php')
-rw-r--r-- | apps/workflowengine/tests/ManagerTest.php | 314 |
1 files changed, 253 insertions, 61 deletions
diff --git a/apps/workflowengine/tests/ManagerTest.php b/apps/workflowengine/tests/ManagerTest.php index 612495a5b6d..56e45936b82 100644 --- a/apps/workflowengine/tests/ManagerTest.php +++ b/apps/workflowengine/tests/ManagerTest.php @@ -1,53 +1,40 @@ <?php + /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\WorkflowEngine\Tests; +use OC\Files\Config\UserMountCache; use OC\L10N\L10N; use OCA\WorkflowEngine\Entity\File; use OCA\WorkflowEngine\Helper\ScopeContext; use OCA\WorkflowEngine\Manager; +use OCP\AppFramework\QueryException; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Events\Node\NodeCreatedEvent; use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountManager; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; -use OCP\ILogger; use OCP\IServerContainer; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\IUserSession; +use OCP\Server; use OCP\SystemTag\ISystemTagManager; +use OCP\WorkflowEngine\Events\RegisterEntitiesEvent; use OCP\WorkflowEngine\ICheck; use OCP\WorkflowEngine\IEntity; use OCP\WorkflowEngine\IEntityEvent; use OCP\WorkflowEngine\IManager; use OCP\WorkflowEngine\IOperation; use PHPUnit\Framework\MockObject\MockObject; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Psr\Log\LoggerInterface; use Test\TestCase; /** @@ -57,15 +44,12 @@ use Test\TestCase; * @group DB */ class ManagerTest extends TestCase { - /** @var Manager */ protected $manager; /** @var MockObject|IDBConnection */ protected $db; - /** @var \PHPUnit\Framework\MockObject\MockObject|ILogger */ + /** @var \PHPUnit\Framework\MockObject\MockObject|LoggerInterface */ protected $logger; - /** @var \PHPUnit\Framework\MockObject\MockObject|EventDispatcherInterface */ - protected $legacyDispatcher; /** @var MockObject|IServerContainer */ protected $container; /** @var MockObject|IUserSession */ @@ -76,11 +60,13 @@ class ManagerTest extends TestCase { protected $dispatcher; /** @var MockObject|IConfig */ protected $config; + /** @var MockObject|ICacheFactory */ + protected $cacheFactory; protected function setUp(): void { parent::setUp(); - $this->db = \OC::$server->getDatabaseConnection(); + $this->db = Server::get(IDBConnection::class); $this->container = $this->createMock(IServerContainer::class); /** @var IL10N|MockObject $l */ $this->l = $this->createMock(IL10N::class); @@ -89,21 +75,21 @@ class ManagerTest extends TestCase { return vsprintf($text, $parameters); }); - $this->legacyDispatcher = $this->createMock(EventDispatcherInterface::class); - $this->logger = $this->createMock(ILogger::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->session = $this->createMock(IUserSession::class); $this->dispatcher = $this->createMock(IEventDispatcher::class); $this->config = $this->createMock(IConfig::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); $this->manager = new Manager( - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->container, $this->l, - $this->legacyDispatcher, $this->logger, $this->session, $this->dispatcher, - $this->config + $this->config, + $this->cacheFactory ); $this->clearTables(); } @@ -116,7 +102,7 @@ class ManagerTest extends TestCase { /** * @return MockObject|ScopeContext */ - protected function buildScope(string $scopeId = null): MockObject { + protected function buildScope(?string $scopeId = null): MockObject { $scopeContext = $this->createMock(ScopeContext::class); $scopeContext->expects($this->any()) ->method('getScope') @@ -139,7 +125,7 @@ class ManagerTest extends TestCase { } } - public function testChecks() { + public function testChecks(): void { $check1 = $this->invokePrivate($this->manager, 'addCheck', ['Test', 'equal', 1]); $check2 = $this->invokePrivate($this->manager, 'addCheck', ['Test', '!equal', 2]); @@ -160,7 +146,7 @@ class ManagerTest extends TestCase { $this->assertArrayHasKey($check2, $data); } - public function testScope() { + public function testScope(): void { $adminScope = $this->buildScope(); $userScope = $this->buildScope('jackie'); $entity = File::class; @@ -194,11 +180,37 @@ class ManagerTest extends TestCase { $this->assertTrue($this->invokePrivate($this->manager, 'canModify', [$opId3, $userScope])); } - public function testGetAllOperations() { + public function testGetAllOperations(): void { $adminScope = $this->buildScope(); $userScope = $this->buildScope('jackie'); $entity = File::class; + $adminOperation = $this->createMock(IOperation::class); + $adminOperation->expects($this->any()) + ->method('isAvailableForScope') + ->willReturnMap([ + [IManager::SCOPE_ADMIN, true], + [IManager::SCOPE_USER, false], + ]); + $userOperation = $this->createMock(IOperation::class); + $userOperation->expects($this->any()) + ->method('isAvailableForScope') + ->willReturnMap([ + [IManager::SCOPE_ADMIN, false], + [IManager::SCOPE_USER, true], + ]); + + $this->container->expects($this->any()) + ->method('query') + ->willReturnCallback(function ($className) use ($adminOperation, $userOperation) { + switch ($className) { + case 'OCA\WFE\TestAdminOp': + return $adminOperation; + case 'OCA\WFE\TestUserOp': + return $userOperation; + } + }); + $opId1 = $this->invokePrivate( $this->manager, 'insertOperation', @@ -219,6 +231,13 @@ class ManagerTest extends TestCase { ); $this->invokePrivate($this->manager, 'addScope', [$opId3, $userScope]); + $opId4 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestAdminOp', 'Test04', [41, 10, 4], 'NoBar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId4, $userScope]); + $adminOps = $this->manager->getAllOperations($adminScope); $userOps = $this->manager->getAllOperations($userScope); @@ -232,7 +251,7 @@ class ManagerTest extends TestCase { $this->assertSame(2, count($userOps['OCA\WFE\TestUserOp'])); } - public function testGetOperations() { + public function testGetOperations(): void { $adminScope = $this->buildScope(); $userScope = $this->buildScope('jackie'); $entity = File::class; @@ -269,43 +288,117 @@ class ManagerTest extends TestCase { ); $this->invokePrivate($this->manager, 'addScope', [$opId5, $userScope]); + $operation = $this->createMock(IOperation::class); + $operation->expects($this->any()) + ->method('isAvailableForScope') + ->willReturnMap([ + [IManager::SCOPE_ADMIN, true], + [IManager::SCOPE_USER, true], + ]); + + $this->container->expects($this->any()) + ->method('query') + ->willReturnCallback(function ($className) use ($operation) { + switch ($className) { + case 'OCA\WFE\TestOp': + return $operation; + case 'OCA\WFE\OtherTestOp': + throw new QueryException(); + } + }); + $adminOps = $this->manager->getOperations('OCA\WFE\TestOp', $adminScope); $userOps = $this->manager->getOperations('OCA\WFE\TestOp', $userScope); $this->assertSame(1, count($adminOps)); - array_walk($adminOps, function ($op) { + array_walk($adminOps, function ($op): void { $this->assertTrue($op['class'] === 'OCA\WFE\TestOp'); }); $this->assertSame(2, count($userOps)); - array_walk($userOps, function ($op) { + array_walk($userOps, function ($op): void { $this->assertTrue($op['class'] === 'OCA\WFE\TestOp'); }); } - public function testUpdateOperation() { + public function testGetAllConfiguredEvents(): void { $adminScope = $this->buildScope(); $userScope = $this->buildScope('jackie'); $entity = File::class; + $opId5 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\OtherTestOp', 'Test04', [], 'foo', $entity, [NodeCreatedEvent::class]] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId5, $userScope]); + + $allOperations = null; + + $cache = $this->createMock(ICache::class); + $cache + ->method('get') + ->willReturnCallback(function () use (&$allOperations) { + if ($allOperations) { + return $allOperations; + } + + return null; + }); + + $this->cacheFactory->method('createDistributed')->willReturn($cache); + $allOperations = $this->manager->getAllConfiguredEvents(); + $this->assertCount(1, $allOperations); + + $allOperationsCached = $this->manager->getAllConfiguredEvents(); + $this->assertCount(1, $allOperationsCached); + $this->assertEquals($allOperationsCached, $allOperations); + } + + public function testUpdateOperation(): void { + $adminScope = $this->buildScope(); + $userScope = $this->buildScope('jackie'); + $entity = File::class; + + $cache = $this->createMock(ICache::class); + $cache->expects($this->exactly(4)) + ->method('remove') + ->with('events'); + $this->cacheFactory->method('createDistributed') + ->willReturn($cache); + + $expectedCalls = [ + [IManager::SCOPE_ADMIN], + [IManager::SCOPE_USER], + ]; + $i = 0; + $operationMock = $this->createMock(IOperation::class); + $operationMock->expects($this->any()) + ->method('isAvailableForScope') + ->willReturnCallback(function () use (&$expectedCalls, &$i): bool { + $this->assertEquals($expectedCalls[$i], func_get_args()); + $i++; + return true; + }); + $this->container->expects($this->any()) ->method('query') - ->willReturnCallback(function ($class) { + ->willReturnCallback(function ($class) use ($operationMock) { if (substr($class, -2) === 'Op') { - return $this->createMock(IOperation::class); + return $operationMock; } elseif ($class === File::class) { return $this->getMockBuilder(File::class) ->setConstructorArgs([ $this->l, $this->createMock(IURLGenerator::class), $this->createMock(IRootFolder::class), - $this->createMock(ILogger::class), - $this->createMock(\OCP\Share\IManager::class), $this->createMock(IUserSession::class), $this->createMock(ISystemTagManager::class), $this->createMock(IUserManager::class), + $this->createMock(UserMountCache::class), + $this->createMock(IMountManager::class), ]) - ->setMethodsExcept(['getEvents']) + ->onlyMethods($this->filterClassMethods(File::class, ['getEvents'])) ->getMock(); } return $this->createMock(ICheck::class); @@ -349,11 +442,17 @@ class ManagerTest extends TestCase { } } - public function testDeleteOperation() { + public function testDeleteOperation(): void { $adminScope = $this->buildScope(); $userScope = $this->buildScope('jackie'); $entity = File::class; + $cache = $this->createMock(ICache::class); + $cache->expects($this->exactly(4)) + ->method('remove') + ->with('events'); + $this->cacheFactory->method('createDistributed')->willReturn($cache); + $opId1 = $this->invokePrivate( $this->manager, 'insertOperation', @@ -393,7 +492,7 @@ class ManagerTest extends TestCase { } } - public function testGetEntitiesListBuildInOnly() { + public function testGetEntitiesListBuildInOnly(): void { $fileEntityMock = $this->createMock(File::class); $this->container->expects($this->once()) @@ -407,7 +506,7 @@ class ManagerTest extends TestCase { $this->assertInstanceOf(IEntity::class, $entities[0]); } - public function testGetEntitiesList() { + public function testGetEntitiesList(): void { $fileEntityMock = $this->createMock(File::class); $this->container->expects($this->once()) @@ -418,10 +517,9 @@ class ManagerTest extends TestCase { /** @var MockObject|IEntity $extraEntity */ $extraEntity = $this->createMock(IEntity::class); - $this->legacyDispatcher->expects($this->once()) - ->method('dispatch') - ->with('OCP\WorkflowEngine::registerEntities', $this->anything()) - ->willReturnCallback(function () use ($extraEntity) { + $this->dispatcher->expects($this->once()) + ->method('dispatchTyped') + ->willReturnCallback(function (RegisterEntitiesEvent $e) use ($extraEntity): void { $this->manager->registerEntity($extraEntity); }); @@ -442,7 +540,7 @@ class ManagerTest extends TestCase { $this->assertSame(1, $entityTypeCounts[1]); } - public function testValidateOperationOK() { + public function testValidateOperationOK(): void { $check = [ 'class' => ICheck::class, 'operator' => 'is', @@ -453,6 +551,16 @@ class ManagerTest extends TestCase { $entityMock = $this->createMock(IEntity::class); $eventEntityMock = $this->createMock(IEntityEvent::class); $checkMock = $this->createMock(ICheck::class); + $scopeMock = $this->createMock(ScopeContext::class); + + $scopeMock->expects($this->any()) + ->method('getScope') + ->willReturn(IManager::SCOPE_ADMIN); + + $operationMock->expects($this->once()) + ->method('isAvailableForScope') + ->with(IManager::SCOPE_ADMIN) + ->willReturn(true); $operationMock->expects($this->once()) ->method('validateOperation') @@ -489,10 +597,10 @@ class ManagerTest extends TestCase { } }); - $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', IEntity::class, ['MyEvent']); + $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', $scopeMock, IEntity::class, ['MyEvent']); } - public function testValidateOperationCheckInputLengthError() { + public function testValidateOperationCheckInputLengthError(): void { $check = [ 'class' => ICheck::class, 'operator' => 'is', @@ -503,6 +611,16 @@ class ManagerTest extends TestCase { $entityMock = $this->createMock(IEntity::class); $eventEntityMock = $this->createMock(IEntityEvent::class); $checkMock = $this->createMock(ICheck::class); + $scopeMock = $this->createMock(ScopeContext::class); + + $scopeMock->expects($this->any()) + ->method('getScope') + ->willReturn(IManager::SCOPE_ADMIN); + + $operationMock->expects($this->once()) + ->method('isAvailableForScope') + ->with(IManager::SCOPE_ADMIN) + ->willReturn(true); $operationMock->expects($this->once()) ->method('validateOperation') @@ -540,13 +658,13 @@ class ManagerTest extends TestCase { }); try { - $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', IEntity::class, ['MyEvent']); + $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', $scopeMock, IEntity::class, ['MyEvent']); } catch (\UnexpectedValueException $e) { $this->assertSame('The provided check value is too long', $e->getMessage()); } } - public function testValidateOperationDataLengthError() { + public function testValidateOperationDataLengthError(): void { $check = [ 'class' => ICheck::class, 'operator' => 'is', @@ -558,6 +676,16 @@ class ManagerTest extends TestCase { $entityMock = $this->createMock(IEntity::class); $eventEntityMock = $this->createMock(IEntityEvent::class); $checkMock = $this->createMock(ICheck::class); + $scopeMock = $this->createMock(ScopeContext::class); + + $scopeMock->expects($this->any()) + ->method('getScope') + ->willReturn(IManager::SCOPE_ADMIN); + + $operationMock->expects($this->once()) + ->method('isAvailableForScope') + ->with(IManager::SCOPE_ADMIN) + ->willReturn(true); $operationMock->expects($this->never()) ->method('validateOperation'); @@ -594,9 +722,73 @@ class ManagerTest extends TestCase { }); try { - $this->manager->validateOperation(IOperation::class, 'test', [$check], $operationData, IEntity::class, ['MyEvent']); + $this->manager->validateOperation(IOperation::class, 'test', [$check], $operationData, $scopeMock, IEntity::class, ['MyEvent']); } catch (\UnexpectedValueException $e) { $this->assertSame('The provided operation data is too long', $e->getMessage()); } } + + public function testValidateOperationScopeNotAvailable(): void { + $check = [ + 'class' => ICheck::class, + 'operator' => 'is', + 'value' => 'barfoo', + ]; + $operationData = str_pad('', IManager::MAX_OPERATION_VALUE_BYTES + 1, 'FooBar'); + + $operationMock = $this->createMock(IOperation::class); + $entityMock = $this->createMock(IEntity::class); + $eventEntityMock = $this->createMock(IEntityEvent::class); + $checkMock = $this->createMock(ICheck::class); + $scopeMock = $this->createMock(ScopeContext::class); + + $scopeMock->expects($this->any()) + ->method('getScope') + ->willReturn(IManager::SCOPE_ADMIN); + + $operationMock->expects($this->once()) + ->method('isAvailableForScope') + ->with(IManager::SCOPE_ADMIN) + ->willReturn(false); + + $operationMock->expects($this->never()) + ->method('validateOperation'); + + $entityMock->expects($this->any()) + ->method('getEvents') + ->willReturn([$eventEntityMock]); + + $eventEntityMock->expects($this->any()) + ->method('getEventName') + ->willReturn('MyEvent'); + + $checkMock->expects($this->any()) + ->method('supportedEntities') + ->willReturn([IEntity::class]); + $checkMock->expects($this->never()) + ->method('validateCheck'); + + $this->container->expects($this->any()) + ->method('query') + ->willReturnCallback(function ($className) use ($operationMock, $entityMock, $eventEntityMock, $checkMock) { + switch ($className) { + case IOperation::class: + return $operationMock; + case IEntity::class: + return $entityMock; + case IEntityEvent::class: + return $eventEntityMock; + case ICheck::class: + return $checkMock; + default: + return $this->createMock($className); + } + }); + + try { + $this->manager->validateOperation(IOperation::class, 'test', [$check], $operationData, $scopeMock, IEntity::class, ['MyEvent']); + } catch (\UnexpectedValueException $e) { + $this->assertSame('Operation OCP\WorkflowEngine\IOperation is invalid', $e->getMessage()); + } + } } |