diff options
Diffstat (limited to 'tests/lib/Command')
-rw-r--r-- | tests/lib/Command/AsyncBusTestCase.php | 179 | ||||
-rw-r--r-- | tests/lib/Command/BackgroundModeTest.php | 60 | ||||
-rw-r--r-- | tests/lib/Command/CronBusTest.php | 40 | ||||
-rw-r--r-- | tests/lib/Command/Integrity/SignAppTest.php | 272 | ||||
-rw-r--r-- | tests/lib/Command/Integrity/SignCoreTest.php | 212 |
5 files changed, 763 insertions, 0 deletions
diff --git a/tests/lib/Command/AsyncBusTestCase.php b/tests/lib/Command/AsyncBusTestCase.php new file mode 100644 index 00000000000..bb47de30b11 --- /dev/null +++ b/tests/lib/Command/AsyncBusTestCase.php @@ -0,0 +1,179 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Command; + +use OC\Command\FileAccess; +use OCP\Command\IBus; +use OCP\Command\ICommand; +use Test\TestCase; + +class SimpleCommand implements ICommand { + public function handle() { + AsyncBusTestCase::$lastCommand = 'SimpleCommand'; + } +} + +class StateFullCommand implements ICommand { + public function __construct( + private $state, + ) { + } + + public function handle() { + AsyncBusTestCase::$lastCommand = $this->state; + } +} + +class FilesystemCommand implements ICommand { + use FileAccess; + + public function handle() { + AsyncBusTestCase::$lastCommand = 'FileAccess'; + } +} + +function basicFunction() { + AsyncBusTestCase::$lastCommand = 'function'; +} + +// clean class to prevent phpunit putting closure in $this +class ThisClosureTest { + private function privateMethod() { + AsyncBusTestCase::$lastCommand = 'closure-this'; + } + + public function test(IBus $bus) { + $bus->push(function (): void { + $this->privateMethod(); + }); + } +} + +abstract class AsyncBusTestCase extends TestCase { + /** + * Basic way to check output from a command + * + * @var string + */ + public static $lastCommand; + + /** + * @var IBus + */ + private $bus; + + public static function DummyCommand() { + self::$lastCommand = 'static'; + } + + /** + * @return IBus + */ + protected function getBus() { + if (!$this->bus instanceof IBus) { + $this->bus = $this->createBus(); + } + return $this->bus; + } + + /** + * @return IBus + */ + abstract protected function createBus(); + + protected function setUp(): void { + self::$lastCommand = ''; + } + + public function testSimpleCommand(): void { + $command = new SimpleCommand(); + $this->getBus()->push($command); + $this->runJobs(); + $this->assertEquals('SimpleCommand', self::$lastCommand); + } + + public function testStateFullCommand(): void { + $command = new StateFullCommand('foo'); + $this->getBus()->push($command); + $this->runJobs(); + $this->assertEquals('foo', self::$lastCommand); + } + + public function testStaticCallable(): void { + $this->getBus()->push(['\Test\Command\AsyncBusTestCase', 'DummyCommand']); + $this->runJobs(); + $this->assertEquals('static', self::$lastCommand); + } + + public function testMemberCallable(): void { + $command = new StateFullCommand('bar'); + $this->getBus()->push([$command, 'handle']); + $this->runJobs(); + $this->assertEquals('bar', self::$lastCommand); + } + + public function testFunctionCallable(): void { + $this->getBus()->push('\Test\Command\BasicFunction'); + $this->runJobs(); + $this->assertEquals('function', self::$lastCommand); + } + + public function testClosure(): void { + $this->getBus()->push(function (): void { + AsyncBusTestCase::$lastCommand = 'closure'; + }); + $this->runJobs(); + $this->assertEquals('closure', self::$lastCommand); + } + + public function testClosureSelf(): void { + $this->getBus()->push(function (): void { + AsyncBusTestCase::$lastCommand = 'closure-self'; + }); + $this->runJobs(); + $this->assertEquals('closure-self', self::$lastCommand); + } + + + public function testClosureThis(): void { + // clean class to prevent phpunit putting closure in $this + $test = new ThisClosureTest(); + $test->test($this->getBus()); + $this->runJobs(); + $this->assertEquals('closure-this', self::$lastCommand); + } + + public function testClosureBind(): void { + $state = 'bar'; + $this->getBus()->push(function () use ($state): void { + AsyncBusTestCase::$lastCommand = 'closure-' . $state; + }); + $this->runJobs(); + $this->assertEquals('closure-bar', self::$lastCommand); + } + + public function testFileFileAccessCommand(): void { + $this->getBus()->push(new FilesystemCommand()); + $this->assertEquals('', self::$lastCommand); + $this->runJobs(); + $this->assertEquals('FileAccess', self::$lastCommand); + } + + public function testFileFileAccessCommandSync(): void { + $this->getBus()->requireSync('\OC\Command\FileAccess'); + $this->getBus()->push(new FilesystemCommand()); + $this->assertEquals('FileAccess', self::$lastCommand); + self::$lastCommand = ''; + $this->runJobs(); + $this->assertEquals('', self::$lastCommand); + } + + + abstract protected function runJobs(); +} diff --git a/tests/lib/Command/BackgroundModeTest.php b/tests/lib/Command/BackgroundModeTest.php new file mode 100644 index 00000000000..ab036ef87ee --- /dev/null +++ b/tests/lib/Command/BackgroundModeTest.php @@ -0,0 +1,60 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2015 Christian Kampka <christian@kampka.net> + * SPDX-License-Identifier: MIT + */ +namespace Test\Command; + +use OC\Core\Command\Background\Mode; +use OCP\IAppConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +/** + * @group DB + */ +class BackgroundModeTest extends TestCase { + private IAppConfig $appConfig; + + private Mode $command; + + public function setUp(): void { + $this->appConfig = $this->createMock(IAppConfig::class); + + $inputDefinition = new InputDefinition([ + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + ]); + + $this->command = new Mode($this->appConfig); + $this->command->setDefinition($inputDefinition); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataModeCommand')] + public function testModeCommand(string $mode): void { + $this->appConfig->expects($this->once()) + ->method('setValueString') + ->with('core', 'backgroundjobs_mode', $mode); + + $commandTester = new CommandTester($this->command); + $commandTester->execute(['command' => 'background:' . $mode]); + + $commandTester->assertCommandIsSuccessful(); + + $output = $commandTester->getDisplay(); + $this->assertStringContainsString($mode, $output); + } + + public static function dataModeCommand(): array { + return [ + 'ajax' => ['ajax'], + 'cron' => ['cron'], + 'webcron' => ['webcron'], + ]; + } +} diff --git a/tests/lib/Command/CronBusTest.php b/tests/lib/Command/CronBusTest.php new file mode 100644 index 00000000000..c86cdcb1da0 --- /dev/null +++ b/tests/lib/Command/CronBusTest.php @@ -0,0 +1,40 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Command; + +use OC\Command\CronBus; +use OCP\BackgroundJob\IJobList; +use Test\BackgroundJob\DummyJobList; + +/** + * @group DB + */ +class CronBusTest extends AsyncBusTestCase { + /** + * @var IJobList + */ + private $jobList; + + + protected function setUp(): void { + parent::setUp(); + + $this->jobList = new DummyJobList(); + } + + protected function createBus() { + return new CronBus($this->jobList); + } + + protected function runJobs() { + $jobs = $this->jobList->getAll(); + foreach ($jobs as $job) { + $job->start($this->jobList); + } + } +} diff --git a/tests/lib/Command/Integrity/SignAppTest.php b/tests/lib/Command/Integrity/SignAppTest.php new file mode 100644 index 00000000000..237afe3a5b5 --- /dev/null +++ b/tests/lib/Command/Integrity/SignAppTest.php @@ -0,0 +1,272 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\Command\Integrity; + +use OC\Core\Command\Integrity\SignApp; +use OC\IntegrityCheck\Checker; +use OC\IntegrityCheck\Helpers\FileAccessHelper; +use OCP\IURLGenerator; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class SignAppTest extends TestCase { + /** @var Checker|\PHPUnit\Framework\MockObject\MockObject */ + private $checker; + /** @var SignApp */ + private $signApp; + /** @var FileAccessHelper|\PHPUnit\Framework\MockObject\MockObject */ + private $fileAccessHelper; + /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */ + private $urlGenerator; + + protected function setUp(): void { + parent::setUp(); + $this->checker = $this->createMock(Checker::class); + $this->fileAccessHelper = $this->createMock(FileAccessHelper::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->signApp = new SignApp( + $this->checker, + $this->fileAccessHelper, + $this->urlGenerator + ); + } + + public function testExecuteWithMissingPath(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['path', null], + ['privateKey', 'PrivateKey'], + ['certificate', 'Certificate'], + ]); + + $calls = [ + 'This command requires the --path, --privateKey and --certificate.', + '*', + '*', + ]; + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message) use (&$calls): void { + $expected = array_shift($calls); + if ($expected === '*') { + $this->assertNotEmpty($message); + } else { + $this->assertEquals($expected, $message); + } + }); + + $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithMissingPrivateKey(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['path', 'AppId'], + ['privateKey', null], + ['certificate', 'Certificate'], + ]); + + $calls = [ + 'This command requires the --path, --privateKey and --certificate.', + '*', + '*', + ]; + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message) use (&$calls): void { + $expected = array_shift($calls); + if ($expected === '*') { + $this->assertNotEmpty($message); + } else { + $this->assertEquals($expected, $message); + } + }); + + $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithMissingCertificate(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['path', 'AppId'], + ['privateKey', 'PrivateKey'], + ['certificate', null], + ]); + + $calls = [ + 'This command requires the --path, --privateKey and --certificate.', + '*', + '*', + ]; + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message) use (&$calls): void { + $expected = array_shift($calls); + if ($expected === '*') { + $this->assertNotEmpty($message); + } else { + $this->assertEquals($expected, $message); + } + }); + + $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithNotExistingPrivateKey(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['path', 'AppId'], + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ]); + + $this->fileAccessHelper + ->expects($this->any()) + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', false], + ]); + + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Private key "privateKey" does not exists.', $message); + }); + + $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithNotExistingCertificate(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['path', 'AppId'], + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ]); + + $this->fileAccessHelper + ->expects($this->any()) + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')], + ['certificate', false], + ]); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Certificate "certificate" does not exists.', $message); + }); + + $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithException(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['path', 'AppId'], + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ]); + + $this->fileAccessHelper + ->expects($this->any()) + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')], + ['certificate', \OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'], + ]); + + $this->checker + ->expects($this->once()) + ->method('writeAppSignature') + ->willThrowException(new \Exception('My error message')); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Error: My error message', $message); + }); + + $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecute(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['path', 'AppId'], + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ]); + + $this->fileAccessHelper + ->expects($this->any()) + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')], + ['certificate', \OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'], + ]); + + $this->checker + ->expects($this->once()) + ->method('writeAppSignature'); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Successfully signed "AppId"', $message); + }); + + $this->assertSame(0, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface])); + } +} diff --git a/tests/lib/Command/Integrity/SignCoreTest.php b/tests/lib/Command/Integrity/SignCoreTest.php new file mode 100644 index 00000000000..843084eebd9 --- /dev/null +++ b/tests/lib/Command/Integrity/SignCoreTest.php @@ -0,0 +1,212 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\Command\Integrity; + +use OC\Core\Command\Integrity\SignCore; +use OC\IntegrityCheck\Checker; +use OC\IntegrityCheck\Helpers\FileAccessHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class SignCoreTest extends TestCase { + /** @var Checker|\PHPUnit\Framework\MockObject\MockObject */ + private $checker; + /** @var FileAccessHelper|\PHPUnit\Framework\MockObject\MockObject */ + private $fileAccessHelper; + /** @var SignCore */ + private $signCore; + + protected function setUp(): void { + parent::setUp(); + $this->checker = $this->createMock(Checker::class); + $this->fileAccessHelper = $this->createMock(FileAccessHelper::class); + $this->signCore = new SignCore( + $this->checker, + $this->fileAccessHelper + ); + } + + public function testExecuteWithMissingPrivateKey(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['privateKey', null], + ['certificate', 'certificate'], + ['path', 'certificate'], + ]); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('--privateKey, --certificate and --path are required.', $message); + }); + + $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithMissingCertificate(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['privateKey', 'privateKey'], + ['certificate', null], + ['path', 'certificate'], + ]); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('--privateKey, --certificate and --path are required.', $message); + }); + + $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithNotExistingPrivateKey(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ['path', 'certificate'], + ]); + + $this->fileAccessHelper + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', false], + ]); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Private key "privateKey" does not exists.', $message); + }); + + $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithNotExistingCertificate(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ['path', 'certificate'], + ]); + + $this->fileAccessHelper + ->expects($this->any()) + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')], + ['certificate', false], + ]); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Certificate "certificate" does not exists.', $message); + }); + + $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecuteWithException(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ['path', 'certificate'], + ]); + + $this->fileAccessHelper + ->expects($this->any()) + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')], + ['certificate', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt')], + ]); + + $this->checker + ->expects($this->once()) + ->method('writeCoreSignature') + ->willThrowException(new \Exception('My exception message')); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Error: My exception message', $message); + }); + + $this->assertEquals(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface])); + } + + public function testExecute(): void { + $inputInterface = $this->createMock(InputInterface::class); + $outputInterface = $this->createMock(OutputInterface::class); + + $inputInterface + ->expects($this->exactly(3)) + ->method('getOption') + ->willReturnMap([ + ['privateKey', 'privateKey'], + ['certificate', 'certificate'], + ['path', 'certificate'], + ]); + + $this->fileAccessHelper + ->expects($this->any()) + ->method('file_get_contents') + ->willReturnMap([ + ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')], + ['certificate', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt')], + ]); + + $this->checker + ->expects($this->once()) + ->method('writeCoreSignature'); + + $outputInterface + ->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function (string $message): void { + $this->assertEquals('Successfully signed "core"', $message); + }); + + $this->assertEquals(0, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface])); + } +} |