diff options
Diffstat (limited to 'apps/workflowengine/tests')
6 files changed, 1094 insertions, 231 deletions
diff --git a/apps/workflowengine/tests/Check/AbstractStringCheckTest.php b/apps/workflowengine/tests/Check/AbstractStringCheckTest.php index b7051870115..26d4ccb8553 100644 --- a/apps/workflowengine/tests/Check/AbstractStringCheckTest.php +++ b/apps/workflowengine/tests/Check/AbstractStringCheckTest.php @@ -1,46 +1,33 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @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\Check; +use OCA\WorkflowEngine\Check\AbstractStringCheck; use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; class AbstractStringCheckTest extends \Test\TestCase { - - protected function getCheckMock() { + protected function getCheckMock(): AbstractStringCheck|MockObject { $l = $this->getMockBuilder(IL10N::class) ->disableOriginalConstructor() ->getMock(); $l->expects($this->any()) ->method('t') - ->willReturnCallback(function($string, $args) { - return sprintf($string, $args); - }); + ->willReturnCallback(function ($string, $args) { + return sprintf($string, $args); + }); - $check = $this->getMockBuilder('OCA\WorkflowEngine\Check\AbstractStringCheck') + $check = $this->getMockBuilder(AbstractStringCheck::class) ->setConstructorArgs([ $l, ]) - ->setMethods([ - 'setPath', + ->onlyMethods([ 'executeCheck', 'getActualValue', ]) @@ -49,7 +36,7 @@ class AbstractStringCheckTest extends \Test\TestCase { return $check; } - public function dataExecuteStringCheck() { + public static function dataExecuteStringCheck(): array { return [ ['is', 'same', 'same', true], ['is', 'different', 'not the same', false], @@ -63,21 +50,15 @@ class AbstractStringCheckTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataExecuteStringCheck - * @param string $operation - * @param string $checkValue - * @param string $actualValue - * @param bool $expected - */ - public function testExecuteStringCheck($operation, $checkValue, $actualValue, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteStringCheck')] + public function testExecuteStringCheck(string $operation, string $checkValue, string $actualValue, bool $expected): void { $check = $this->getCheckMock(); - /** @var \OCA\WorkflowEngine\Check\AbstractStringCheck $check */ + /** @var AbstractStringCheck $check */ $this->assertEquals($expected, $this->invokePrivate($check, 'executeStringCheck', [$operation, $checkValue, $actualValue])); } - public function dataValidateCheck() { + public static function dataValidateCheck(): array { return [ ['is', '/Invalid(Regex/'], ['!is', '/Invalid(Regex/'], @@ -86,19 +67,17 @@ class AbstractStringCheckTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataValidateCheck - * @param string $operator - * @param string $value - */ - public function testValidateCheck($operator, $value) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateCheck')] + public function testValidateCheck(string $operator, string $value): void { $check = $this->getCheckMock(); - /** @var \OCA\WorkflowEngine\Check\AbstractStringCheck $check */ + /** @var AbstractStringCheck $check */ $check->validateCheck($operator, $value); + + $this->addToAssertionCount(1); } - public function dataValidateCheckInvalid() { + public static function dataValidateCheckInvalid(): array { return [ ['!!is', '', 1, 'The given operator is invalid'], ['less', '', 1, 'The given operator is invalid'], @@ -107,18 +86,12 @@ class AbstractStringCheckTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataValidateCheckInvalid - * @param $operator - * @param $value - * @param $exceptionCode - * @param $exceptionMessage - */ - public function testValidateCheckInvalid($operator, $value, $exceptionCode, $exceptionMessage) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateCheckInvalid')] + public function testValidateCheckInvalid(string $operator, string $value, int $exceptionCode, string $exceptionMessage): void { $check = $this->getCheckMock(); try { - /** @var \OCA\WorkflowEngine\Check\AbstractStringCheck $check */ + /** @var AbstractStringCheck $check */ $check->validateCheck($operator, $value); } catch (\UnexpectedValueException $e) { $this->assertEquals($exceptionCode, $e->getCode()); @@ -126,21 +99,15 @@ class AbstractStringCheckTest extends \Test\TestCase { } } - public function dataMatch() { + public static function dataMatch(): array { return [ ['/valid/', 'valid', [], true], ['/valid/', 'valid', [md5('/valid/') => [md5('valid') => false]], false], // Cache hit ]; } - /** - * @dataProvider dataMatch - * @param string $pattern - * @param string $subject - * @param array[] $matches - * @param bool $expected - */ - public function testMatch($pattern, $subject, $matches, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataMatch')] + public function testMatch(string $pattern, string $subject, array $matches, bool $expected): void { $check = $this->getCheckMock(); $this->invokePrivate($check, 'matches', [$matches]); diff --git a/apps/workflowengine/tests/Check/FileMimeTypeTest.php b/apps/workflowengine/tests/Check/FileMimeTypeTest.php new file mode 100644 index 00000000000..55aea3db172 --- /dev/null +++ b/apps/workflowengine/tests/Check/FileMimeTypeTest.php @@ -0,0 +1,177 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\WorkflowEngine\Tests\Check; + +use OC\Files\Storage\Temporary; +use OCA\WorkflowEngine\Check\FileMimeType; +use OCP\Files\IMimeTypeDetector; +use OCP\IL10N; +use OCP\IRequest; +use Test\TestCase; + +class TemporaryNoLocal extends Temporary { + public function instanceOfStorage(string $class): bool { + if ($class === '\OC\Files\Storage\Local') { + return false; + } else { + return parent::instanceOfStorage($class); + } + } +} + +/** + * @group DB + */ +class FileMimeTypeTest extends TestCase { + /** @var IL10N */ + private $l10n; + /** @var IRequest */ + private $request; + /** @var IMimeTypeDetector */ + private $mimeDetector; + + private $extensions = [ + '.txt' => 'text/plain-path-detected', + ]; + + private $content = [ + 'text-content' => 'text/plain-content-detected', + ]; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->request = $this->createMock(IRequest::class); + $this->mimeDetector = $this->createMock(IMimeTypeDetector::class); + $this->mimeDetector->method('detectPath') + ->willReturnCallback(function ($path) { + foreach ($this->extensions as $extension => $mime) { + if (str_contains($path, $extension)) { + return $mime; + } + } + return 'application/octet-stream'; + }); + $this->mimeDetector->method('detectContent') + ->willReturnCallback(function ($path) { + $body = file_get_contents($path); + foreach ($this->content as $match => $mime) { + if (str_contains($body, $match)) { + return $mime; + } + } + return 'application/octet-stream'; + }); + } + + public function testUseCachedMimetype(): void { + $storage = new Temporary([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'asd'); + $storage->getScanner()->scan(''); + + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain')); + } + + public function testNonCachedNotExists(): void { + $storage = new Temporary([]); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-path-detected')); + } + + public function testNonCachedLocal(): void { + $storage = new Temporary([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + } + + public function testNonCachedNotLocal(): void { + $storage = new TemporaryNoLocal([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + } + + public function testFallback(): void { + $storage = new Temporary([]); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'unknown'); + + $this->assertTrue($check->executeCheck('is', 'application/octet-stream')); + } + + public function testFromCacheCached(): void { + $storage = new Temporary([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + $storage->getScanner()->scan(''); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain')); + + $storage->getCache()->clear(); + + $this->assertTrue($check->executeCheck('is', 'text/plain')); + + $newCheck = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $newCheck->setFileInfo($storage, 'foo/bar.txt'); + $this->assertTrue($newCheck->executeCheck('is', 'text/plain-content-detected')); + } + + public function testExistsCached(): void { + $storage = new TemporaryNoLocal([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + $storage->unlink('foo/bar.txt'); + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + + $newCheck = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $newCheck->setFileInfo($storage, 'foo/bar.txt'); + $this->assertTrue($newCheck->executeCheck('is', 'text/plain-path-detected')); + } + + public function testNonExistsNotCached(): void { + $storage = new TemporaryNoLocal([]); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-path-detected')); + + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + } +} diff --git a/apps/workflowengine/tests/Check/RequestRemoteAddressTest.php b/apps/workflowengine/tests/Check/RequestRemoteAddressTest.php index 9e313122a1f..c0e56daefa8 100644 --- a/apps/workflowengine/tests/Check/RequestRemoteAddressTest.php +++ b/apps/workflowengine/tests/Check/RequestRemoteAddressTest.php @@ -1,41 +1,24 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @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\Check; +use OCA\WorkflowEngine\Check\RequestRemoteAddress; use OCP\IL10N; use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; class RequestRemoteAddressTest extends \Test\TestCase { - /** @var \OCP\IRequest|\PHPUnit_Framework_MockObject_MockObject */ - protected $request; + protected IRequest&MockObject $request; - /** - * @return \OCP\IL10N|\PHPUnit_Framework_MockObject_MockObject - */ - protected function getL10NMock() { - $l = $this->getMockBuilder(IL10N::class) - ->disableOriginalConstructor() - ->getMock(); + protected function getL10NMock(): IL10N&MockObject { + $l = $this->createMock(IL10N::class); $l->expects($this->any()) ->method('t') ->willReturnCallback(function ($string, $args) { @@ -44,14 +27,13 @@ class RequestRemoteAddressTest extends \Test\TestCase { return $l; } - protected function setUp() { + protected function setUp(): void { parent::setUp(); - $this->request = $this->getMockBuilder(IRequest::class) - ->getMock(); + $this->request = $this->createMock(IRequest::class); } - public function dataExecuteCheckIPv4() { + public static function dataExecuteCheckIPv4(): array { return [ ['127.0.0.1/32', '127.0.0.1', true], ['127.0.0.1/32', '127.0.0.0', false], @@ -62,14 +44,9 @@ class RequestRemoteAddressTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataExecuteCheckIPv4 - * @param string $value - * @param string $ip - * @param bool $expected - */ - public function testExecuteCheckMatchesIPv4($value, $ip, $expected) { - $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteCheckIPv4')] + public function testExecuteCheckMatchesIPv4(string $value, string $ip, bool $expected): void { + $check = new RequestRemoteAddress($this->getL10NMock(), $this->request); $this->request->expects($this->once()) ->method('getRemoteAddress') @@ -78,14 +55,9 @@ class RequestRemoteAddressTest extends \Test\TestCase { $this->assertEquals($expected, $check->executeCheck('matchesIPv4', $value)); } - /** - * @dataProvider dataExecuteCheckIPv4 - * @param string $value - * @param string $ip - * @param bool $expected - */ - public function testExecuteCheckNotMatchesIPv4($value, $ip, $expected) { - $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteCheckIPv4')] + public function testExecuteCheckNotMatchesIPv4(string $value, string $ip, bool $expected): void { + $check = new RequestRemoteAddress($this->getL10NMock(), $this->request); $this->request->expects($this->once()) ->method('getRemoteAddress') @@ -94,7 +66,7 @@ class RequestRemoteAddressTest extends \Test\TestCase { $this->assertEquals(!$expected, $check->executeCheck('!matchesIPv4', $value)); } - public function dataExecuteCheckIPv6() { + public static function dataExecuteCheckIPv6(): array { return [ ['::1/128', '::1', true], ['::2/128', '::3', false], @@ -106,14 +78,9 @@ class RequestRemoteAddressTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataExecuteCheckIPv6 - * @param string $value - * @param string $ip - * @param bool $expected - */ - public function testExecuteCheckMatchesIPv6($value, $ip, $expected) { - $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteCheckIPv6')] + public function testExecuteCheckMatchesIPv6(string $value, string $ip, bool $expected): void { + $check = new RequestRemoteAddress($this->getL10NMock(), $this->request); $this->request->expects($this->once()) ->method('getRemoteAddress') @@ -122,14 +89,9 @@ class RequestRemoteAddressTest extends \Test\TestCase { $this->assertEquals($expected, $check->executeCheck('matchesIPv6', $value)); } - /** - * @dataProvider dataExecuteCheckIPv6 - * @param string $value - * @param string $ip - * @param bool $expected - */ - public function testExecuteCheckNotMatchesIPv6($value, $ip, $expected) { - $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteCheckIPv6')] + public function testExecuteCheckNotMatchesIPv6(string $value, string $ip, bool $expected): void { + $check = new RequestRemoteAddress($this->getL10NMock(), $this->request); $this->request->expects($this->once()) ->method('getRemoteAddress') diff --git a/apps/workflowengine/tests/Check/RequestTimeTest.php b/apps/workflowengine/tests/Check/RequestTimeTest.php index 7249f5109f2..a8439b8b9f4 100644 --- a/apps/workflowengine/tests/Check/RequestTimeTest.php +++ b/apps/workflowengine/tests/Check/RequestTimeTest.php @@ -1,40 +1,23 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @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\Check; +use OCA\WorkflowEngine\Check\RequestTime; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; class RequestTimeTest extends \Test\TestCase { + protected ITimeFactory&MockObject $timeFactory; - /** @var \OCP\AppFramework\Utility\ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $timeFactory; - - /** - * @return \OCP\IL10N|\PHPUnit_Framework_MockObject_MockObject - */ - protected function getL10NMock() { - $l = $this->getMockBuilder(IL10N::class) - ->disableOriginalConstructor() - ->getMock(); + protected function getL10NMock(): IL10N&MockObject { + $l = $this->createMock(IL10N::class); $l->expects($this->any()) ->method('t') ->willReturnCallback(function ($string, $args) { @@ -43,14 +26,13 @@ class RequestTimeTest extends \Test\TestCase { return $l; } - protected function setUp() { + protected function setUp(): void { parent::setUp(); - $this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory') - ->getMock(); + $this->timeFactory = $this->createMock(ITimeFactory::class); } - public function dataExecuteCheck() { + public static function dataExecuteCheck(): array { return [ [json_encode(['08:00 Europe/Berlin', '17:00 Europe/Berlin']), 1467870105, false], // 2016-07-07T07:41:45+02:00 [json_encode(['08:00 Europe/Berlin', '17:00 Europe/Berlin']), 1467873705, true], // 2016-07-07T08:41:45+02:00 @@ -81,14 +63,9 @@ class RequestTimeTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataExecuteCheck - * @param string $value - * @param int $timestamp - * @param bool $expected - */ - public function testExecuteCheckIn($value, $timestamp, $expected) { - $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteCheck')] + public function testExecuteCheckIn(string $value, int $timestamp, bool $expected): void { + $check = new RequestTime($this->getL10NMock(), $this->timeFactory); $this->timeFactory->expects($this->once()) ->method('getTime') @@ -97,14 +74,9 @@ class RequestTimeTest extends \Test\TestCase { $this->assertEquals($expected, $check->executeCheck('in', $value)); } - /** - * @dataProvider dataExecuteCheck - * @param string $value - * @param int $timestamp - * @param bool $expected - */ - public function testExecuteCheckNotIn($value, $timestamp, $expected) { - $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteCheck')] + public function testExecuteCheckNotIn(string $value, int $timestamp, bool $expected): void { + $check = new RequestTime($this->getL10NMock(), $this->timeFactory); $this->timeFactory->expects($this->once()) ->method('getTime') @@ -113,7 +85,7 @@ class RequestTimeTest extends \Test\TestCase { $this->assertEquals(!$expected, $check->executeCheck('!in', $value)); } - public function dataValidateCheck() { + public static function dataValidateCheck(): array { return [ ['in', '["08:00 Europe/Berlin","17:00 Europe/Berlin"]'], ['!in', '["08:00 Europe/Berlin","17:00 America/North_Dakota/Beulah"]'], @@ -121,17 +93,14 @@ class RequestTimeTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataValidateCheck - * @param string $operator - * @param string $value - */ - public function testValidateCheck($operator, $value) { - $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateCheck')] + public function testValidateCheck(string $operator, string $value): void { + $check = new RequestTime($this->getL10NMock(), $this->timeFactory); $check->validateCheck($operator, $value); + $this->addToAssertionCount(1); } - public function dataValidateCheckInvalid() { + public static function dataValidateCheckInvalid(): array { return [ ['!!in', '["08:00 Europe/Berlin","17:00 Europe/Berlin"]', 1, 'The given operator is invalid'], ['in', '["28:00 Europe/Berlin","17:00 Europe/Berlin"]', 2, 'The given time span is invalid'], @@ -143,15 +112,9 @@ class RequestTimeTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataValidateCheckInvalid - * @param string $operator - * @param string $value - * @param int $exceptionCode - * @param string $exceptionMessage - */ - public function testValidateCheckInvalid($operator, $value, $exceptionCode, $exceptionMessage) { - $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateCheckInvalid')] + public function testValidateCheckInvalid(string $operator, string $value, int $exceptionCode, string $exceptionMessage): void { + $check = new RequestTime($this->getL10NMock(), $this->timeFactory); try { $check->validateCheck($operator, $value); diff --git a/apps/workflowengine/tests/Check/RequestUserAgentTest.php b/apps/workflowengine/tests/Check/RequestUserAgentTest.php new file mode 100644 index 00000000000..09eaea6555b --- /dev/null +++ b/apps/workflowengine/tests/Check/RequestUserAgentTest.php @@ -0,0 +1,94 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\WorkflowEngine\Tests\Check; + +use OCA\WorkflowEngine\Check\AbstractStringCheck; +use OCA\WorkflowEngine\Check\RequestUserAgent; +use OCP\IL10N; +use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class RequestUserAgentTest extends TestCase { + protected IRequest&MockObject $request; + protected RequestUserAgent $check; + + protected function setUp(): void { + parent::setUp(); + + $this->request = $this->createMock(IRequest::class); + /** @var IL10N&MockObject $l */ + $l = $this->createMock(IL10N::class); + $l->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($string, $args) { + return sprintf($string, $args); + }); + + $this->check = new RequestUserAgent($l, $this->request); + } + + public static function dataExecuteCheck(): array { + return [ + ['is', 'android', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', true], + ['is', 'android', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', false], + ['is', 'android', 'Mozilla/5.0 (Linux) mirall/2.2.0', false], + ['is', 'android', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', false], + ['is', 'android', 'Filelink for *cloud/2.2.0', false], + ['!is', 'android', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', false], + ['!is', 'android', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', true], + ['!is', 'android', 'Mozilla/5.0 (Linux) mirall/2.2.0', true], + ['!is', 'android', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', true], + ['!is', 'android', 'Filelink for *cloud/2.2.0', true], + + ['is', 'ios', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', false], + ['is', 'ios', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', true], + ['is', 'ios', 'Mozilla/5.0 (Linux) mirall/2.2.0', false], + ['is', 'ios', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', false], + ['is', 'ios', 'Filelink for *cloud/2.2.0', false], + ['!is', 'ios', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', true], + ['!is', 'ios', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', false], + ['!is', 'ios', 'Mozilla/5.0 (Linux) mirall/2.2.0', true], + ['!is', 'ios', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', true], + ['!is', 'ios', 'Filelink for *cloud/2.2.0', true], + + ['is', 'desktop', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', false], + ['is', 'desktop', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', false], + ['is', 'desktop', 'Mozilla/5.0 (Linux) mirall/2.2.0', true], + ['is', 'desktop', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', false], + ['is', 'desktop', 'Filelink for *cloud/2.2.0', false], + ['!is', 'desktop', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', true], + ['!is', 'desktop', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', true], + ['!is', 'desktop', 'Mozilla/5.0 (Linux) mirall/2.2.0', false], + ['!is', 'desktop', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', true], + ['!is', 'desktop', 'Filelink for *cloud/2.2.0', true], + + ['is', 'mail', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', false], + ['is', 'mail', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', false], + ['is', 'mail', 'Mozilla/5.0 (Linux) mirall/2.2.0', false], + ['is', 'mail', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', true], + ['is', 'mail', 'Filelink for *cloud/2.2.0', true], + ['!is', 'mail', 'Mozilla/5.0 (Android) Nextcloud-android/2.2.0', true], + ['!is', 'mail', 'Mozilla/5.0 (iOS) Nextcloud-iOS/2.2.0', true], + ['!is', 'mail', 'Mozilla/5.0 (Linux) mirall/2.2.0', true], + ['!is', 'mail', 'Mozilla/5.0 (Windows) Nextcloud-Outlook v2.2.0', false], + ['!is', 'mail', 'Filelink for *cloud/2.2.0', false], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteCheck')] + public function testExecuteCheck(string $operation, string $checkValue, string $actualValue, bool $expected): void { + $this->request->expects($this->once()) + ->method('getHeader') + ->willReturn($actualValue); + + /** @var AbstractStringCheck $check */ + $this->assertEquals($expected, $this->check->executeCheck($operation, $checkValue)); + } +} diff --git a/apps/workflowengine/tests/ManagerTest.php b/apps/workflowengine/tests/ManagerTest.php index 9136bf0e7cd..56e45936b82 100644 --- a/apps/workflowengine/tests/ManagerTest.php +++ b/apps/workflowengine/tests/ManagerTest.php @@ -1,31 +1,40 @@ <?php + /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @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\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 Psr\Log\LoggerInterface; use Test\TestCase; /** @@ -35,43 +44,88 @@ use Test\TestCase; * @group DB */ class ManagerTest extends TestCase { - /** @var Manager */ protected $manager; - /** @var IDBConnection */ + /** @var MockObject|IDBConnection */ protected $db; + /** @var \PHPUnit\Framework\MockObject\MockObject|LoggerInterface */ + protected $logger; + /** @var MockObject|IServerContainer */ + protected $container; + /** @var MockObject|IUserSession */ + protected $session; + /** @var MockObject|L10N */ + protected $l; + /** @var MockObject|IEventDispatcher */ + protected $dispatcher; + /** @var MockObject|IConfig */ + protected $config; + /** @var MockObject|ICacheFactory */ + protected $cacheFactory; - protected function setUp() { + protected function setUp(): void { parent::setUp(); - $this->db = \OC::$server->getDatabaseConnection(); - $container = $this->createMock(IServerContainer::class); - $l = $this->createMock(IL10N::class); - $l->method('t') - ->will($this->returnCallback(function($text, $parameters = []) { + $this->db = Server::get(IDBConnection::class); + $this->container = $this->createMock(IServerContainer::class); + /** @var IL10N|MockObject $l */ + $this->l = $this->createMock(IL10N::class); + $this->l->method('t') + ->willReturnCallback(function ($text, $parameters = []) { return vsprintf($text, $parameters); - })); + }); + + $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(), - $container, - $l + Server::get(IDBConnection::class), + $this->container, + $this->l, + $this->logger, + $this->session, + $this->dispatcher, + $this->config, + $this->cacheFactory ); - $this->clearChecks(); + $this->clearTables(); } - protected function tearDown() { - $this->clearChecks(); + protected function tearDown(): void { + $this->clearTables(); parent::tearDown(); } - public function clearChecks() { + /** + * @return MockObject|ScopeContext + */ + protected function buildScope(?string $scopeId = null): MockObject { + $scopeContext = $this->createMock(ScopeContext::class); + $scopeContext->expects($this->any()) + ->method('getScope') + ->willReturn($scopeId ? IManager::SCOPE_USER : IManager::SCOPE_ADMIN); + $scopeContext->expects($this->any()) + ->method('getScopeId') + ->willReturn($scopeId ?? ''); + $scopeContext->expects($this->any()) + ->method('getHash') + ->willReturn(md5($scopeId ?? '')); + + return $scopeContext; + } + + public function clearTables() { $query = $this->db->getQueryBuilder(); - $query->delete('flow_checks') - ->execute(); + foreach (['flow_checks', 'flow_operations', 'flow_operations_scope'] as $table) { + $query->delete($table) + ->execute(); + } } - 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]); @@ -91,4 +145,650 @@ class ManagerTest extends TestCase { $this->assertArrayNotHasKey($check1, $data); $this->assertArrayHasKey($check2, $data); } + + public function testScope(): void { + $adminScope = $this->buildScope(); + $userScope = $this->buildScope('jackie'); + $entity = File::class; + + $opId1 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestOp', 'Test01', [11, 22], 'foo', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId1, $adminScope]); + + $opId2 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestOp', 'Test02', [33, 22], 'bar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId2, $userScope]); + $opId3 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestOp', 'Test03', [11, 44], 'foobar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId3, $userScope]); + + $this->assertTrue($this->invokePrivate($this->manager, 'canModify', [$opId1, $adminScope])); + $this->assertFalse($this->invokePrivate($this->manager, 'canModify', [$opId2, $adminScope])); + $this->assertFalse($this->invokePrivate($this->manager, 'canModify', [$opId3, $adminScope])); + + $this->assertFalse($this->invokePrivate($this->manager, 'canModify', [$opId1, $userScope])); + $this->assertTrue($this->invokePrivate($this->manager, 'canModify', [$opId2, $userScope])); + $this->assertTrue($this->invokePrivate($this->manager, 'canModify', [$opId3, $userScope])); + } + + 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', + ['OCA\WFE\TestAdminOp', 'Test01', [11, 22], 'foo', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId1, $adminScope]); + + $opId2 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestUserOp', 'Test02', [33, 22], 'bar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId2, $userScope]); + $opId3 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestUserOp', 'Test03', [11, 44], 'foobar', $entity, []] + ); + $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); + + $this->assertSame(1, count($adminOps)); + $this->assertTrue(array_key_exists('OCA\WFE\TestAdminOp', $adminOps)); + $this->assertFalse(array_key_exists('OCA\WFE\TestUserOp', $adminOps)); + + $this->assertSame(1, count($userOps)); + $this->assertFalse(array_key_exists('OCA\WFE\TestAdminOp', $userOps)); + $this->assertTrue(array_key_exists('OCA\WFE\TestUserOp', $userOps)); + $this->assertSame(2, count($userOps['OCA\WFE\TestUserOp'])); + } + + public function testGetOperations(): void { + $adminScope = $this->buildScope(); + $userScope = $this->buildScope('jackie'); + $entity = File::class; + + $opId1 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestOp', 'Test01', [11, 22], 'foo', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId1, $adminScope]); + $opId4 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\OtherTestOp', 'Test04', [5], 'foo', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId4, $adminScope]); + + $opId2 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestOp', 'Test02', [33, 22], 'bar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId2, $userScope]); + $opId3 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestOp', 'Test03', [11, 44], 'foobar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId3, $userScope]); + $opId5 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\OtherTestOp', 'Test05', [5], 'foobar', $entity, []] + ); + $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): void { + $this->assertTrue($op['class'] === 'OCA\WFE\TestOp'); + }); + + $this->assertSame(2, count($userOps)); + array_walk($userOps, function ($op): void { + $this->assertTrue($op['class'] === 'OCA\WFE\TestOp'); + }); + } + + 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) use ($operationMock) { + if (substr($class, -2) === 'Op') { + return $operationMock; + } elseif ($class === File::class) { + return $this->getMockBuilder(File::class) + ->setConstructorArgs([ + $this->l, + $this->createMock(IURLGenerator::class), + $this->createMock(IRootFolder::class), + $this->createMock(IUserSession::class), + $this->createMock(ISystemTagManager::class), + $this->createMock(IUserManager::class), + $this->createMock(UserMountCache::class), + $this->createMock(IMountManager::class), + ]) + ->onlyMethods($this->filterClassMethods(File::class, ['getEvents'])) + ->getMock(); + } + return $this->createMock(ICheck::class); + }); + + $opId1 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestAdminOp', 'Test01', [11, 22], 'foo', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId1, $adminScope]); + + $opId2 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestUserOp', 'Test02', [33, 22], 'bar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId2, $userScope]); + + $check1 = ['class' => 'OCA\WFE\C22', 'operator' => 'eq', 'value' => 'asdf']; + $check2 = ['class' => 'OCA\WFE\C33', 'operator' => 'eq', 'value' => 23456]; + + /** @noinspection PhpUnhandledExceptionInspection */ + $op = $this->manager->updateOperation($opId1, 'Test01a', [$check1, $check2], 'foohur', $adminScope, $entity, ['\OCP\Files::postDelete']); + $this->assertSame('Test01a', $op['name']); + $this->assertSame('foohur', $op['operation']); + + /** @noinspection PhpUnhandledExceptionInspection */ + $op = $this->manager->updateOperation($opId2, 'Test02a', [$check1], 'barfoo', $userScope, $entity, ['\OCP\Files::postDelete']); + $this->assertSame('Test02a', $op['name']); + $this->assertSame('barfoo', $op['operation']); + + foreach ([[$adminScope, $opId2], [$userScope, $opId1]] as $run) { + try { + /** @noinspection PhpUnhandledExceptionInspection */ + $this->manager->updateOperation($run[1], 'Evil', [$check2], 'hackx0r', $run[0], $entity, []); + $this->assertTrue(false, 'DomainException not thrown'); + } catch (\DomainException $e) { + $this->assertTrue(true); + } + } + } + + 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', + ['OCA\WFE\TestAdminOp', 'Test01', [11, 22], 'foo', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId1, $adminScope]); + + $opId2 = $this->invokePrivate( + $this->manager, + 'insertOperation', + ['OCA\WFE\TestUserOp', 'Test02', [33, 22], 'bar', $entity, []] + ); + $this->invokePrivate($this->manager, 'addScope', [$opId2, $userScope]); + + foreach ([[$adminScope, $opId2], [$userScope, $opId1]] as $run) { + try { + /** @noinspection PhpUnhandledExceptionInspection */ + $this->manager->deleteOperation($run[1], $run[0]); + $this->assertTrue(false, 'DomainException not thrown'); + } catch (\Exception $e) { + $this->assertInstanceOf(\DomainException::class, $e); + } + } + + /** @noinspection PhpUnhandledExceptionInspection */ + $this->manager->deleteOperation($opId1, $adminScope); + /** @noinspection PhpUnhandledExceptionInspection */ + $this->manager->deleteOperation($opId2, $userScope); + + foreach ([$opId1, $opId2] as $opId) { + try { + $this->invokePrivate($this->manager, 'getOperation', [$opId]); + $this->assertTrue(false, 'UnexpectedValueException not thrown'); + } catch (\Exception $e) { + $this->assertInstanceOf(\UnexpectedValueException::class, $e); + } + } + } + + public function testGetEntitiesListBuildInOnly(): void { + $fileEntityMock = $this->createMock(File::class); + + $this->container->expects($this->once()) + ->method('query') + ->with(File::class) + ->willReturn($fileEntityMock); + + $entities = $this->manager->getEntitiesList(); + + $this->assertCount(1, $entities); + $this->assertInstanceOf(IEntity::class, $entities[0]); + } + + public function testGetEntitiesList(): void { + $fileEntityMock = $this->createMock(File::class); + + $this->container->expects($this->once()) + ->method('query') + ->with(File::class) + ->willReturn($fileEntityMock); + + /** @var MockObject|IEntity $extraEntity */ + $extraEntity = $this->createMock(IEntity::class); + + $this->dispatcher->expects($this->once()) + ->method('dispatchTyped') + ->willReturnCallback(function (RegisterEntitiesEvent $e) use ($extraEntity): void { + $this->manager->registerEntity($extraEntity); + }); + + $entities = $this->manager->getEntitiesList(); + + $this->assertCount(2, $entities); + + $entityTypeCounts = array_reduce($entities, function (array $carry, IEntity $entity) { + if ($entity instanceof File) { + $carry[0]++; + } elseif ($entity instanceof IEntity) { + $carry[1]++; + } + return $carry; + }, [0, 0]); + + $this->assertSame(1, $entityTypeCounts[0]); + $this->assertSame(1, $entityTypeCounts[1]); + } + + public function testValidateOperationOK(): void { + $check = [ + 'class' => ICheck::class, + 'operator' => 'is', + 'value' => 'barfoo', + ]; + + $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(true); + + $operationMock->expects($this->once()) + ->method('validateOperation') + ->with('test', [$check], 'operationData'); + + $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->atLeastOnce()) + ->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); + } + }); + + $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', $scopeMock, IEntity::class, ['MyEvent']); + } + + public function testValidateOperationCheckInputLengthError(): void { + $check = [ + 'class' => ICheck::class, + 'operator' => 'is', + 'value' => str_pad('', IManager::MAX_CHECK_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(true); + + $operationMock->expects($this->once()) + ->method('validateOperation') + ->with('test', [$check], 'operationData'); + + $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('The provided check value is too long', $e->getMessage()); + } + } + + public function testValidateOperationDataLengthError(): 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(true); + + $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('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()); + } + } } |