diff options
author | Daniel Rudolf <github.com@daniel-rudolf.de> | 2020-06-02 22:46:01 +0300 |
---|---|---|
committer | Daniel Rudolf <github.com@daniel-rudolf.de> | 2020-06-02 22:46:01 +0300 |
commit | cebcfefac84715dfe4a88264b0622df5c939cfec (patch) | |
tree | ab830a33ec1961195c61d1f1fe1b0a74611e1ff8 | |
parent | 08b7bf8b2f91b5b53e01f9e59db9a5c6b69d48a1 (diff) | |
parent | 0b13f861f82dd17d2b789f20884b5a4a67c01a6d (diff) |
Merge branch 'feature/cmd-room-followup' into feature/cmd-room-followup-backport19
-rw-r--r-- | lib/Command/Room/Add.php | 48 | ||||
-rw-r--r-- | lib/Command/Room/Create.php | 45 | ||||
-rw-r--r-- | lib/Command/Room/Delete.php | 20 | ||||
-rw-r--r-- | lib/Command/Room/Demote.php | 34 | ||||
-rw-r--r-- | lib/Command/Room/Promote.php | 34 | ||||
-rw-r--r-- | lib/Command/Room/Remove.php | 40 | ||||
-rw-r--r-- | lib/Command/Room/TRoomCommand.php | 134 | ||||
-rw-r--r-- | lib/Command/Room/Update.php | 42 | ||||
-rw-r--r-- | lib/Manager.php | 37 | ||||
-rw-r--r-- | lib/Room.php | 32 | ||||
-rw-r--r-- | tests/php/Command/Room/AddTest.php | 327 | ||||
-rw-r--r-- | tests/php/Command/Room/CreateTest.php | 372 | ||||
-rw-r--r-- | tests/php/Command/Room/DeleteTest.php | 149 | ||||
-rw-r--r-- | tests/php/Command/Room/DemoteTest.php | 247 | ||||
-rw-r--r-- | tests/php/Command/Room/PromoteTest.php | 231 | ||||
-rw-r--r-- | tests/php/Command/Room/RemoveTest.php | 214 | ||||
-rw-r--r-- | tests/php/Command/Room/RoomMockContainer.php | 221 | ||||
-rw-r--r-- | tests/php/Command/Room/TRoomCommandTest.php | 125 | ||||
-rw-r--r-- | tests/php/Command/Room/UpdateTest.php | 352 |
19 files changed, 2524 insertions, 180 deletions
diff --git a/lib/Command/Room/Add.php b/lib/Command/Room/Add.php index c48755aa4..d5d0b7338 100644 --- a/lib/Command/Room/Add.php +++ b/lib/Command/Room/Add.php @@ -25,12 +25,11 @@ declare(strict_types=1); namespace OCA\Talk\Command\Room; -use Exception; +use InvalidArgumentException; use OC\Core\Command\Base; use OCA\Talk\Exceptions\RoomNotFoundException; -use OCA\Talk\Manager; use OCA\Talk\Room; -use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -39,19 +38,6 @@ use Symfony\Component\Console\Output\OutputInterface; class Add extends Base { use TRoomCommand; - /** @var IUserManager */ - public $userManager; - - /** @var Manager */ - public $manager; - - public function __construct(IUserManager $userManager, Manager $manager) { - parent::__construct(); - - $this->userManager = $userManager; - $this->manager = $manager; - } - protected function configure(): void { $this ->setName('talk:room:add') @@ -70,11 +56,6 @@ class Add extends Base { null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Invites all members of the given groups to the room' - )->addOption( - 'circle', - null, - InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Invites all members of the given circles to the room' ); } @@ -82,7 +63,6 @@ class Add extends Base { $token = $input->getArgument('token'); $users = $input->getOption('user'); $groups = $input->getOption('group'); - $circles = $input->getOption('circle'); try { $room = $this->manager->getRoomByToken($token); @@ -99,8 +79,7 @@ class Add extends Base { try { $this->addRoomParticipants($room, $users); $this->addRoomParticipantsByGroup($room, $groups); - $this->addRoomParticipantsByCircle($room, $circles); - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { $output->writeln(sprintf('<error>%s</error>', $e->getMessage())); return 1; } @@ -108,4 +87,25 @@ class Add extends Base { $output->writeln('<info>Users successfully added to room.</info>'); return 0; } + + public function completeOptionValues($optionName, CompletionContext $context) { + switch ($optionName) { + case 'user': + return $this->completeUserValues($context); + + case 'group': + return $this->completeGroupValues($context); + } + + return parent::completeOptionValues($optionName, $context); + } + + public function completeArgumentValues($argumentName, CompletionContext $context) { + switch ($argumentName) { + case 'token': + return $this->completeTokenValues($context); + } + + return parent::completeArgumentValues($argumentName, $context); + } } diff --git a/lib/Command/Room/Create.php b/lib/Command/Room/Create.php index bc708d838..f96ef7dd5 100644 --- a/lib/Command/Room/Create.php +++ b/lib/Command/Room/Create.php @@ -25,10 +25,9 @@ declare(strict_types=1); namespace OCA\Talk\Command\Room; -use Exception; +use InvalidArgumentException; use OC\Core\Command\Base; -use OCA\Talk\Manager; -use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -37,19 +36,6 @@ use Symfony\Component\Console\Output\OutputInterface; class Create extends Base { use TRoomCommand; - /** @var IUserManager */ - public $userManager; - - /** @var Manager */ - public $manager; - - public function __construct(IUserManager $userManager, Manager $manager) { - parent::__construct(); - - $this->userManager = $userManager; - $this->manager = $manager; - } - protected function configure(): void { $this ->setName('talk:room:create') @@ -69,11 +55,6 @@ class Create extends Base { InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Invites all members of the given group to the room to create' )->addOption( - 'circle', - null, - InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Invites all members of the given circle to the room to create' - )->addOption( 'public', null, InputOption::VALUE_NONE, @@ -105,7 +86,6 @@ class Create extends Base { $name = $input->getArgument('name'); $users = $input->getOption('user'); $groups = $input->getOption('group'); - $circles = $input->getOption('circle'); $public = $input->getOption('public'); $readonly = $input->getOption('readonly'); $password = $input->getOption('password'); @@ -114,7 +94,7 @@ class Create extends Base { $name = trim($name); if (!$this->validateRoomName($name)) { - $output->writeln("<error>Invalid room name.</error>"); + $output->writeln('<error>Invalid room name.</error>'); return 1; } @@ -129,13 +109,12 @@ class Create extends Base { $this->addRoomParticipants($room, $users); $this->addRoomParticipantsByGroup($room, $groups); - $this->addRoomParticipantsByCircle($room, $circles); $this->addRoomModerators($room, $moderators); if ($owner !== null) { $this->setRoomOwner($room, $owner); } - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { $room->deleteRoom(); $output->writeln(sprintf('<error>%s</error>', $e->getMessage())); @@ -145,4 +124,20 @@ class Create extends Base { $output->writeln('<info>Room successfully created.</info>'); return 0; } + + public function completeOptionValues($optionName, CompletionContext $context) { + switch ($optionName) { + case 'user': + return $this->completeUserValues($context); + + case 'group': + return $this->completeGroupValues($context); + + case 'owner': + case 'moderator': + return $this->completeParticipantValues($context); + } + + return parent::completeOptionValues($optionName, $context); + } } diff --git a/lib/Command/Room/Delete.php b/lib/Command/Room/Delete.php index 986ca67a4..34b767cc3 100644 --- a/lib/Command/Room/Delete.php +++ b/lib/Command/Room/Delete.php @@ -27,21 +27,14 @@ namespace OCA\Talk\Command\Room; use OC\Core\Command\Base; use OCA\Talk\Exceptions\RoomNotFoundException; -use OCA\Talk\Manager; use OCA\Talk\Room; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class Delete extends Base { - /** @var Manager */ - public $manager; - - public function __construct(Manager $manager) { - parent::__construct(); - - $this->manager = $manager; - } + use TRoomCommand; protected function configure(): void { $this @@ -74,4 +67,13 @@ class Delete extends Base { $output->writeln('<info>Room successfully deleted.</info>'); return 0; } + + public function completeArgumentValues($argumentName, CompletionContext $context) { + switch ($argumentName) { + case 'token': + return $this->completeTokenValues($context); + } + + return parent::completeArgumentValues($argumentName, $context); + } } diff --git a/lib/Command/Room/Demote.php b/lib/Command/Room/Demote.php index 9bdb607db..e510f311b 100644 --- a/lib/Command/Room/Demote.php +++ b/lib/Command/Room/Demote.php @@ -25,12 +25,11 @@ declare(strict_types=1); namespace OCA\Talk\Command\Room; -use Exception; +use InvalidArgumentException; use OC\Core\Command\Base; use OCA\Talk\Exceptions\RoomNotFoundException; -use OCA\Talk\Manager; use OCA\Talk\Room; -use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -38,19 +37,6 @@ use Symfony\Component\Console\Output\OutputInterface; class Demote extends Base { use TRoomCommand; - /** @var IUserManager */ - public $userManager; - - /** @var Manager */ - public $manager; - - public function __construct(IUserManager $userManager, Manager $manager) { - parent::__construct(); - - $this->userManager = $userManager; - $this->manager = $manager; - } - protected function configure(): void { $this ->setName('talk:room:demote') @@ -84,12 +70,24 @@ class Demote extends Base { try { $this->removeRoomModerators($room, $users); - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { $output->writeln(sprintf('<error>%s</error>', $e->getMessage())); return 1; } - $output->writeln('<info>Users successfully remove from room.</info>'); + $output->writeln('<info>Participants successfully demoted to regular users.</info>'); return 0; } + + public function completeArgumentValues($argumentName, CompletionContext $context) { + switch ($argumentName) { + case 'token': + return $this->completeTokenValues($context); + + case 'participant': + return $this->completeParticipantValues($context); + } + + return parent::completeArgumentValues($argumentName, $context); + } } diff --git a/lib/Command/Room/Promote.php b/lib/Command/Room/Promote.php index 44b378c74..613d8d0b7 100644 --- a/lib/Command/Room/Promote.php +++ b/lib/Command/Room/Promote.php @@ -25,12 +25,11 @@ declare(strict_types=1); namespace OCA\Talk\Command\Room; -use Exception; +use InvalidArgumentException; use OC\Core\Command\Base; use OCA\Talk\Exceptions\RoomNotFoundException; -use OCA\Talk\Manager; use OCA\Talk\Room; -use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -38,19 +37,6 @@ use Symfony\Component\Console\Output\OutputInterface; class Promote extends Base { use TRoomCommand; - /** @var IUserManager */ - public $userManager; - - /** @var Manager */ - public $manager; - - public function __construct(IUserManager $userManager, Manager $manager) { - parent::__construct(); - - $this->userManager = $userManager; - $this->manager = $manager; - } - protected function configure(): void { $this ->setName('talk:room:promote') @@ -84,12 +70,24 @@ class Promote extends Base { try { $this->addRoomModerators($room, $users); - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { $output->writeln(sprintf('<error>%s</error>', $e->getMessage())); return 1; } - $output->writeln('<info>Users successfully added to room.</info>'); + $output->writeln('<info>Participants successfully promoted to moderators.</info>'); return 0; } + + public function completeArgumentValues($argumentName, CompletionContext $context) { + switch ($argumentName) { + case 'token': + return $this->completeTokenValues($context); + + case 'participant': + return $this->completeParticipantValues($context); + } + + return parent::completeArgumentValues($argumentName, $context); + } } diff --git a/lib/Command/Room/Remove.php b/lib/Command/Room/Remove.php index aeb7313a6..b3cc192d2 100644 --- a/lib/Command/Room/Remove.php +++ b/lib/Command/Room/Remove.php @@ -25,12 +25,11 @@ declare(strict_types=1); namespace OCA\Talk\Command\Room; -use Exception; +use InvalidArgumentException; use OC\Core\Command\Base; use OCA\Talk\Exceptions\RoomNotFoundException; -use OCA\Talk\Manager; use OCA\Talk\Room; -use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -38,19 +37,6 @@ use Symfony\Component\Console\Output\OutputInterface; class Remove extends Base { use TRoomCommand; - /** @var IUserManager */ - public $userManager; - - /** @var Manager */ - public $manager; - - public function __construct(IUserManager $userManager, Manager $manager) { - parent::__construct(); - - $this->userManager = $userManager; - $this->manager = $manager; - } - protected function configure(): void { $this ->setName('talk:room:remove') @@ -60,15 +46,15 @@ class Remove extends Base { InputArgument::REQUIRED, 'Token of the room to remove users from' )->addArgument( - 'user', + 'participant', InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'Removes the given users from the room' + 'Removes the given participants from the room' ); } protected function execute(InputInterface $input, OutputInterface $output): ?int { $token = $input->getArgument('token'); - $users = $input->getArgument('user'); + $users = $input->getArgument('participant'); try { $room = $this->manager->getRoomByToken($token); @@ -84,12 +70,24 @@ class Remove extends Base { try { $this->removeRoomParticipants($room, $users); - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { $output->writeln(sprintf('<error>%s</error>', $e->getMessage())); return 1; } - $output->writeln('<info>Users successfully remove from room.</info>'); + $output->writeln('<info>Users successfully removed from room.</info>'); return 0; } + + public function completeArgumentValues($argumentName, CompletionContext $context) { + switch ($argumentName) { + case 'token': + return $this->completeTokenValues($context); + + case 'participant': + return $this->completeParticipantValues($context); + } + + return parent::completeArgumentValues($argumentName, $context); + } } diff --git a/lib/Command/Room/TRoomCommand.php b/lib/Command/Room/TRoomCommand.php index 1baf70793..cb8bb2daa 100644 --- a/lib/Command/Room/TRoomCommand.php +++ b/lib/Command/Room/TRoomCommand.php @@ -26,14 +26,44 @@ declare(strict_types=1); namespace OCA\Talk\Command\Room; use InvalidArgumentException; -use OCA\Circles\Api\v1\Circles; -use OCA\Circles\Model\Member; use OCA\Talk\Exceptions\ParticipantNotFoundException; +use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Manager; use OCA\Talk\Participant; use OCA\Talk\Room; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IUser; +use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; trait TRoomCommand { + /** @var Manager */ + protected $manager; + + /** @var IUserManager */ + protected $userManager; + + /** @var IGroupManager */ + protected $groupManager; + + /** + * TRoomCommand constructor. + * + * @param Manager $manager + * @param IUserManager $userManager + * @param IGroupManager $groupManager + */ + public function __construct(Manager $manager, IUserManager $userManager, IGroupManager $groupManager) { + parent::__construct(); + + $this->manager = $manager; + $this->userManager = $userManager; + $this->groupManager = $groupManager; + } + /** * @param Room $room * @param string $name @@ -136,6 +166,8 @@ trait TRoomCommand { throw new InvalidArgumentException(sprintf("User '%s' is no participant.", $userId)); } + $this->unsetRoomOwner($room); + $room->setParticipantType($participant, Participant::OWNER); } @@ -163,11 +195,9 @@ trait TRoomCommand { return; } - $groupManager = \OC::$server->getGroupManager(); - $users = []; foreach ($groupIds as $groupId) { - $group = $groupManager->get($groupId); + $group = $this->groupManager->get($groupId); if ($group === null) { throw new InvalidArgumentException(sprintf("Group '%s' not found.", $groupId)); } @@ -184,43 +214,6 @@ trait TRoomCommand { /** * @param Room $room - * @param string[] $circleIds - * - * @throws InvalidArgumentException - */ - protected function addRoomParticipantsByCircle(Room $room, array $circleIds): void { - if (!$circleIds) { - return; - } - - if (!\OC::$server->getAppManager()->isEnabledForUser('circles')) { - throw new InvalidArgumentException("App 'circles' is not enabled."); - } - - $users = []; - foreach ($circleIds as $circleId) { - try { - $circle = Circles::detailsCircle($circleId); - } catch (\Exception $e) { - throw new InvalidArgumentException(sprintf("Circle '%s' not found.", $circleId)); - } - - $circleUsers = array_filter($circle->getMembers(), function (Member $member) { - if (($member->getType() !== Member::TYPE_USER) || ($member->getUserId() === '')) { - return false; - } - - return in_array($member->getStatus(), [Member::STATUS_INVITED, Member::STATUS_MEMBER], true); - }); - - $users = array_merge($users, $circleUsers); - } - - $this->addRoomParticipants($room, $users); - } - - /** - * @param Room $room * @param string[] $userIds * * @throws InvalidArgumentException @@ -230,11 +223,9 @@ trait TRoomCommand { return; } - $userManager = \OC::$server->getUserManager(); - $participants = []; foreach ($userIds as $userId) { - $user = $userManager->get($userId); + $user = $this->userManager->get($userId); if ($user === null) { throw new InvalidArgumentException(sprintf("User '%s' not found.", $userId)); } @@ -268,8 +259,6 @@ trait TRoomCommand { * @throws InvalidArgumentException */ protected function removeRoomParticipants(Room $room, array $userIds): void { - $userManager = \OC::$server->getUserManager(); - $users = []; foreach ($userIds as $userId) { try { @@ -278,7 +267,7 @@ trait TRoomCommand { throw new InvalidArgumentException(sprintf("User '%s' is no participant.", $userId)); } - $users[] = $userManager->get($userId); + $users[] = $this->userManager->get($userId); } foreach ($users as $user) { @@ -335,4 +324,53 @@ trait TRoomCommand { $room->setParticipantType($participant, Participant::USER); } } + + protected function completeTokenValues(CompletionContext $context): array { + return array_map(function (Room $room) { + return $room->getToken(); + }, $this->manager->searchRoomsByToken($context->getCurrentWord())); + } + + protected function completeUserValues(CompletionContext $context): array { + return array_map(function (IUser $user) { + return $user->getUID(); + }, $this->userManager->search($context->getCurrentWord())); + } + + protected function completeGroupValues(CompletionContext $context): array { + return array_map(function (IGroup $group) { + return $group->getGID(); + }, $this->groupManager->search($context->getCurrentWord())); + } + + protected function completeParticipantValues(CompletionContext $context): array { + $definition = new InputDefinition(); + + if ($this->getApplication() !== null) { + $definition->addArguments($this->getApplication()->getDefinition()->getArguments()); + $definition->addOptions($this->getApplication()->getDefinition()->getOptions()); + } + + $definition->addArguments($this->getDefinition()->getArguments()); + $definition->addOptions($this->getDefinition()->getOptions()); + + $input = new ArgvInput($context->getWords(), $definition); + if ($input->hasArgument('token')) { + $token = $input->getArgument('token'); + } elseif ($input->hasOption('token')) { + $token = $input->getOption('token'); + } else { + return []; + } + + try { + $room = $this->manager->getRoomByToken($token); + } catch (RoomNotFoundException $e) { + return []; + } + + return array_map(function (Participant $participant) { + return $participant->getUser(); + }, $room->searchParticipants($context->getCurrentWord())); + } } diff --git a/lib/Command/Room/Update.php b/lib/Command/Room/Update.php index a75dd6a7f..48bbf1748 100644 --- a/lib/Command/Room/Update.php +++ b/lib/Command/Room/Update.php @@ -25,12 +25,11 @@ declare(strict_types=1); namespace OCA\Talk\Command\Room; -use Exception; +use InvalidArgumentException; use OC\Core\Command\Base; use OCA\Talk\Exceptions\RoomNotFoundException; -use OCA\Talk\Manager; use OCA\Talk\Room; -use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -39,19 +38,6 @@ use Symfony\Component\Console\Output\OutputInterface; class Update extends Base { use TRoomCommand; - /** @var IUserManager */ - public $userManager; - - /** @var Manager */ - public $manager; - - public function __construct(IUserManager $userManager, Manager $manager) { - parent::__construct(); - - $this->userManager = $userManager; - $this->manager = $manager; - } - protected function configure(): void { $this ->setName('talk:room:update') @@ -142,7 +128,7 @@ class Update extends Base { $this->unsetRoomOwner($room); } } - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { $output->writeln(sprintf('<error>%s</error>', $e->getMessage())); return 1; } @@ -150,4 +136,26 @@ class Update extends Base { $output->writeln('<info>Room successfully updated.</info>'); return 0; } + + public function completeOptionValues($optionName, CompletionContext $context) { + switch ($optionName) { + case 'public': + case 'readonly': + return ['1', '0']; + + case 'owner': + return $this->completeParticipantValues($context); + } + + return parent::completeOptionValues($optionName, $context); + } + + public function completeArgumentValues($argumentName, CompletionContext $context) { + switch ($argumentName) { + case 'token': + return $this->completeTokenValues($context); + } + + return parent::completeArgumentValues($argumentName, $context); + } } diff --git a/lib/Manager.php b/lib/Manager.php index 43c4bf180..c96e5f2c8 100644 --- a/lib/Manager.php +++ b/lib/Manager.php @@ -225,6 +225,43 @@ class Manager { } /** + * @param string $searchToken + * @param int $limit + * @param int $offset + * @return Room[] + */ + public function searchRoomsByToken(string $searchToken = '', int $limit = null, int $offset = null): array { + $query = $this->db->getQueryBuilder(); + $query->select('*') + ->from('talk_rooms') + ->setMaxResults(1); + + if ($searchToken !== '') { + $query->where($query->expr()->iLike('token', $query->createNamedParameter( + '%' . $this->db->escapeLikeParameter($searchToken) . '%' + ))); + } + + $query->setMaxResults($limit) + ->setFirstResult($offset) + ->orderBy('token', 'ASC'); + $result = $query->execute(); + + $rooms = []; + while ($row = $result->fetch()) { + if ($row['token'] === null) { + // FIXME Temporary solution for the Talk6 release + continue; + } + + $rooms[] = $this->createRoomObject($row); + } + $result->closeCursor(); + + return $rooms; + } + + /** * @param string $participant * @param bool $includeLastMessage * @return Room[] diff --git a/lib/Room.php b/lib/Room.php index d087a2112..d3e9795af 100644 --- a/lib/Room.php +++ b/lib/Room.php @@ -1016,6 +1016,38 @@ class Room { } /** + * @param string $search + * @param int $limit + * @param int $offset + * @return Participant[] + */ + public function searchParticipants(string $search = '', int $limit = null, int $offset = null): array { + $query = $this->db->getQueryBuilder(); + $query->select('*') + ->from('talk_participants') + ->where($query->expr()->eq('room_id', $query->createNamedParameter($this->getId()))); + + if ($search !== '') { + $query->where($query->expr()->iLike('user_id', $query->createNamedParameter( + '%' . $this->db->escapeLikeParameter($search) . '%' + ))); + } + + $query->setMaxResults($limit) + ->setFirstResult($offset) + ->orderBy('user_id', 'ASC'); + $result = $query->execute(); + + $participants = []; + while ($row = $result->fetch()) { + $participants[] = $this->manager->createParticipantObject($this, $row); + } + $result->closeCursor(); + + return $participants; + } + + /** * @return Participant[] */ public function getParticipantsInCall(): array { diff --git a/tests/php/Command/Room/AddTest.php b/tests/php/Command/Room/AddTest.php new file mode 100644 index 000000000..f6d70e60c --- /dev/null +++ b/tests/php/Command/Room/AddTest.php @@ -0,0 +1,327 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCA\Talk\Command\Room\Add; +use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Manager; +use OCA\Talk\Participant; +use OCA\Talk\Room; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Exception\RuntimeException as ConsoleRuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class AddTest extends TestCase { + use TRoomCommandTest; + + /** @var Add */ + private $command; + + /** @var Manager|MockObject */ + private $manager; + + /** @var RoomMockContainer */ + private $roomMockContainer; + + public function setUp(): void { + parent::setUp(); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->manager = $this->createMock(Manager::class); + $this->command = new Add($this->manager, $this->userManager, $this->groupManager); + + $this->roomMockContainer = new RoomMockContainer($this); + + $this->createTestUserMocks(); + $this->createTestGroupMocks(); + } + + public function testMissingArguments(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "token").'); + + $tester = new CommandTester($this->command); + $tester->execute([]); + } + + /** + * @dataProvider validProvider + */ + public function testValid(array $input, array $expectedRoomData, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals("Users successfully added to room.\n", $tester->getDisplay()); + + $this->assertEquals($expectedRoomData, $this->roomMockContainer->getRoomData()); + } + + public function validProvider(): array { + return [ + [ + [ + 'token' => '__test-room', + ], + RoomMockContainer::prepareRoomData([]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--user' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--user' => ['user1', 'user2'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--user' => ['user2'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + '--user' => ['user3'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ['userId' => 'user3', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + '--group' => ['group1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--group' => ['group1', 'group2'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ['userId' => 'user3', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--group' => ['group1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + '--group' => ['group1', 'group2'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user4', 'participantType' => Participant::MODERATOR], + ['userId' => 'user2', 'participantType' => Participant::USER], + ['userId' => 'user3', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user4', 'participantType' => Participant::MODERATOR], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + '--group' => ['group1'], + '--user' => ['user4'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user4', 'participantType' => Participant::USER], + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--group' => ['group1'], + '--user' => ['user4'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::MODERATOR], + ['userId' => 'user3', 'participantType' => Participant::USER], + ['userId' => 'user4', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::MODERATOR], + ['userId' => 'user3', 'participantType' => Participant::USER], + ], + ]), + ], + ]; + } + + /** + * @dataProvider invalidProvider + */ + public function testInvalid(array $input, string $expectedOutput, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals($expectedOutput, $tester->getDisplay()); + } + + public function invalidProvider(): array { + return [ + [ + [ + 'token' => '__test-invalid', + ], + "Room not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + ], + "Room is no group call.\n", + RoomMockContainer::prepareRoomData([ + 'type' => Room::ONE_TO_ONE_CALL, + ]), + ], + [ + [ + 'token' => '__test-room', + '--user' => ['user1','invalid'] + ], + "User 'invalid' not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--group' => ['group1','invalid'] + ], + "Group 'invalid' not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + ]; + } +} diff --git a/tests/php/Command/Room/CreateTest.php b/tests/php/Command/Room/CreateTest.php new file mode 100644 index 000000000..37948bb06 --- /dev/null +++ b/tests/php/Command/Room/CreateTest.php @@ -0,0 +1,372 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCA\Talk\Command\Room\Create; +use OCA\Talk\Manager; +use OCA\Talk\Participant; +use OCA\Talk\Room; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Exception\RuntimeException as ConsoleRuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class CreateTest extends TestCase { + use TRoomCommandTest; + + /** @var Create */ + private $command; + + /** @var Manager|MockObject */ + private $manager; + + /** @var RoomMockContainer */ + private $roomMockContainer; + + public function setUp(): void { + parent::setUp(); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->manager = $this->createMock(Manager::class); + $this->command = new Create($this->manager, $this->userManager, $this->groupManager); + + $this->roomMockContainer = new RoomMockContainer($this); + + $this->createTestUserMocks(); + $this->createTestGroupMocks(); + } + + public function testMissingArguments(): void { + $this->manager->expects($this->never()) + ->method('createGroupRoom'); + + $this->manager->expects($this->never()) + ->method('createPublicRoom'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "name").'); + + $tester = new CommandTester($this->command); + $tester->execute([]); + } + + /** + * @dataProvider validProvider + */ + public function testValid(array $input, array $expectedRoomData): void { + $this->manager + ->method('createGroupRoom') + ->willReturnCallback(function (string $name = ''): Room { + return $this->roomMockContainer->create(['name' => $name, 'type' => Room::GROUP_CALL]); + }); + + $this->manager + ->method('createPublicRoom') + ->willReturnCallback(function (string $name = ''): Room { + return $this->roomMockContainer->create(['name' => $name, 'type' => Room::PUBLIC_CALL]); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals("Room successfully created.\n", $tester->getDisplay()); + + $this->assertEquals($expectedRoomData, $this->roomMockContainer->getRoomData()); + } + + public function validProvider(): array { + return [ + [ + [ + 'name' => 'PHPUnit Test Room', + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--public' => true, + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'type' => Room::PUBLIC_CALL, + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--readonly' => true, + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'readOnly' => Room::READ_ONLY, + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--public' => true, + '--password' => 'my-secret-password', + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'type' => Room::PUBLIC_CALL, + 'password' => 'my-secret-password', + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1', 'user2'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1', 'user2'], + '--moderator' => ['user2'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::MODERATOR], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1', 'user2', 'user3'], + '--moderator' => ['user2', 'user3'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::MODERATOR], + ['userId' => 'user3', 'participantType' => Participant::MODERATOR], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1', 'user2'], + '--owner' => 'user2', + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::OWNER], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--group' => ['group1'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--group' => ['group1', 'group2'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ['userId' => 'user3', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--group' => ['group1'], + '--user' => ['user4'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user4', 'participantType' => Participant::USER], + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--group' => ['group1'], + '--moderator' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::MODERATOR], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--group' => ['group1'], + '--owner' => 'user1', + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room', + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + ]; + } + + /** + * @dataProvider invalidProvider + */ + public function testInvalid(array $input, string $expectedOutput): void { + $this->manager + ->method('createGroupRoom') + ->willReturnCallback(function (string $name = ''): Room { + return $this->roomMockContainer->create(['name' => $name, 'type' => Room::GROUP_CALL]); + }); + + $this->manager + ->method('createPublicRoom') + ->willReturnCallback(function (string $name = ''): Room { + return $this->roomMockContainer->create(['name' => $name, 'type' => Room::PUBLIC_CALL]); + }); + + $this->roomMockContainer->registerCallback(function (object $room) { + /** @var Room|MockObject $room */ + $room->expects($this->once()) + ->method('deleteRoom'); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals($expectedOutput, $tester->getDisplay()); + } + + public function invalidProvider(): array { + return [ + [ + [ + 'name' => '', + ], + "Invalid room name.\n", + ], + [ + [ + 'name' => ' ', + ], + "Invalid room name.\n", + ], + [ + [ + 'name' => str_repeat('x', 256), + ], + "Invalid room name.\n", + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--password' => 'my-secret-password', + ], + "Unable to add password protection to private room.\n", + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1','invalid'], + ], + "User 'invalid' not found.\n", + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--group' => ['group1','invalid'], + ], + "Group 'invalid' not found.\n", + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1'], + '--moderator' => ['user2'], + ], + "User 'user2' is no participant.\n", + ], + [ + [ + 'name' => 'PHPUnit Test Room', + '--user' => ['user1'], + '--owner' => 'user2', + ], + "User 'user2' is no participant.\n", + ], + ]; + } +} diff --git a/tests/php/Command/Room/DeleteTest.php b/tests/php/Command/Room/DeleteTest.php new file mode 100644 index 000000000..39e9a001e --- /dev/null +++ b/tests/php/Command/Room/DeleteTest.php @@ -0,0 +1,149 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCA\Talk\Command\Room\Delete; +use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Manager; +use OCA\Talk\Room; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Exception\RuntimeException as ConsoleRuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class DeleteTest extends TestCase { + use TRoomCommandTest; + + /** @var Delete */ + private $command; + + /** @var Manager|MockObject */ + private $manager; + + /** @var RoomMockContainer */ + private $roomMockContainer; + + public function setUp(): void { + parent::setUp(); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->manager = $this->createMock(Manager::class); + $this->command = new Delete($this->manager, $this->userManager, $this->groupManager); + + $this->roomMockContainer = new RoomMockContainer($this); + } + + public function testMissingArguments(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "token").'); + + $tester = new CommandTester($this->command); + $tester->execute([]); + } + + /** + * @dataProvider validProvider + */ + public function testValid(array $input, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $this->roomMockContainer->registerCallback(function (object $room) { + /** @var Room|MockObject $room */ + $room->expects($this->once()) + ->method('deleteRoom'); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals("Room successfully deleted.\n", $tester->getDisplay()); + } + + public function validProvider(): array { + return [ + [ + [ + 'token' => '__test-room', + ], + RoomMockContainer::prepareRoomData([]), + ], + ]; + } + + /** + * @dataProvider invalidProvider + */ + public function testInvalid(array $input, string $expectedOutput, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals($expectedOutput, $tester->getDisplay()); + } + + public function invalidProvider(): array { + return [ + [ + [ + 'token' => '__test-invalid', + ], + "Room not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + ], + "Room is no group call.\n", + RoomMockContainer::prepareRoomData([ + 'type' => Room::ONE_TO_ONE_CALL, + ]), + ], + ]; + } +} diff --git a/tests/php/Command/Room/DemoteTest.php b/tests/php/Command/Room/DemoteTest.php new file mode 100644 index 000000000..1fb28ac8a --- /dev/null +++ b/tests/php/Command/Room/DemoteTest.php @@ -0,0 +1,247 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCA\Talk\Command\Room\Demote; +use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Manager; +use OCA\Talk\Participant; +use OCA\Talk\Room; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Exception\RuntimeException as ConsoleRuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class DemoteTest extends TestCase { + use TRoomCommandTest; + + /** @var Demote */ + private $command; + + /** @var Manager|MockObject */ + private $manager; + + /** @var RoomMockContainer */ + private $roomMockContainer; + + public function setUp(): void { + parent::setUp(); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->manager = $this->createMock(Manager::class); + $this->command = new Demote($this->manager, $this->userManager, $this->groupManager); + + $this->roomMockContainer = new RoomMockContainer($this); + + $this->createTestUserMocks(); + $this->createTestGroupMocks(); + } + + public function testMissingArguments(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "token, participant").'); + + $tester = new CommandTester($this->command); + $tester->execute([]); + } + + public function testMissingArgumentParticipant(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "participant").'); + + $tester = new CommandTester($this->command); + $tester->execute([ + 'token' => '__test-room', + ]); + } + + /** + * @dataProvider validProvider + */ + public function testValid(array $input, array $expectedRoomData, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals("Participants successfully demoted to regular users.\n", $tester->getDisplay()); + + $this->assertEquals($expectedRoomData, $this->roomMockContainer->getRoomData()); + } + + public function validProvider(): array { + return [ + [ + [ + 'token' => '__test-room', + 'participant' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::MODERATOR], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1', 'user2'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::MODERATOR], + ['userId' => 'user2', 'participantType' => Participant::MODERATOR], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ], + ]), + ], + ]; + } + + /** + * @dataProvider invalidProvider + */ + public function testInvalid(array $input, string $expectedOutput, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals($expectedOutput, $tester->getDisplay()); + } + + public function invalidProvider(): array { + return [ + [ + [ + 'token' => '__test-invalid', + 'participant' => [''], + ], + "Room not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => [''], + ], + "Room is no group call.\n", + RoomMockContainer::prepareRoomData([ + 'type' => Room::ONE_TO_ONE_CALL, + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1','invalid'] + ], + "User 'user1' is no participant.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1','invalid'] + ], + "User 'invalid' is no participant.\n", + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + ]; + } +} diff --git a/tests/php/Command/Room/PromoteTest.php b/tests/php/Command/Room/PromoteTest.php new file mode 100644 index 000000000..ee627ca44 --- /dev/null +++ b/tests/php/Command/Room/PromoteTest.php @@ -0,0 +1,231 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCA\Talk\Command\Room\Promote; +use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Manager; +use OCA\Talk\Participant; +use OCA\Talk\Room; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Exception\RuntimeException as ConsoleRuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class PromoteTest extends TestCase { + use TRoomCommandTest; + + /** @var Promote */ + private $command; + + /** @var Manager|MockObject */ + private $manager; + + /** @var RoomMockContainer */ + private $roomMockContainer; + + public function setUp(): void { + parent::setUp(); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->manager = $this->createMock(Manager::class); + $this->command = new Promote($this->manager, $this->userManager, $this->groupManager); + + $this->roomMockContainer = new RoomMockContainer($this); + + $this->createTestUserMocks(); + $this->createTestGroupMocks(); + } + + public function testMissingArguments(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "token, participant").'); + + $tester = new CommandTester($this->command); + $tester->execute([]); + } + + public function testMissingArgumentParticipant(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "participant").'); + + $tester = new CommandTester($this->command); + $tester->execute([ + 'token' => '__test-room', + ]); + } + + /** + * @dataProvider validProvider + */ + public function testValid(array $input, array $expectedRoomData, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals("Participants successfully promoted to moderators.\n", $tester->getDisplay()); + + $this->assertEquals($expectedRoomData, $this->roomMockContainer->getRoomData()); + } + + public function validProvider(): array { + return [ + [ + [ + 'token' => '__test-room', + 'participant' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::MODERATOR], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1', 'user2'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::MODERATOR], + ['userId' => 'user2', 'participantType' => Participant::MODERATOR], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ], + ]), + ], + ]; + } + + /** + * @dataProvider invalidProvider + */ + public function testInvalid(array $input, string $expectedOutput, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals($expectedOutput, $tester->getDisplay()); + } + + public function invalidProvider(): array { + return [ + [ + [ + 'token' => '__test-invalid', + 'participant' => [''], + ], + "Room not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => [''], + ], + "Room is no group call.\n", + RoomMockContainer::prepareRoomData([ + 'type' => Room::ONE_TO_ONE_CALL, + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1','invalid'] + ], + "User 'user1' is no participant.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1','invalid'] + ], + "User 'invalid' is no participant.\n", + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + ]; + } +} diff --git a/tests/php/Command/Room/RemoveTest.php b/tests/php/Command/Room/RemoveTest.php new file mode 100644 index 000000000..f9069fe3c --- /dev/null +++ b/tests/php/Command/Room/RemoveTest.php @@ -0,0 +1,214 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCA\Talk\Command\Room\Remove; +use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Manager; +use OCA\Talk\Participant; +use OCA\Talk\Room; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Exception\RuntimeException as ConsoleRuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class RemoveTest extends TestCase { + use TRoomCommandTest; + + /** @var Remove */ + private $command; + + /** @var Manager|MockObject */ + private $manager; + + /** @var RoomMockContainer */ + private $roomMockContainer; + + public function setUp(): void { + parent::setUp(); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->manager = $this->createMock(Manager::class); + $this->command = new Remove($this->manager, $this->userManager, $this->groupManager); + + $this->roomMockContainer = new RoomMockContainer($this); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->createTestUserMocks(); + $this->createTestGroupMocks(); + } + + public function testMissingArguments(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "token, participant").'); + + $tester = new CommandTester($this->command); + $tester->execute([]); + } + + public function testMissingArgumentUser(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "participant").'); + + $tester = new CommandTester($this->command); + $tester->execute([ + 'token' => '__test-room', + ]); + } + + /** + * @dataProvider validProvider + */ + public function testValid(array $input, array $expectedRoomData, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals("Users successfully removed from room.\n", $tester->getDisplay()); + + $this->assertEquals($expectedRoomData, $this->roomMockContainer->getRoomData()); + } + + public function validProvider(): array { + return [ + [ + [ + 'token' => '__test-room', + 'participant' => ['user1'], + ], + RoomMockContainer::prepareRoomData([]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1', 'user2'], + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user3', 'participantType' => Participant::USER], + ['userId' => 'user4', 'participantType' => Participant::MODERATOR], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ['userId' => 'user3', 'participantType' => Participant::USER], + ['userId' => 'user4', 'participantType' => Participant::MODERATOR], + ], + ]), + ], + ]; + } + + /** + * @dataProvider invalidProvider + */ + public function testInvalid(array $input, string $expectedOutput, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals($expectedOutput, $tester->getDisplay()); + } + + public function invalidProvider(): array { + return [ + [ + [ + 'token' => '__test-invalid', + 'participant' => [''], + ], + "Room not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => [''], + ], + "Room is no group call.\n", + RoomMockContainer::prepareRoomData([ + 'type' => Room::ONE_TO_ONE_CALL, + ]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1','invalid'] + ], + "User 'user1' is no participant.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + 'participant' => ['user1','invalid'] + ], + "User 'invalid' is no participant.\n", + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + ]; + } +} diff --git a/tests/php/Command/Room/RoomMockContainer.php b/tests/php/Command/Room/RoomMockContainer.php new file mode 100644 index 000000000..abe712a1a --- /dev/null +++ b/tests/php/Command/Room/RoomMockContainer.php @@ -0,0 +1,221 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use InvalidArgumentException; +use OCA\Talk\Exceptions\ParticipantNotFoundException; +use OCA\Talk\Participant; +use OCA\Talk\Room; +use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class RoomMockContainer { + /** @var TestCase */ + private $testCase; + + /** @var callable[] */ + private $callbacks = []; + + /** @var Room|null */ + private $room; + + /** @var array|null */ + private $roomData; + + /** @var Participant[] */ + private $participants = []; + + /** @var array[] */ + private $participantData = []; + + public function __construct(TestCase $testCase) { + $this->testCase = $testCase; + } + + public function create(array $data = []): Room { + if ($this->room !== null) { + throw new InvalidArgumentException(__METHOD__ . ' must not be called multiple times.'); + } + + /** @var Room|MockObject $room */ + $room = $this->createMock(Room::class); + + $this->room = $room; + $this->roomData = self::prepareRoomData($data); + + // simple getter + foreach (['token', 'name', 'type', 'readOnly'] as $key) { + $room->method('get' . ucfirst($key)) + ->willReturnCallback(function () use ($key) { + return $this->roomData[$key]; + }); + } + + // simple setter + foreach (['name', 'type', 'readOnly', 'password'] as $key) { + $room->method('set' . ucfirst($key)) + ->willReturnCallback(function ($value) use ($key): bool { + $this->roomData[$key] = $value; + return true; + }); + } + + // password + $room->method('hasPassword') + ->willReturnCallback(function (): bool { + return $this->roomData['password'] !== ''; + }); + + $room->method('verifyPassword') + ->willReturnCallback(function (string $password): array { + return [ + 'result' => in_array($this->roomData['password'], ['', $password], true), + 'url' => '', + ]; + }); + + // participants + $room->method('getParticipants') + ->willReturnCallback(function (): array { + return $this->participants; + }); + + $room->method('getParticipant') + ->willReturnCallback(function (?string $userId): Participant { + if (in_array($userId, [null, ''], true)) { + throw new ParticipantNotFoundException('Not a user'); + } + if (!isset($this->participants[$userId])) { + throw new ParticipantNotFoundException('User is not a participant'); + } + + return $this->participants[$userId]; + }); + + $room->method('addUsers') + ->willReturnCallback(function (array ...$participants): void { + foreach ($participants as $participant) { + $userId = $participant['userId']; + $participantType = $participant['participantType'] ?? Participant::USER; + + $this->createParticipant($userId, ['participantType' => $participantType]); + } + }); + + $room->method('removeUser') + ->willReturnCallback(function (IUser $user): void { + $this->removeParticipant($user->getUID()); + }); + + $room->method('setParticipantType') + ->willReturnCallback(function (Participant $participant, int $participantType): void { + $userId = $participant->getUser(); + $this->updateParticipant($userId, ['participantType' => $participantType]); + }); + + // add participants + foreach ($this->roomData['participants'] as $participant) { + $userId = $participant['userId']; + $participantType = $participant['participantType'] ?? Participant::USER; + + $this->createParticipant($userId, ['participantType' => $participantType]); + } + + unset($this->roomData['participants']); + + // execute callbacks + foreach ($this->callbacks as $callback) { + $callback($room); + } + + return $room; + } + + protected function createParticipant(string $userId, array $data): Participant { + /** @var Participant|MockObject $participant */ + $participant = $this->createMock(Participant::class); + + $this->participants[$userId] = $participant; + $this->participantData[$userId] = ['userId' => $userId] + $data; + + $participant->method('getUser') + ->willReturnCallback(function () use ($userId): string { + return $this->participantData[$userId]['userId']; + }); + + $participant->method('getParticipantType') + ->willReturnCallback(function () use ($userId): int { + return $this->participantData[$userId]['participantType']; + }); + + return $participant; + } + + protected function updateParticipant(string $userId, array $data): void { + $this->participantData[$userId] = array_merge($this->participantData[$userId], $data); + } + + protected function removeParticipant(string $userId): void { + unset($this->participants[$userId], $this->participantData[$userId]); + } + + public function registerCallback(callable $callback): void { + $this->callbacks[] = $callback; + } + + protected function createMock(string $originalClassName): MockObject { + return $this->testCase->getMockBuilder($originalClassName) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->disableAutoReturnValueGeneration() + ->getMock(); + } + + public function getRoom(): ?Room { + return $this->room; + } + + public function getRoomData(): ?array { + $participants = array_values($this->participantData); + return $this->roomData + ['participants' => $participants]; + } + + public static function prepareRoomData(array $data): array { + $data += [ + 'token' => '__test-room', + 'name' => 'PHPUnit Test Room', + 'type' => Room::GROUP_CALL, + 'readOnly' => Room::READ_WRITE, + 'password' => '', + 'participants' => [], + ]; + + return $data; + } +} diff --git a/tests/php/Command/Room/TRoomCommandTest.php b/tests/php/Command/Room/TRoomCommandTest.php new file mode 100644 index 000000000..1757d8895 --- /dev/null +++ b/tests/php/Command/Room/TRoomCommandTest.php @@ -0,0 +1,125 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; + +trait TRoomCommandTest { + /** @var IUserManager|MockObject */ + protected $userManager; + + /** @var IGroupManager|MockObject */ + protected $groupManager; + + /** @var IUser[] */ + private $userMocks; + + /** @var IGroup[] */ + private $groupMocks; + + protected function registerUserManagerMock(): void { + $this->userManager = $this->createMock(IUserManager::class); + + $this->userManager->method('get') + ->willReturnCallback([$this, 'getUserMock']); + + $this->overwriteService(IUserManager::class, $this->userManager); + } + + protected function createTestUserMocks(): void { + $this->createUserMock('user1'); + $this->createUserMock('user2'); + $this->createUserMock('user3'); + $this->createUserMock('user4'); + $this->createUserMock('other'); + } + + public function getUserMock(string $uid): ?IUser { + return $this->userMocks[$uid] ?? null; + } + + protected function createUserMock(string $uid): IUser { + /** @var IUser|MockObject $user */ + $user = $this->createMock(IUser::class); + + $this->userMocks[$uid] = $user; + + $user->method('getUID') + ->willReturn($uid); + + return $user; + } + + protected function registerGroupManagerMock(): void { + $this->groupManager = $this->createMock(IGroupManager::class); + + $this->groupManager->method('get') + ->willReturnCallback([$this, 'getGroupMock']); + + $this->overwriteService(IGroupManager::class, $this->groupManager); + } + + protected function createTestGroupMocks(): void { + $this->createGroupMock('group1', ['user1', 'user2']); + $this->createGroupMock('group2', ['user2', 'user3']); + $this->createGroupMock('other', ['other']); + } + + public function getGroupMock(string $gid): ?IGroup { + return $this->groupMocks[$gid] ?? null; + } + + protected function createGroupMock(string $gid, array $userIds): IGroup { + /** @var IGroup|MockObject $group */ + $group = $this->createMock(IGroup::class); + + $this->groupMocks[$gid] = $group; + + $group->method('getGID') + ->willReturn($gid); + + $group->method('getUsers') + ->willReturnCallback(function () use ($userIds) { + return array_map([$this, 'getUserMock'], $userIds); + }); + + return $group; + } + + protected function createMock($originalClassName): MockObject { + return $this->getMockBuilder($originalClassName) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->disableAutoReturnValueGeneration() + ->getMock(); + } +} diff --git a/tests/php/Command/Room/UpdateTest.php b/tests/php/Command/Room/UpdateTest.php new file mode 100644 index 000000000..7ab6a0f0b --- /dev/null +++ b/tests/php/Command/Room/UpdateTest.php @@ -0,0 +1,352 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> + * + * @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/>. + * + */ + +namespace OCA\Talk\Tests\php\Command\Room; + +use OCA\Talk\Command\Room\Update; +use OCA\Talk\Exceptions\RoomNotFoundException; +use OCA\Talk\Manager; +use OCA\Talk\Participant; +use OCA\Talk\Room; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Exception\RuntimeException as ConsoleRuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class UpdateTest extends TestCase { + use TRoomCommandTest; + + /** @var Update */ + private $command; + + /** @var Manager|MockObject */ + private $manager; + + /** @var RoomMockContainer */ + private $roomMockContainer; + + public function setUp(): void { + parent::setUp(); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->manager = $this->createMock(Manager::class); + $this->command = new Update($this->manager, $this->userManager, $this->groupManager); + + $this->roomMockContainer = new RoomMockContainer($this); + + $this->registerUserManagerMock(); + $this->registerGroupManagerMock(); + + $this->createTestUserMocks(); + $this->createTestGroupMocks(); + } + + public function testMissingArguments(): void { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + + $this->expectException(ConsoleRuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "token").'); + + $tester = new CommandTester($this->command); + $tester->execute([]); + } + + /** + * @dataProvider validProvider + */ + public function testValid(array $input, array $expectedRoomData, array $initialRoomData): void { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals("Room successfully updated.\n", $tester->getDisplay()); + + $this->assertEquals($expectedRoomData, $this->roomMockContainer->getRoomData()); + } + + public function validProvider(): array { + return [ + [ + [ + 'token' => '__test-room', + ], + RoomMockContainer::prepareRoomData([]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--name' => 'PHPUnit Test Room 2' + ], + RoomMockContainer::prepareRoomData([ + 'name' => 'PHPUnit Test Room 2', + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--public' => '1' + ], + RoomMockContainer::prepareRoomData([ + 'type' => Room::PUBLIC_CALL, + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--public' => '0' + ], + RoomMockContainer::prepareRoomData([]), + RoomMockContainer::prepareRoomData([ + 'type' => Room::PUBLIC_CALL, + ]), + ], + [ + [ + 'token' => '__test-room', + '--readonly' => '1' + ], + RoomMockContainer::prepareRoomData([ + 'readOnly' => Room::READ_ONLY, + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--readonly' => '0' + ], + RoomMockContainer::prepareRoomData([]), + RoomMockContainer::prepareRoomData([ + 'readOnly' => Room::READ_ONLY, + ]), + ], + [ + [ + 'token' => '__test-room', + '--readonly' => '1' + ], + RoomMockContainer::prepareRoomData([ + 'readOnly' => Room::READ_ONLY, + ]), + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--password' => 'my-secret-password' + ], + RoomMockContainer::prepareRoomData([ + 'type' => Room::PUBLIC_CALL, + 'password' => 'my-secret-password', + ]), + RoomMockContainer::prepareRoomData([ + 'type' => Room::PUBLIC_CALL, + ]), + ], + [ + [ + 'token' => '__test-room', + '--password' => '' + ], + RoomMockContainer::prepareRoomData([ + 'type' => Room::PUBLIC_CALL, + ]), + RoomMockContainer::prepareRoomData([ + 'type' => Room::PUBLIC_CALL, + 'password' => 'my-secret-password', + ]), + ], + [ + [ + 'token' => '__test-room', + '--owner' => 'user1' + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + '--owner' => 'user2' + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::OWNER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + [ + [ + 'token' => '__test-room', + '--owner' => '' + ], + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::USER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + RoomMockContainer::prepareRoomData([ + 'participants' => [ + ['userId' => 'user1', 'participantType' => Participant::OWNER], + ['userId' => 'user2', 'participantType' => Participant::USER], + ], + ]), + ], + ]; + } + + /** + * @dataProvider invalidProvider + */ + public function testInvalid(array $input, string $expectedOutput, array $initialRoomData = null): void { + if ($initialRoomData !== null) { + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->willReturnCallback(function (string $token) use ($initialRoomData): Room { + if ($token !== $initialRoomData['token']) { + throw new RoomNotFoundException(); + } + + return $this->roomMockContainer->create($initialRoomData); + }); + } else { + $this->manager->expects($this->never()) + ->method('getRoomByToken'); + } + + $tester = new CommandTester($this->command); + $tester->execute($input); + + $this->assertEquals($expectedOutput, $tester->getDisplay()); + } + + public function invalidProvider(): array { + return [ + [ + [ + 'token' => '__test-room', + '--public' => '', + ], + "Invalid value for option \"--public\" given.\n", + ], + [ + [ + 'token' => '__test-room', + '--readonly' => '', + ], + "Invalid value for option \"--readonly\" given.\n", + ], + [ + [ + 'token' => '__test-invalid', + ], + "Room not found.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + ], + "Room is no group call.\n", + RoomMockContainer::prepareRoomData([ + 'type' => Room::ONE_TO_ONE_CALL, + ]), + ], + [ + [ + 'token' => '__test-room', + '--name' => '', + ], + "Invalid room name.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--name' => ' ', + ], + "Invalid room name.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--name' => str_repeat('x', 256), + ], + "Invalid room name.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--password' => 'my-secret-password', + ], + "Unable to add password protection to private room.\n", + RoomMockContainer::prepareRoomData([]), + ], + [ + [ + 'token' => '__test-room', + '--owner' => 'invalid', + ], + "User 'invalid' is no participant.\n", + RoomMockContainer::prepareRoomData([]), + ], + ]; + } +} |